Index: vendor/lld/dist/COFF/CMakeLists.txt =================================================================== --- vendor/lld/dist/COFF/CMakeLists.txt (revision 351287) +++ vendor/lld/dist/COFF/CMakeLists.txt (revision 351288) @@ -1,47 +1,48 @@ set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(COFFOptionsTableGen) if(NOT LLD_BUILT_STANDALONE) set(tablegen_deps intrinsics_gen) endif() add_lld_library(lldCOFF Chunks.cpp + DebugTypes.cpp DLL.cpp Driver.cpp DriverUtils.cpp ICF.cpp InputFiles.cpp LTO.cpp MapFile.cpp MarkLive.cpp MinGW.cpp PDB.cpp SymbolTable.cpp Symbols.cpp Writer.cpp LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} BinaryFormat Core DebugInfoCodeView DebugInfoMSF DebugInfoPDB LibDriver LTO MC Object Option Support WindowsManifest LINK_LIBS lldCommon ${LLVM_PTHREAD_LIB} DEPENDS COFFOptionsTableGen ${tablegen_deps} ) Index: vendor/lld/dist/COFF/Chunks.cpp =================================================================== --- vendor/lld/dist/COFF/Chunks.cpp (revision 351287) +++ vendor/lld/dist/COFF/Chunks.cpp (revision 351288) @@ -1,863 +1,922 @@ //===- Chunks.cpp ---------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "Chunks.h" #include "InputFiles.h" #include "Symbols.h" #include "Writer.h" #include "SymbolTable.h" #include "lld/Common/ErrorHandler.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::COFF; using llvm::support::ulittle32_t; namespace lld { namespace coff { -SectionChunk::SectionChunk(ObjFile *F, const coff_section *H) - : Chunk(SectionKind), Repl(this), Header(H), File(F), - Relocs(File->getCOFFObj()->getRelocations(Header)) { - // Initialize SectionName. - File->getCOFFObj()->getSectionName(Header, SectionName); +SectionChunk::SectionChunk(ObjFile *f, const coff_section *h) + : Chunk(SectionKind), file(f), header(h), repl(this) { + // Initialize relocs. + setRelocs(file->getCOFFObj()->getRelocations(header)); - Alignment = Header->getAlignment(); + // Initialize sectionName. + StringRef sectionName; + if (Expected e = file->getCOFFObj()->getSectionName(header)) + sectionName = *e; + sectionNameData = sectionName.data(); + sectionNameSize = sectionName.size(); + setAlignment(header->getAlignment()); + + hasData = !(header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA); + // If linker GC is disabled, every chunk starts out alive. If linker GC is // enabled, treat non-comdat sections as roots. Generally optimized object // files will be built with -ffunction-sections or /Gy, so most things worth // stripping will be in a comdat. - Live = !Config->DoGC || !isCOMDAT(); + live = !config->doGC || !isCOMDAT(); } -// Initialize the RelocTargets vector, to allow redirecting certain relocations -// to a thunk instead of the actual symbol the relocation's symbol table index -// indicates. -void SectionChunk::readRelocTargets() { - assert(RelocTargets.empty()); - RelocTargets.reserve(Relocs.size()); - for (const coff_relocation &Rel : Relocs) - RelocTargets.push_back(File->getSymbol(Rel.SymbolTableIndex)); -} +// SectionChunk is one of the most frequently allocated classes, so it is +// important to keep it as compact as possible. As of this writing, the number +// below is the size of this class on x64 platforms. +static_assert(sizeof(SectionChunk) <= 88, "SectionChunk grew unexpectedly"); -// Reset RelocTargets to their original targets before thunks were added. -void SectionChunk::resetRelocTargets() { - for (size_t I = 0, E = Relocs.size(); I < E; ++I) - RelocTargets[I] = File->getSymbol(Relocs[I].SymbolTableIndex); -} +static void add16(uint8_t *p, int16_t v) { write16le(p, read16le(p) + v); } +static void add32(uint8_t *p, int32_t v) { write32le(p, read32le(p) + v); } +static void add64(uint8_t *p, int64_t v) { write64le(p, read64le(p) + v); } +static void or16(uint8_t *p, uint16_t v) { write16le(p, read16le(p) | v); } +static void or32(uint8_t *p, uint32_t v) { write32le(p, read32le(p) | v); } -static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); } -static void add32(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); } -static void add64(uint8_t *P, int64_t V) { write64le(P, read64le(P) + V); } -static void or16(uint8_t *P, uint16_t V) { write16le(P, read16le(P) | V); } -static void or32(uint8_t *P, uint32_t V) { write32le(P, read32le(P) | V); } - // Verify that given sections are appropriate targets for SECREL // relocations. This check is relaxed because unfortunately debug // sections have section-relative relocations against absolute symbols. -static bool checkSecRel(const SectionChunk *Sec, OutputSection *OS) { - if (OS) +static bool checkSecRel(const SectionChunk *sec, OutputSection *os) { + if (os) return true; - if (Sec->isCodeView()) + if (sec->isCodeView()) return false; error("SECREL relocation cannot be applied to absolute symbols"); return false; } -static void applySecRel(const SectionChunk *Sec, uint8_t *Off, - OutputSection *OS, uint64_t S) { - if (!checkSecRel(Sec, OS)) +static void applySecRel(const SectionChunk *sec, uint8_t *off, + OutputSection *os, uint64_t s) { + if (!checkSecRel(sec, os)) return; - uint64_t SecRel = S - OS->getRVA(); - if (SecRel > UINT32_MAX) { - error("overflow in SECREL relocation in section: " + Sec->getSectionName()); + uint64_t secRel = s - os->getRVA(); + if (secRel > UINT32_MAX) { + error("overflow in SECREL relocation in section: " + sec->getSectionName()); return; } - add32(Off, SecRel); + add32(off, secRel); } -static void applySecIdx(uint8_t *Off, OutputSection *OS) { +static void applySecIdx(uint8_t *off, OutputSection *os) { // Absolute symbol doesn't have section index, but section index relocation // against absolute symbol should be resolved to one plus the last output // section index. This is required for compatibility with MSVC. - if (OS) - add16(Off, OS->SectionIndex); + if (os) + add16(off, os->sectionIndex); else - add16(Off, DefinedAbsolute::NumOutputSections + 1); + add16(off, DefinedAbsolute::numOutputSections + 1); } -void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS, - uint64_t S, uint64_t P) const { - switch (Type) { - case IMAGE_REL_AMD64_ADDR32: add32(Off, S + Config->ImageBase); break; - case IMAGE_REL_AMD64_ADDR64: add64(Off, S + Config->ImageBase); break; - case IMAGE_REL_AMD64_ADDR32NB: add32(Off, S); break; - case IMAGE_REL_AMD64_REL32: add32(Off, S - P - 4); break; - case IMAGE_REL_AMD64_REL32_1: add32(Off, S - P - 5); break; - case IMAGE_REL_AMD64_REL32_2: add32(Off, S - P - 6); break; - case IMAGE_REL_AMD64_REL32_3: add32(Off, S - P - 7); break; - case IMAGE_REL_AMD64_REL32_4: add32(Off, S - P - 8); break; - case IMAGE_REL_AMD64_REL32_5: add32(Off, S - P - 9); break; - case IMAGE_REL_AMD64_SECTION: applySecIdx(Off, OS); break; - case IMAGE_REL_AMD64_SECREL: applySecRel(this, Off, OS, S); break; +void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, + uint64_t s, uint64_t p) const { + switch (type) { + case IMAGE_REL_AMD64_ADDR32: add32(off, s + config->imageBase); break; + case IMAGE_REL_AMD64_ADDR64: add64(off, s + config->imageBase); break; + case IMAGE_REL_AMD64_ADDR32NB: add32(off, s); break; + case IMAGE_REL_AMD64_REL32: add32(off, s - p - 4); break; + case IMAGE_REL_AMD64_REL32_1: add32(off, s - p - 5); break; + case IMAGE_REL_AMD64_REL32_2: add32(off, s - p - 6); break; + case IMAGE_REL_AMD64_REL32_3: add32(off, s - p - 7); break; + case IMAGE_REL_AMD64_REL32_4: add32(off, s - p - 8); break; + case IMAGE_REL_AMD64_REL32_5: add32(off, s - p - 9); break; + case IMAGE_REL_AMD64_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_AMD64_SECREL: applySecRel(this, off, os, s); break; default: - error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " + - toString(File)); + error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " + + toString(file)); } } -void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, OutputSection *OS, - uint64_t S, uint64_t P) const { - switch (Type) { +void SectionChunk::applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, + uint64_t s, uint64_t p) const { + switch (type) { case IMAGE_REL_I386_ABSOLUTE: break; - case IMAGE_REL_I386_DIR32: add32(Off, S + Config->ImageBase); break; - case IMAGE_REL_I386_DIR32NB: add32(Off, S); break; - case IMAGE_REL_I386_REL32: add32(Off, S - P - 4); break; - case IMAGE_REL_I386_SECTION: applySecIdx(Off, OS); break; - case IMAGE_REL_I386_SECREL: applySecRel(this, Off, OS, S); break; + case IMAGE_REL_I386_DIR32: add32(off, s + config->imageBase); break; + case IMAGE_REL_I386_DIR32NB: add32(off, s); break; + case IMAGE_REL_I386_REL32: add32(off, s - p - 4); break; + case IMAGE_REL_I386_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_I386_SECREL: applySecRel(this, off, os, s); break; default: - error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " + - toString(File)); + error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " + + toString(file)); } } -static void applyMOV(uint8_t *Off, uint16_t V) { - write16le(Off, (read16le(Off) & 0xfbf0) | ((V & 0x800) >> 1) | ((V >> 12) & 0xf)); - write16le(Off + 2, (read16le(Off + 2) & 0x8f00) | ((V & 0x700) << 4) | (V & 0xff)); +static void applyMOV(uint8_t *off, uint16_t v) { + write16le(off, (read16le(off) & 0xfbf0) | ((v & 0x800) >> 1) | ((v >> 12) & 0xf)); + write16le(off + 2, (read16le(off + 2) & 0x8f00) | ((v & 0x700) << 4) | (v & 0xff)); } -static uint16_t readMOV(uint8_t *Off, bool MOVT) { - uint16_t Op1 = read16le(Off); - if ((Op1 & 0xfbf0) != (MOVT ? 0xf2c0 : 0xf240)) - error("unexpected instruction in " + Twine(MOVT ? "MOVT" : "MOVW") + +static uint16_t readMOV(uint8_t *off, bool movt) { + uint16_t op1 = read16le(off); + if ((op1 & 0xfbf0) != (movt ? 0xf2c0 : 0xf240)) + error("unexpected instruction in " + Twine(movt ? "MOVT" : "MOVW") + " instruction in MOV32T relocation"); - uint16_t Op2 = read16le(Off + 2); - if ((Op2 & 0x8000) != 0) - error("unexpected instruction in " + Twine(MOVT ? "MOVT" : "MOVW") + + uint16_t op2 = read16le(off + 2); + if ((op2 & 0x8000) != 0) + error("unexpected instruction in " + Twine(movt ? "MOVT" : "MOVW") + " instruction in MOV32T relocation"); - return (Op2 & 0x00ff) | ((Op2 >> 4) & 0x0700) | ((Op1 << 1) & 0x0800) | - ((Op1 & 0x000f) << 12); + return (op2 & 0x00ff) | ((op2 >> 4) & 0x0700) | ((op1 << 1) & 0x0800) | + ((op1 & 0x000f) << 12); } -void applyMOV32T(uint8_t *Off, uint32_t V) { - uint16_t ImmW = readMOV(Off, false); // read MOVW operand - uint16_t ImmT = readMOV(Off + 4, true); // read MOVT operand - uint32_t Imm = ImmW | (ImmT << 16); - V += Imm; // add the immediate offset - applyMOV(Off, V); // set MOVW operand - applyMOV(Off + 4, V >> 16); // set MOVT operand +void applyMOV32T(uint8_t *off, uint32_t v) { + uint16_t immW = readMOV(off, false); // read MOVW operand + uint16_t immT = readMOV(off + 4, true); // read MOVT operand + uint32_t imm = immW | (immT << 16); + v += imm; // add the immediate offset + applyMOV(off, v); // set MOVW operand + applyMOV(off + 4, v >> 16); // set MOVT operand } -static void applyBranch20T(uint8_t *Off, int32_t V) { - if (!isInt<21>(V)) +static void applyBranch20T(uint8_t *off, int32_t v) { + if (!isInt<21>(v)) error("relocation out of range"); - uint32_t S = V < 0 ? 1 : 0; - uint32_t J1 = (V >> 19) & 1; - uint32_t J2 = (V >> 18) & 1; - or16(Off, (S << 10) | ((V >> 12) & 0x3f)); - or16(Off + 2, (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff)); + uint32_t s = v < 0 ? 1 : 0; + uint32_t j1 = (v >> 19) & 1; + uint32_t j2 = (v >> 18) & 1; + or16(off, (s << 10) | ((v >> 12) & 0x3f)); + or16(off + 2, (j1 << 13) | (j2 << 11) | ((v >> 1) & 0x7ff)); } -void applyBranch24T(uint8_t *Off, int32_t V) { - if (!isInt<25>(V)) +void applyBranch24T(uint8_t *off, int32_t v) { + if (!isInt<25>(v)) error("relocation out of range"); - uint32_t S = V < 0 ? 1 : 0; - uint32_t J1 = ((~V >> 23) & 1) ^ S; - uint32_t J2 = ((~V >> 22) & 1) ^ S; - or16(Off, (S << 10) | ((V >> 12) & 0x3ff)); + uint32_t s = v < 0 ? 1 : 0; + uint32_t j1 = ((~v >> 23) & 1) ^ s; + uint32_t j2 = ((~v >> 22) & 1) ^ s; + or16(off, (s << 10) | ((v >> 12) & 0x3ff)); // Clear out the J1 and J2 bits which may be set. - write16le(Off + 2, (read16le(Off + 2) & 0xd000) | (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff)); + write16le(off + 2, (read16le(off + 2) & 0xd000) | (j1 << 13) | (j2 << 11) | ((v >> 1) & 0x7ff)); } -void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS, - uint64_t S, uint64_t P) const { +void SectionChunk::applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, + uint64_t s, uint64_t p) const { // Pointer to thumb code must have the LSB set. - uint64_t SX = S; - if (OS && (OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE)) - SX |= 1; - switch (Type) { - case IMAGE_REL_ARM_ADDR32: add32(Off, SX + Config->ImageBase); break; - case IMAGE_REL_ARM_ADDR32NB: add32(Off, SX); break; - case IMAGE_REL_ARM_MOV32T: applyMOV32T(Off, SX + Config->ImageBase); break; - case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(Off, SX - P - 4); break; - case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(Off, SX - P - 4); break; - case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, SX - P - 4); break; - case IMAGE_REL_ARM_SECTION: applySecIdx(Off, OS); break; - case IMAGE_REL_ARM_SECREL: applySecRel(this, Off, OS, S); break; + uint64_t sx = s; + if (os && (os->header.Characteristics & IMAGE_SCN_MEM_EXECUTE)) + sx |= 1; + switch (type) { + case IMAGE_REL_ARM_ADDR32: add32(off, sx + config->imageBase); break; + case IMAGE_REL_ARM_ADDR32NB: add32(off, sx); break; + case IMAGE_REL_ARM_MOV32T: applyMOV32T(off, sx + config->imageBase); break; + case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(off, sx - p - 4); break; + case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(off, sx - p - 4); break; + case IMAGE_REL_ARM_BLX23T: applyBranch24T(off, sx - p - 4); break; + case IMAGE_REL_ARM_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_ARM_SECREL: applySecRel(this, off, os, s); break; + case IMAGE_REL_ARM_REL32: add32(off, sx - p - 4); break; default: - error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " + - toString(File)); + error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " + + toString(file)); } } // Interpret the existing immediate value as a byte offset to the // target symbol, then update the instruction with the immediate as // the page offset from the current instruction to the target. -void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift) { - uint32_t Orig = read32le(Off); - uint64_t Imm = ((Orig >> 29) & 0x3) | ((Orig >> 3) & 0x1FFFFC); - S += Imm; - Imm = (S >> Shift) - (P >> Shift); - uint32_t ImmLo = (Imm & 0x3) << 29; - uint32_t ImmHi = (Imm & 0x1FFFFC) << 3; - uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3); - write32le(Off, (Orig & ~Mask) | ImmLo | ImmHi); +void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift) { + uint32_t orig = read32le(off); + uint64_t imm = ((orig >> 29) & 0x3) | ((orig >> 3) & 0x1FFFFC); + s += imm; + imm = (s >> shift) - (p >> shift); + uint32_t immLo = (imm & 0x3) << 29; + uint32_t immHi = (imm & 0x1FFFFC) << 3; + uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3); + write32le(off, (orig & ~mask) | immLo | immHi); } // Update the immediate field in a AARCH64 ldr, str, and add instruction. // Optionally limit the range of the written immediate by one or more bits -// (RangeLimit). -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 >> RangeLimit)) << 10)); +// (rangeLimit). +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 >> 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) { - uint32_t Orig = read32le(Off); - uint32_t Size = Orig >> 30; +static void applyArm64Ldr(uint8_t *off, uint64_t 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) + if ((orig & 0x4800000) == 0x4800000) + size += 4; + if ((imm & ((1 << size) - 1)) != 0) error("misaligned ldr/str offset"); - applyArm64Imm(Off, Imm >> Size, Size); + applyArm64Imm(off, imm >> size, size); } -static void applySecRelLow12A(const SectionChunk *Sec, uint8_t *Off, - OutputSection *OS, uint64_t S) { - if (checkSecRel(Sec, OS)) - applyArm64Imm(Off, (S - OS->getRVA()) & 0xfff, 0); +static void applySecRelLow12A(const SectionChunk *sec, uint8_t *off, + OutputSection *os, uint64_t s) { + if (checkSecRel(sec, os)) + applyArm64Imm(off, (s - os->getRVA()) & 0xfff, 0); } -static void applySecRelHigh12A(const SectionChunk *Sec, uint8_t *Off, - OutputSection *OS, uint64_t S) { - if (!checkSecRel(Sec, OS)) +static void applySecRelHigh12A(const SectionChunk *sec, uint8_t *off, + OutputSection *os, uint64_t s) { + if (!checkSecRel(sec, os)) return; - uint64_t SecRel = (S - OS->getRVA()) >> 12; - if (0xfff < SecRel) { + uint64_t secRel = (s - os->getRVA()) >> 12; + if (0xfff < secRel) { error("overflow in SECREL_HIGH12A relocation in section: " + - Sec->getSectionName()); + sec->getSectionName()); return; } - applyArm64Imm(Off, SecRel & 0xfff, 0); + applyArm64Imm(off, secRel & 0xfff, 0); } -static void applySecRelLdr(const SectionChunk *Sec, uint8_t *Off, - OutputSection *OS, uint64_t S) { - if (checkSecRel(Sec, OS)) - applyArm64Ldr(Off, (S - OS->getRVA()) & 0xfff); +static void applySecRelLdr(const SectionChunk *sec, uint8_t *off, + OutputSection *os, uint64_t s) { + if (checkSecRel(sec, os)) + applyArm64Ldr(off, (s - os->getRVA()) & 0xfff); } -void applyArm64Branch26(uint8_t *Off, int64_t V) { - if (!isInt<28>(V)) +void applyArm64Branch26(uint8_t *off, int64_t v) { + if (!isInt<28>(v)) error("relocation out of range"); - or32(Off, (V & 0x0FFFFFFC) >> 2); + or32(off, (v & 0x0FFFFFFC) >> 2); } -static void applyArm64Branch19(uint8_t *Off, int64_t V) { - if (!isInt<21>(V)) +static void applyArm64Branch19(uint8_t *off, int64_t v) { + if (!isInt<21>(v)) error("relocation out of range"); - or32(Off, (V & 0x001FFFFC) << 3); + or32(off, (v & 0x001FFFFC) << 3); } -static void applyArm64Branch14(uint8_t *Off, int64_t V) { - if (!isInt<16>(V)) +static void applyArm64Branch14(uint8_t *off, int64_t v) { + if (!isInt<16>(v)) error("relocation out of range"); - or32(Off, (V & 0x0000FFFC) << 3); + or32(off, (v & 0x0000FFFC) << 3); } -void SectionChunk::applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS, - uint64_t S, uint64_t P) const { - switch (Type) { - case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(Off, S, P, 12); break; - case IMAGE_REL_ARM64_REL21: applyArm64Addr(Off, S, P, 0); break; - case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(Off, S & 0xfff, 0); break; - case IMAGE_REL_ARM64_PAGEOFFSET_12L: applyArm64Ldr(Off, S & 0xfff); break; - case IMAGE_REL_ARM64_BRANCH26: applyArm64Branch26(Off, S - P); break; - case IMAGE_REL_ARM64_BRANCH19: applyArm64Branch19(Off, S - P); break; - case IMAGE_REL_ARM64_BRANCH14: applyArm64Branch14(Off, S - P); break; - case IMAGE_REL_ARM64_ADDR32: add32(Off, S + Config->ImageBase); break; - case IMAGE_REL_ARM64_ADDR32NB: add32(Off, S); break; - case IMAGE_REL_ARM64_ADDR64: add64(Off, S + Config->ImageBase); break; - case IMAGE_REL_ARM64_SECREL: applySecRel(this, Off, OS, S); break; - case IMAGE_REL_ARM64_SECREL_LOW12A: applySecRelLow12A(this, Off, OS, S); break; - case IMAGE_REL_ARM64_SECREL_HIGH12A: applySecRelHigh12A(this, Off, OS, S); break; - case IMAGE_REL_ARM64_SECREL_LOW12L: applySecRelLdr(this, Off, OS, S); break; - case IMAGE_REL_ARM64_SECTION: applySecIdx(Off, OS); break; +void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, + uint64_t s, uint64_t p) const { + switch (type) { + case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(off, s, p, 12); break; + case IMAGE_REL_ARM64_REL21: applyArm64Addr(off, s, p, 0); break; + case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(off, s & 0xfff, 0); break; + case IMAGE_REL_ARM64_PAGEOFFSET_12L: applyArm64Ldr(off, s & 0xfff); break; + case IMAGE_REL_ARM64_BRANCH26: applyArm64Branch26(off, s - p); break; + case IMAGE_REL_ARM64_BRANCH19: applyArm64Branch19(off, s - p); break; + case IMAGE_REL_ARM64_BRANCH14: applyArm64Branch14(off, s - p); break; + case IMAGE_REL_ARM64_ADDR32: add32(off, s + config->imageBase); break; + case IMAGE_REL_ARM64_ADDR32NB: add32(off, s); break; + case IMAGE_REL_ARM64_ADDR64: add64(off, s + config->imageBase); break; + case IMAGE_REL_ARM64_SECREL: applySecRel(this, off, os, s); break; + case IMAGE_REL_ARM64_SECREL_LOW12A: applySecRelLow12A(this, off, os, s); break; + case IMAGE_REL_ARM64_SECREL_HIGH12A: applySecRelHigh12A(this, off, os, s); break; + case IMAGE_REL_ARM64_SECREL_LOW12L: applySecRelLdr(this, off, os, s); break; + case IMAGE_REL_ARM64_SECTION: applySecIdx(off, os); break; + case IMAGE_REL_ARM64_REL32: add32(off, s - p - 4); break; default: - error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " + - toString(File)); + error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " + + toString(file)); } } -static void maybeReportRelocationToDiscarded(const SectionChunk *FromChunk, - Defined *Sym, - const coff_relocation &Rel) { +static void maybeReportRelocationToDiscarded(const SectionChunk *fromChunk, + Defined *sym, + const coff_relocation &rel) { // Don't report these errors when the relocation comes from a debug info // section or in mingw mode. MinGW mode object files (built by GCC) can // have leftover sections with relocations against discarded comdat // sections. Such sections are left as is, with relocations untouched. - if (FromChunk->isCodeView() || FromChunk->isDWARF() || Config->MinGW) + if (fromChunk->isCodeView() || fromChunk->isDWARF() || config->mingw) return; // Get the name of the symbol. If it's null, it was discarded early, so we // have to go back to the object file. - ObjFile *File = FromChunk->File; - StringRef Name; - if (Sym) { - Name = Sym->getName(); + ObjFile *file = fromChunk->file; + StringRef name; + if (sym) { + name = sym->getName(); } else { - COFFSymbolRef COFFSym = - check(File->getCOFFObj()->getSymbol(Rel.SymbolTableIndex)); - File->getCOFFObj()->getSymbolName(COFFSym, Name); + COFFSymbolRef coffSym = + check(file->getCOFFObj()->getSymbol(rel.SymbolTableIndex)); + file->getCOFFObj()->getSymbolName(coffSym, name); } - error("relocation against symbol in discarded section: " + Name + - getSymbolLocations(File, Rel.SymbolTableIndex)); + std::vector symbolLocations = + getSymbolLocations(file, rel.SymbolTableIndex); + + std::string out; + llvm::raw_string_ostream os(out); + os << "relocation against symbol in discarded section: " + name; + for (const std::string &s : symbolLocations) + os << s; + error(os.str()); } -void SectionChunk::writeTo(uint8_t *Buf) const { - if (!hasData()) +void SectionChunk::writeTo(uint8_t *buf) const { + if (!hasData) return; // Copy section contents from source object file to output file. - ArrayRef A = getContents(); - if (!A.empty()) - memcpy(Buf + OutputSectionOff, A.data(), A.size()); + ArrayRef a = getContents(); + if (!a.empty()) + memcpy(buf, a.data(), a.size()); // Apply relocations. - size_t InputSize = getSize(); - for (size_t I = 0, E = Relocs.size(); I < E; I++) { - const coff_relocation &Rel = Relocs[I]; + size_t inputSize = getSize(); + for (size_t i = 0, e = relocsSize; i < e; i++) { + const coff_relocation &rel = relocsData[i]; // Check for an invalid relocation offset. This check isn't perfect, because // we don't have the relocation size, which is only known after checking the // machine and relocation type. As a result, a relocation may overwrite the // beginning of the following input section. - if (Rel.VirtualAddress >= InputSize) { + if (rel.VirtualAddress >= inputSize) { error("relocation points beyond the end of its parent section"); continue; } - uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress; + uint8_t *off = buf + rel.VirtualAddress; - // Use the potentially remapped Symbol instead of the one that the - // relocation points to. - auto *Sym = dyn_cast_or_null(RelocTargets[I]); + auto *sym = + dyn_cast_or_null(file->getSymbol(rel.SymbolTableIndex)); // 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. - Chunk *C = Sym ? Sym->getChunk() : nullptr; - OutputSection *OS = C ? C->getOutputSection() : nullptr; + Chunk *c = sym ? sym->getChunk() : nullptr; + OutputSection *os = c ? c->getOutputSection() : nullptr; // Skip the relocation if it refers to a discarded section, and diagnose it // as an error if appropriate. If a symbol was discarded early, it may be // null. If it was discarded late, the output section will be null, unless // it was an absolute or synthetic symbol. - if (!Sym || - (!OS && !isa(Sym) && !isa(Sym))) { - maybeReportRelocationToDiscarded(this, Sym, Rel); + if (!sym || + (!os && !isa(sym) && !isa(sym))) { + maybeReportRelocationToDiscarded(this, sym, rel); continue; } - uint64_t S = Sym->getRVA(); + uint64_t s = sym->getRVA(); // Compute the RVA of the relocation for relative relocations. - uint64_t P = RVA + Rel.VirtualAddress; - switch (Config->Machine) { + uint64_t p = rva + rel.VirtualAddress; + switch (config->machine) { case AMD64: - applyRelX64(Off, Rel.Type, OS, S, P); + applyRelX64(off, rel.Type, os, s, p); break; case I386: - applyRelX86(Off, Rel.Type, OS, S, P); + applyRelX86(off, rel.Type, os, s, p); break; case ARMNT: - applyRelARM(Off, Rel.Type, OS, S, P); + applyRelARM(off, rel.Type, os, s, p); break; case ARM64: - applyRelARM64(Off, Rel.Type, OS, S, P); + applyRelARM64(off, rel.Type, os, s, p); break; default: llvm_unreachable("unknown machine type"); } } } -void SectionChunk::addAssociative(SectionChunk *Child) { - AssocChildren.push_back(Child); +void SectionChunk::addAssociative(SectionChunk *child) { + // Insert this child at the head of the list. + assert(child->assocChildren == nullptr && + "associated sections cannot have their own associated children"); + child->assocChildren = assocChildren; + assocChildren = child; } -static uint8_t getBaserelType(const coff_relocation &Rel) { - switch (Config->Machine) { +static uint8_t getBaserelType(const coff_relocation &rel) { + switch (config->machine) { case AMD64: - if (Rel.Type == IMAGE_REL_AMD64_ADDR64) + if (rel.Type == IMAGE_REL_AMD64_ADDR64) return IMAGE_REL_BASED_DIR64; return IMAGE_REL_BASED_ABSOLUTE; case I386: - if (Rel.Type == IMAGE_REL_I386_DIR32) + if (rel.Type == IMAGE_REL_I386_DIR32) return IMAGE_REL_BASED_HIGHLOW; return IMAGE_REL_BASED_ABSOLUTE; case ARMNT: - if (Rel.Type == IMAGE_REL_ARM_ADDR32) + if (rel.Type == IMAGE_REL_ARM_ADDR32) return IMAGE_REL_BASED_HIGHLOW; - if (Rel.Type == IMAGE_REL_ARM_MOV32T) + if (rel.Type == IMAGE_REL_ARM_MOV32T) return IMAGE_REL_BASED_ARM_MOV32T; return IMAGE_REL_BASED_ABSOLUTE; case ARM64: - if (Rel.Type == IMAGE_REL_ARM64_ADDR64) + if (rel.Type == IMAGE_REL_ARM64_ADDR64) return IMAGE_REL_BASED_DIR64; return IMAGE_REL_BASED_ABSOLUTE; default: llvm_unreachable("unknown machine type"); } } // Windows-specific. // Collect all locations that contain absolute addresses, which need to be // fixed by the loader if load-time relocation is needed. // Only called when base relocation is enabled. -void SectionChunk::getBaserels(std::vector *Res) { - for (size_t I = 0, E = Relocs.size(); I < E; I++) { - const coff_relocation &Rel = Relocs[I]; - uint8_t Ty = getBaserelType(Rel); - if (Ty == IMAGE_REL_BASED_ABSOLUTE) +void SectionChunk::getBaserels(std::vector *res) { + for (size_t i = 0, e = relocsSize; i < e; i++) { + const coff_relocation &rel = relocsData[i]; + uint8_t ty = getBaserelType(rel); + if (ty == IMAGE_REL_BASED_ABSOLUTE) continue; - // Use the potentially remapped Symbol instead of the one that the - // relocation points to. - Symbol *Target = RelocTargets[I]; - if (!Target || isa(Target)) + Symbol *target = file->getSymbol(rel.SymbolTableIndex); + if (!target || isa(target)) continue; - Res->emplace_back(RVA + Rel.VirtualAddress, Ty); + res->emplace_back(rva + rel.VirtualAddress, ty); } } // MinGW specific. // Check whether a static relocation of type Type can be deferred and // handled at runtime as a pseudo relocation (for references to a module // local variable, which turned out to actually need to be imported from // another DLL) This returns the size the relocation is supposed to update, // in bits, or 0 if the relocation cannot be handled as a runtime pseudo // relocation. -static int getRuntimePseudoRelocSize(uint16_t Type) { +static int getRuntimePseudoRelocSize(uint16_t type) { // Relocations that either contain an absolute address, or a plain // relative offset, since the runtime pseudo reloc implementation // adds 8/16/32/64 bit values to a memory address. // // Given a pseudo relocation entry, // // typedef struct { // DWORD sym; // DWORD target; // DWORD flags; // } runtime_pseudo_reloc_item_v2; // // the runtime relocation performs this adjustment: // *(base + .target) += *(base + .sym) - (base + .sym) // // This works for both absolute addresses (IMAGE_REL_*_ADDR32/64, // IMAGE_REL_I386_DIR32, where the memory location initially contains // the address of the IAT slot, and for relative addresses (IMAGE_REL*_REL32), // where the memory location originally contains the relative offset to the // IAT slot. // // This requires the target address to be writable, either directly out of // the image, or temporarily changed at runtime with VirtualProtect. // Since this only operates on direct address values, it doesn't work for // ARM/ARM64 relocations, other than the plain ADDR32/ADDR64 relocations. - switch (Config->Machine) { + switch (config->machine) { case AMD64: - switch (Type) { + switch (type) { case IMAGE_REL_AMD64_ADDR64: return 64; case IMAGE_REL_AMD64_ADDR32: case IMAGE_REL_AMD64_REL32: case IMAGE_REL_AMD64_REL32_1: case IMAGE_REL_AMD64_REL32_2: case IMAGE_REL_AMD64_REL32_3: case IMAGE_REL_AMD64_REL32_4: case IMAGE_REL_AMD64_REL32_5: return 32; default: return 0; } case I386: - switch (Type) { + switch (type) { case IMAGE_REL_I386_DIR32: case IMAGE_REL_I386_REL32: return 32; default: return 0; } case ARMNT: - switch (Type) { + switch (type) { case IMAGE_REL_ARM_ADDR32: return 32; default: return 0; } case ARM64: - switch (Type) { + switch (type) { case IMAGE_REL_ARM64_ADDR64: return 64; case IMAGE_REL_ARM64_ADDR32: return 32; default: return 0; } default: llvm_unreachable("unknown machine type"); } } // MinGW specific. // Append information to the provided vector about all relocations that // need to be handled at runtime as runtime pseudo relocations (references // to a module local variable, which turned out to actually need to be // imported from another DLL). void SectionChunk::getRuntimePseudoRelocs( - std::vector &Res) { - for (const coff_relocation &Rel : Relocs) { - auto *Target = - dyn_cast_or_null(File->getSymbol(Rel.SymbolTableIndex)); - if (!Target || !Target->IsRuntimePseudoReloc) + std::vector &res) { + for (const coff_relocation &rel : getRelocs()) { + auto *target = + dyn_cast_or_null(file->getSymbol(rel.SymbolTableIndex)); + if (!target || !target->isRuntimePseudoReloc) continue; - int SizeInBits = getRuntimePseudoRelocSize(Rel.Type); - if (SizeInBits == 0) { - error("unable to automatically import from " + Target->getName() + + int sizeInBits = getRuntimePseudoRelocSize(rel.Type); + if (sizeInBits == 0) { + error("unable to automatically import from " + target->getName() + " with relocation type " + - File->getCOFFObj()->getRelocationTypeName(Rel.Type) + " in " + - toString(File)); + file->getCOFFObj()->getRelocationTypeName(rel.Type) + " in " + + toString(file)); continue; } - // SizeInBits is used to initialize the Flags field; currently no + // sizeInBits is used to initialize the Flags field; currently no // other flags are defined. - Res.emplace_back( - RuntimePseudoReloc(Target, this, Rel.VirtualAddress, SizeInBits)); + res.emplace_back( + RuntimePseudoReloc(target, this, rel.VirtualAddress, sizeInBits)); } } -bool SectionChunk::hasData() const { - return !(Header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA); -} - -uint32_t SectionChunk::getOutputCharacteristics() const { - return Header->Characteristics & (PermMask | TypeMask); -} - bool SectionChunk::isCOMDAT() const { - return Header->Characteristics & IMAGE_SCN_LNK_COMDAT; + return header->Characteristics & IMAGE_SCN_LNK_COMDAT; } void SectionChunk::printDiscardedMessage() const { // Removed by dead-stripping. If it's removed by ICF, ICF already // printed out the name, so don't repeat that here. - if (Sym && this == Repl) - message("Discarded " + Sym->getName()); + if (sym && this == repl) + message("Discarded " + sym->getName()); } -StringRef SectionChunk::getDebugName() { - if (Sym) - return Sym->getName(); +StringRef SectionChunk::getDebugName() const { + if (sym) + return sym->getName(); return ""; } ArrayRef SectionChunk::getContents() const { - ArrayRef A; - File->getCOFFObj()->getSectionContents(Header, A); - return A; + ArrayRef a; + cantFail(file->getCOFFObj()->getSectionContents(header, a)); + return a; } -void SectionChunk::replace(SectionChunk *Other) { - Alignment = std::max(Alignment, Other->Alignment); - Other->Repl = Repl; - Other->Live = false; +ArrayRef SectionChunk::consumeDebugMagic() { + assert(isCodeView()); + return consumeDebugMagic(getContents(), getSectionName()); } +ArrayRef SectionChunk::consumeDebugMagic(ArrayRef data, + StringRef sectionName) { + if (data.empty()) + return {}; + + // First 4 bytes are section magic. + if (data.size() < 4) + fatal("the section is too short: " + sectionName); + + if (!sectionName.startswith(".debug$")) + fatal("invalid section: " + sectionName); + + uint32_t magic = support::endian::read32le(data.data()); + uint32_t expectedMagic = sectionName == ".debug$H" + ? DEBUG_HASHES_SECTION_MAGIC + : DEBUG_SECTION_MAGIC; + if (magic != expectedMagic) { + warn("ignoring section " + sectionName + " with unrecognized magic 0x" + + utohexstr(magic)); + return {}; + } + return data.slice(4); +} + +SectionChunk *SectionChunk::findByName(ArrayRef sections, + StringRef name) { + for (SectionChunk *c : sections) + if (c->getSectionName() == name) + return c; + return nullptr; +} + +void SectionChunk::replace(SectionChunk *other) { + p2Align = std::max(p2Align, other->p2Align); + other->repl = repl; + other->live = false; +} + uint32_t SectionChunk::getSectionNumber() const { - DataRefImpl R; - R.p = reinterpret_cast(Header); - SectionRef S(R, File->getCOFFObj()); - return S.getIndex() + 1; + DataRefImpl r; + r.p = reinterpret_cast(header); + SectionRef s(r, file->getCOFFObj()); + return s.getIndex() + 1; } -CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) { - // Common symbols are aligned on natural boundaries up to 32 bytes. +CommonChunk::CommonChunk(const COFFSymbolRef s) : sym(s) { + // The value of a common symbol is its size. Align all common symbols smaller + // than 32 bytes naturally, i.e. round the size up to the next power of two. // This is what MSVC link.exe does. - Alignment = std::min(uint64_t(32), PowerOf2Ceil(Sym.getValue())); + setAlignment(std::min(32U, uint32_t(PowerOf2Ceil(sym.getValue())))); + hasData = false; } uint32_t CommonChunk::getOutputCharacteristics() const { return IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE; } -void StringChunk::writeTo(uint8_t *Buf) const { - memcpy(Buf + OutputSectionOff, Str.data(), Str.size()); - Buf[OutputSectionOff + Str.size()] = '\0'; +void StringChunk::writeTo(uint8_t *buf) const { + memcpy(buf, str.data(), str.size()); + buf[str.size()] = '\0'; } -ImportThunkChunkX64::ImportThunkChunkX64(Defined *S) : ImpSymbol(S) { +ImportThunkChunkX64::ImportThunkChunkX64(Defined *s) : ImportThunkChunk(s) { // Intel Optimization Manual says that all branch targets // should be 16-byte aligned. MSVC linker does this too. - Alignment = 16; + setAlignment(16); } -void ImportThunkChunkX64::writeTo(uint8_t *Buf) const { - memcpy(Buf + OutputSectionOff, ImportThunkX86, sizeof(ImportThunkX86)); +void ImportThunkChunkX64::writeTo(uint8_t *buf) const { + memcpy(buf, importThunkX86, sizeof(importThunkX86)); // The first two bytes is a JMP instruction. Fill its operand. - write32le(Buf + OutputSectionOff + 2, ImpSymbol->getRVA() - RVA - getSize()); + write32le(buf + 2, impSymbol->getRVA() - rva - getSize()); } -void ImportThunkChunkX86::getBaserels(std::vector *Res) { - Res->emplace_back(getRVA() + 2); +void ImportThunkChunkX86::getBaserels(std::vector *res) { + res->emplace_back(getRVA() + 2); } -void ImportThunkChunkX86::writeTo(uint8_t *Buf) const { - memcpy(Buf + OutputSectionOff, ImportThunkX86, sizeof(ImportThunkX86)); +void ImportThunkChunkX86::writeTo(uint8_t *buf) const { + memcpy(buf, importThunkX86, sizeof(importThunkX86)); // The first two bytes is a JMP instruction. Fill its operand. - write32le(Buf + OutputSectionOff + 2, - ImpSymbol->getRVA() + Config->ImageBase); + write32le(buf + 2, + impSymbol->getRVA() + config->imageBase); } -void ImportThunkChunkARM::getBaserels(std::vector *Res) { - Res->emplace_back(getRVA(), IMAGE_REL_BASED_ARM_MOV32T); +void ImportThunkChunkARM::getBaserels(std::vector *res) { + res->emplace_back(getRVA(), IMAGE_REL_BASED_ARM_MOV32T); } -void ImportThunkChunkARM::writeTo(uint8_t *Buf) const { - memcpy(Buf + OutputSectionOff, ImportThunkARM, sizeof(ImportThunkARM)); +void ImportThunkChunkARM::writeTo(uint8_t *buf) const { + memcpy(buf, importThunkARM, sizeof(importThunkARM)); // Fix mov.w and mov.t operands. - applyMOV32T(Buf + OutputSectionOff, ImpSymbol->getRVA() + Config->ImageBase); + applyMOV32T(buf, impSymbol->getRVA() + config->imageBase); } -void ImportThunkChunkARM64::writeTo(uint8_t *Buf) const { - int64_t Off = ImpSymbol->getRVA() & 0xfff; - memcpy(Buf + OutputSectionOff, ImportThunkARM64, sizeof(ImportThunkARM64)); - applyArm64Addr(Buf + OutputSectionOff, ImpSymbol->getRVA(), RVA, 12); - applyArm64Ldr(Buf + OutputSectionOff + 4, Off); +void ImportThunkChunkARM64::writeTo(uint8_t *buf) const { + int64_t off = impSymbol->getRVA() & 0xfff; + memcpy(buf, importThunkARM64, sizeof(importThunkARM64)); + applyArm64Addr(buf, impSymbol->getRVA(), rva, 12); + applyArm64Ldr(buf + 4, off); } // A Thumb2, PIC, non-interworking range extension thunk. -const uint8_t ArmThunk[] = { +const uint8_t armThunk[] = { 0x40, 0xf2, 0x00, 0x0c, // P: movw ip,:lower16:S - (P + (L1-P) + 4) 0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4) 0xe7, 0x44, // L1: add pc, ip }; -size_t RangeExtensionThunk::getSize() const { - assert(Config->Machine == ARMNT); - return sizeof(ArmThunk); +size_t RangeExtensionThunkARM::getSize() const { + assert(config->machine == ARMNT); + return sizeof(armThunk); } -void RangeExtensionThunk::writeTo(uint8_t *Buf) const { - assert(Config->Machine == ARMNT); - uint64_t Offset = Target->getRVA() - RVA - 12; - memcpy(Buf + OutputSectionOff, ArmThunk, sizeof(ArmThunk)); - applyMOV32T(Buf + OutputSectionOff, uint32_t(Offset)); +void RangeExtensionThunkARM::writeTo(uint8_t *buf) const { + assert(config->machine == ARMNT); + uint64_t offset = target->getRVA() - rva - 12; + memcpy(buf, armThunk, sizeof(armThunk)); + applyMOV32T(buf, uint32_t(offset)); } -void LocalImportChunk::getBaserels(std::vector *Res) { - Res->emplace_back(getRVA()); +// A position independent ARM64 adrp+add thunk, with a maximum range of +// +/- 4 GB, which is enough for any PE-COFF. +const uint8_t arm64Thunk[] = { + 0x10, 0x00, 0x00, 0x90, // adrp x16, Dest + 0x10, 0x02, 0x00, 0x91, // add x16, x16, :lo12:Dest + 0x00, 0x02, 0x1f, 0xd6, // br x16 +}; + +size_t RangeExtensionThunkARM64::getSize() const { + assert(config->machine == ARM64); + return sizeof(arm64Thunk); } -size_t LocalImportChunk::getSize() const { return Config->Wordsize; } +void RangeExtensionThunkARM64::writeTo(uint8_t *buf) const { + assert(config->machine == ARM64); + memcpy(buf, arm64Thunk, sizeof(arm64Thunk)); + applyArm64Addr(buf + 0, target->getRVA(), rva, 12); + applyArm64Imm(buf + 4, target->getRVA() & 0xfff, 0); +} -void LocalImportChunk::writeTo(uint8_t *Buf) const { - if (Config->is64()) { - write64le(Buf + OutputSectionOff, Sym->getRVA() + Config->ImageBase); +void LocalImportChunk::getBaserels(std::vector *res) { + res->emplace_back(getRVA()); +} + +size_t LocalImportChunk::getSize() const { return config->wordsize; } + +void LocalImportChunk::writeTo(uint8_t *buf) const { + if (config->is64()) { + write64le(buf, sym->getRVA() + config->imageBase); } else { - write32le(Buf + OutputSectionOff, Sym->getRVA() + Config->ImageBase); + write32le(buf, sym->getRVA() + config->imageBase); } } -void RVATableChunk::writeTo(uint8_t *Buf) const { - ulittle32_t *Begin = reinterpret_cast(Buf + OutputSectionOff); - size_t Cnt = 0; - for (const ChunkAndOffset &CO : Syms) - Begin[Cnt++] = CO.InputChunk->getRVA() + CO.Offset; - std::sort(Begin, Begin + Cnt); - assert(std::unique(Begin, Begin + Cnt) == Begin + Cnt && +void RVATableChunk::writeTo(uint8_t *buf) const { + ulittle32_t *begin = reinterpret_cast(buf); + size_t cnt = 0; + for (const ChunkAndOffset &co : syms) + begin[cnt++] = co.inputChunk->getRVA() + co.offset; + std::sort(begin, begin + cnt); + assert(std::unique(begin, begin + cnt) == begin + cnt && "RVA tables should be de-duplicated"); } // MinGW specific, for the "automatic import of variables from DLLs" feature. size_t PseudoRelocTableChunk::getSize() const { - if (Relocs.empty()) + if (relocs.empty()) return 0; - return 12 + 12 * Relocs.size(); + return 12 + 12 * relocs.size(); } // MinGW specific. -void PseudoRelocTableChunk::writeTo(uint8_t *Buf) const { - if (Relocs.empty()) +void PseudoRelocTableChunk::writeTo(uint8_t *buf) const { + if (relocs.empty()) return; - ulittle32_t *Table = reinterpret_cast(Buf + OutputSectionOff); + ulittle32_t *table = reinterpret_cast(buf); // This is the list header, to signal the runtime pseudo relocation v2 // format. - Table[0] = 0; - Table[1] = 0; - Table[2] = 1; + table[0] = 0; + table[1] = 0; + table[2] = 1; - size_t Idx = 3; - for (const RuntimePseudoReloc &RPR : Relocs) { - Table[Idx + 0] = RPR.Sym->getRVA(); - Table[Idx + 1] = RPR.Target->getRVA() + RPR.TargetOffset; - Table[Idx + 2] = RPR.Flags; - Idx += 3; + size_t idx = 3; + for (const RuntimePseudoReloc &rpr : relocs) { + table[idx + 0] = rpr.sym->getRVA(); + table[idx + 1] = rpr.target->getRVA() + rpr.targetOffset; + table[idx + 2] = rpr.flags; + idx += 3; } } // Windows-specific. This class represents a block in .reloc section. // The format is described here. // // On Windows, each DLL is linked against a fixed base address and // usually loaded to that address. However, if there's already another // DLL that overlaps, the loader has to relocate it. To do that, DLLs // contain .reloc sections which contain offsets that need to be fixed // up at runtime. If the loader finds that a DLL cannot be loaded to its // desired base address, it loads it to somewhere else, and add - to each offset that is // specified by the .reloc section. In ELF terms, .reloc sections // contain relative relocations in REL format (as opposed to RELA.) // // This already significantly reduces the size of relocations compared // to ELF .rel.dyn, but Windows does more to reduce it (probably because // it was invented for PCs in the late '80s or early '90s.) Offsets in // .reloc are grouped by page where the page size is 12 bits, and // offsets sharing the same page address are stored consecutively to // represent them with less space. This is very similar to the page // table which is grouped by (multiple stages of) pages. // // For example, let's say we have 0x00030, 0x00500, 0x00700, 0x00A00, // 0x20004, and 0x20008 in a .reloc section for x64. The uppermost 4 // bits have a type IMAGE_REL_BASED_DIR64 or 0xA. In the section, they // are represented like this: // // 0x00000 -- page address (4 bytes) // 16 -- size of this block (4 bytes) // 0xA030 -- entries (2 bytes each) // 0xA500 // 0xA700 // 0xAA00 // 0x20000 -- page address (4 bytes) // 12 -- size of this block (4 bytes) // 0xA004 -- entries (2 bytes each) // 0xA008 // // Usually we have a lot of relocations for each page, so the number of // bytes for one .reloc entry is close to 2 bytes on average. -BaserelChunk::BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End) { +BaserelChunk::BaserelChunk(uint32_t page, Baserel *begin, Baserel *end) { // Block header consists of 4 byte page RVA and 4 byte block size. // Each entry is 2 byte. Last entry may be padding. - Data.resize(alignTo((End - Begin) * 2 + 8, 4)); - uint8_t *P = Data.data(); - write32le(P, Page); - write32le(P + 4, Data.size()); - P += 8; - for (Baserel *I = Begin; I != End; ++I) { - write16le(P, (I->Type << 12) | (I->RVA - Page)); - P += 2; + data.resize(alignTo((end - begin) * 2 + 8, 4)); + uint8_t *p = data.data(); + write32le(p, page); + write32le(p + 4, data.size()); + p += 8; + for (Baserel *i = begin; i != end; ++i) { + write16le(p, (i->type << 12) | (i->rva - page)); + p += 2; } } -void BaserelChunk::writeTo(uint8_t *Buf) const { - memcpy(Buf + OutputSectionOff, Data.data(), Data.size()); +void BaserelChunk::writeTo(uint8_t *buf) const { + memcpy(buf, data.data(), data.size()); } uint8_t Baserel::getDefaultType() { - switch (Config->Machine) { + 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"); } } -std::map MergeChunk::Instances; +MergeChunk *MergeChunk::instances[Log2MaxSectionAlignment + 1] = {}; -MergeChunk::MergeChunk(uint32_t Alignment) - : Builder(StringTableBuilder::RAW, Alignment) { - this->Alignment = Alignment; +MergeChunk::MergeChunk(uint32_t alignment) + : builder(StringTableBuilder::RAW, alignment) { + setAlignment(alignment); } -void MergeChunk::addSection(SectionChunk *C) { - auto *&MC = Instances[C->Alignment]; - if (!MC) - MC = make(C->Alignment); - MC->Sections.push_back(C); +void MergeChunk::addSection(SectionChunk *c) { + assert(isPowerOf2_32(c->getAlignment())); + uint8_t p2Align = llvm::Log2_32(c->getAlignment()); + assert(p2Align < array_lengthof(instances)); + auto *&mc = instances[p2Align]; + if (!mc) + mc = make(c->getAlignment()); + mc->sections.push_back(c); } void MergeChunk::finalizeContents() { - if (!Finalized) { - for (SectionChunk *C : Sections) - if (C->Live) - Builder.add(toStringRef(C->getContents())); - Builder.finalize(); - Finalized = true; - } + assert(!finalized && "should only finalize once"); + for (SectionChunk *c : sections) + if (c->live) + builder.add(toStringRef(c->getContents())); + builder.finalize(); + finalized = true; +} - for (SectionChunk *C : Sections) { - if (!C->Live) +void MergeChunk::assignSubsectionRVAs() { + for (SectionChunk *c : sections) { + if (!c->live) continue; - size_t Off = Builder.getOffset(toStringRef(C->getContents())); - C->setOutputSection(Out); - C->setRVA(RVA + Off); - C->OutputSectionOff = OutputSectionOff + Off; + size_t off = builder.getOffset(toStringRef(c->getContents())); + c->setRVA(rva + off); } } uint32_t MergeChunk::getOutputCharacteristics() const { return IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA; } size_t MergeChunk::getSize() const { - return Builder.getSize(); + return builder.getSize(); } -void MergeChunk::writeTo(uint8_t *Buf) const { - Builder.write(Buf + OutputSectionOff); +void MergeChunk::writeTo(uint8_t *buf) const { + builder.write(buf); } // MinGW specific. -size_t AbsolutePointerChunk::getSize() const { return Config->Wordsize; } +size_t AbsolutePointerChunk::getSize() const { return config->wordsize; } -void AbsolutePointerChunk::writeTo(uint8_t *Buf) const { - if (Config->is64()) { - write64le(Buf + OutputSectionOff, Value); +void AbsolutePointerChunk::writeTo(uint8_t *buf) const { + if (config->is64()) { + write64le(buf, value); } else { - write32le(Buf + OutputSectionOff, Value); + write32le(buf, value); } } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/Chunks.h =================================================================== --- vendor/lld/dist/COFF/Chunks.h (revision 351287) +++ vendor/lld/dist/COFF/Chunks.h (revision 351288) @@ -1,518 +1,686 @@ //===- Chunks.h -------------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #ifndef LLD_COFF_CHUNKS_H #define LLD_COFF_CHUNKS_H #include "Config.h" #include "InputFiles.h" #include "lld/Common/LLVM.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/COFF.h" #include #include namespace lld { namespace coff { using llvm::COFF::ImportDirectoryTableEntry; using llvm::object::COFFSymbolRef; using llvm::object::SectionRef; using llvm::object::coff_relocation; using llvm::object::coff_section; class Baserel; class Defined; class DefinedImportData; class DefinedRegular; class ObjFile; class OutputSection; class RuntimePseudoReloc; class Symbol; // Mask for permissions (discardable, writable, readable, executable, etc). -const uint32_t PermMask = 0xFE000000; +const uint32_t permMask = 0xFE000000; // Mask for section types (code, data, bss). -const uint32_t TypeMask = 0x000000E0; +const uint32_t typeMask = 0x000000E0; +// The log base 2 of the largest section alignment, which is log2(8192), or 13. +enum : unsigned { Log2MaxSectionAlignment = 13 }; + // A Chunk represents a chunk of data that will occupy space in the // output (if the resolver chose that). It may or may not be backed by // a section of an input file. It could be linker-created data, or // doesn't even have actual data (if common or bss). class Chunk { public: - enum Kind { SectionKind, OtherKind }; - Kind kind() const { return ChunkKind; } - virtual ~Chunk() = default; + enum Kind : uint8_t { SectionKind, OtherKind, ImportThunkKind }; + Kind kind() const { return chunkKind; } // Returns the size of this chunk (even if this is a common or BSS.) - virtual size_t getSize() const = 0; + size_t getSize() const; + // Returns chunk alignment in power of two form. Value values are powers of + // two from 1 to 8192. + uint32_t getAlignment() const { return 1U << p2Align; } + + // Update the chunk section alignment measured in bytes. Internally alignment + // is stored in log2. + void setAlignment(uint32_t align) { + // Treat zero byte alignment as 1 byte alignment. + align = align ? align : 1; + assert(llvm::isPowerOf2_32(align) && "alignment is not a power of 2"); + p2Align = llvm::Log2_32(align); + assert(p2Align <= Log2MaxSectionAlignment && + "impossible requested alignment"); + } + // Write this chunk to a mmap'ed file, assuming Buf is pointing to // beginning of the file. Because this function may use RVA values // of other chunks for relocations, you need to set them properly // before calling this function. - virtual void writeTo(uint8_t *Buf) const {} + void writeTo(uint8_t *buf) const; - // Called by the writer once before assigning addresses and writing - // the output. - virtual void readRelocTargets() {} + // The writer sets and uses the addresses. In practice, PE images cannot be + // larger than 2GB. Chunks are always laid as part of the image, so Chunk RVAs + // can be stored with 32 bits. + uint32_t getRVA() const { return rva; } + void setRVA(uint64_t v) { + rva = (uint32_t)v; + assert(rva == v && "RVA truncated"); + } - // Called if restarting thunk addition. - virtual void resetRelocTargets() {} + // Returns readable/writable/executable bits. + uint32_t getOutputCharacteristics() const; - // Called by the writer after an RVA is assigned, but before calling - // getSize(). - virtual void finalizeContents() {} + // Returns the section name if this is a section chunk. + // It is illegal to call this function on non-section chunks. + StringRef getSectionName() const; - // The writer sets and uses the addresses. - uint64_t getRVA() const { return RVA; } - void setRVA(uint64_t V) { RVA = V; } + // An output section has pointers to chunks in the section, and each + // chunk has a back pointer to an output section. + void setOutputSectionIdx(uint16_t o) { osidx = o; } + uint16_t getOutputSectionIdx() const { return osidx; } + OutputSection *getOutputSection() const; + // Windows-specific. + // Collect all locations that contain absolute addresses for base relocations. + void getBaserels(std::vector *res); + + // Returns a human-readable name of this chunk. Chunks are unnamed chunks of + // bytes, so this is used only for logging or debugging. + StringRef getDebugName() const; + + // Return true if this file has the hotpatch flag set to true in the + // S_COMPILE3 record in codeview debug info. Also returns true for some thunks + // synthesized by the linker. + bool isHotPatchable() const; + +protected: + Chunk(Kind k = OtherKind) : chunkKind(k), hasData(true), p2Align(0) {} + + const Kind chunkKind; + +public: // Returns true if this has non-zero data. BSS chunks return // false. If false is returned, the space occupied by this chunk - // will be filled with zeros. - virtual bool hasData() const { return true; } + // will be filled with zeros. Corresponds to the + // IMAGE_SCN_CNT_UNINITIALIZED_DATA section characteristic bit. + uint8_t hasData : 1; - // Returns readable/writable/executable bits. +public: + // The alignment of this chunk, stored in log2 form. The writer uses the + // value. + uint8_t p2Align : 7; + + // The output section index for this chunk. The first valid section number is + // one. + uint16_t osidx = 0; + + // The RVA of this chunk in the output. The writer sets a value. + uint32_t rva = 0; +}; + +class NonSectionChunk : public Chunk { +public: + virtual ~NonSectionChunk() = default; + + // Returns the size of this chunk (even if this is a common or BSS.) + virtual size_t getSize() const = 0; + virtual uint32_t getOutputCharacteristics() const { return 0; } + // Write this chunk to a mmap'ed file, assuming Buf is pointing to + // beginning of the file. Because this function may use RVA values + // of other chunks for relocations, you need to set them properly + // before calling this function. + virtual void writeTo(uint8_t *buf) const {} + // Returns the section name if this is a section chunk. // It is illegal to call this function on non-section chunks. virtual StringRef getSectionName() const { llvm_unreachable("unimplemented getSectionName"); } - // An output section has pointers to chunks in the section, and each - // chunk has a back pointer to an output section. - void setOutputSection(OutputSection *O) { Out = O; } - OutputSection *getOutputSection() const { return Out; } - // Windows-specific. // Collect all locations that contain absolute addresses for base relocations. - virtual void getBaserels(std::vector *Res) {} + virtual void getBaserels(std::vector *res) {} // Returns a human-readable name of this chunk. Chunks are unnamed chunks of // bytes, so this is used only for logging or debugging. - virtual StringRef getDebugName() { return ""; } + virtual StringRef getDebugName() const { return ""; } - // The alignment of this chunk. The writer uses the value. - uint32_t Alignment = 1; + static bool classof(const Chunk *c) { return c->kind() != SectionKind; } protected: - Chunk(Kind K = OtherKind) : ChunkKind(K) {} - const Kind ChunkKind; - - // The RVA of this chunk in the output. The writer sets a value. - uint64_t RVA = 0; - - // The 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; - - // Whether this section needs to be kept distinct from other sections during - // ICF. This is set by the driver using address-significance tables. - bool KeepUnique = false; + NonSectionChunk(Kind k = OtherKind) : Chunk(k) {} }; // A chunk corresponding a section of an input file. class SectionChunk final : public Chunk { // Identical COMDAT Folding feature accesses section internal data. friend class ICF; public: class symbol_iterator : public llvm::iterator_adaptor_base< symbol_iterator, const coff_relocation *, std::random_access_iterator_tag, Symbol *> { friend SectionChunk; - ObjFile *File; + ObjFile *file; - symbol_iterator(ObjFile *File, const coff_relocation *I) - : symbol_iterator::iterator_adaptor_base(I), File(File) {} + symbol_iterator(ObjFile *file, const coff_relocation *i) + : symbol_iterator::iterator_adaptor_base(i), file(file) {} public: symbol_iterator() = default; - Symbol *operator*() const { return File->getSymbol(I->SymbolTableIndex); } + Symbol *operator*() const { return file->getSymbol(I->SymbolTableIndex); } }; - SectionChunk(ObjFile *File, const coff_section *Header); - static bool classof(const Chunk *C) { return C->kind() == SectionKind; } - void readRelocTargets() override; - void resetRelocTargets() override; - size_t getSize() const override { return Header->SizeOfRawData; } + SectionChunk(ObjFile *file, const coff_section *header); + static bool classof(const Chunk *c) { return c->kind() == SectionKind; } + size_t getSize() const { return header->SizeOfRawData; } ArrayRef getContents() const; - void writeTo(uint8_t *Buf) const override; - bool hasData() const override; - uint32_t getOutputCharacteristics() const override; - StringRef getSectionName() const override { return SectionName; } - void getBaserels(std::vector *Res) override; + void writeTo(uint8_t *buf) const; + + uint32_t getOutputCharacteristics() const { + return header->Characteristics & (permMask | typeMask); + } + StringRef getSectionName() const { + return StringRef(sectionNameData, sectionNameSize); + } + void getBaserels(std::vector *res); bool isCOMDAT() const; - void applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S, - uint64_t P) const; - void applyRelX86(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S, - uint64_t P) const; - void applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S, - uint64_t P) const; - void applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S, - uint64_t P) const; + void applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, + uint64_t p) const; + void applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, + uint64_t p) const; + void applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, + uint64_t p) const; + void applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, + uint64_t p) const; - void getRuntimePseudoRelocs(std::vector &Res); + void getRuntimePseudoRelocs(std::vector &res); // Called if the garbage collector decides to not include this chunk // in a final output. It's supposed to print out a log message to stdout. void printDiscardedMessage() const; // Adds COMDAT associative sections to this COMDAT section. A chunk // and its children are treated as a group by the garbage collector. - void addAssociative(SectionChunk *Child); + void addAssociative(SectionChunk *child); - StringRef getDebugName() override; + StringRef getDebugName() const; // 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$"); + return getSectionName() == ".debug" || getSectionName().startswith(".debug$"); } // True if this is a DWARF debug info or exception handling chunk. bool isDWARF() const { - return SectionName.startswith(".debug_") || SectionName == ".eh_frame"; + return getSectionName().startswith(".debug_") || getSectionName() == ".eh_frame"; } // Allow iteration over the bodies of this chunk's relocated symbols. llvm::iterator_range symbols() const { - return llvm::make_range(symbol_iterator(File, Relocs.begin()), - symbol_iterator(File, Relocs.end())); + return llvm::make_range(symbol_iterator(file, relocsData), + symbol_iterator(file, relocsData + relocsSize)); } + ArrayRef getRelocs() const { + return llvm::makeArrayRef(relocsData, relocsSize); + } + + // Reloc setter used by ARM range extension thunk insertion. + void setRelocs(ArrayRef newRelocs) { + relocsData = newRelocs.data(); + relocsSize = newRelocs.size(); + assert(relocsSize == newRelocs.size() && "reloc size truncation"); + } + + // Single linked list iterator for associated comdat children. + class AssociatedIterator + : public llvm::iterator_facade_base< + AssociatedIterator, std::forward_iterator_tag, SectionChunk> { + public: + AssociatedIterator() = default; + AssociatedIterator(SectionChunk *head) : cur(head) {} + AssociatedIterator &operator=(const AssociatedIterator &r) { + cur = r.cur; + return *this; + } + bool operator==(const AssociatedIterator &r) const { return cur == r.cur; } + const SectionChunk &operator*() const { return *cur; } + SectionChunk &operator*() { return *cur; } + AssociatedIterator &operator++() { + cur = cur->assocChildren; + return *this; + } + + private: + SectionChunk *cur = nullptr; + }; + // Allow iteration over the associated child chunks for this section. - ArrayRef children() const { return AssocChildren; } + llvm::iterator_range children() const { + return llvm::make_range(AssociatedIterator(assocChildren), + AssociatedIterator(nullptr)); + } // The section ID this chunk belongs to in its Obj. uint32_t getSectionNumber() const; - // A pointer pointing to a replacement for this chunk. - // Initially it points to "this" object. If this chunk is merged - // with other chunk by ICF, it points to another chunk, - // and this chunk is considered as dead. - SectionChunk *Repl; + ArrayRef consumeDebugMagic(); - // The CRC of the contents as described in the COFF spec 4.5.5. - // Auxiliary Format 5: Section Definitions. Used for ICF. - uint32_t Checksum = 0; + static ArrayRef consumeDebugMagic(ArrayRef data, + StringRef sectionName); - const coff_section *Header; + static SectionChunk *findByName(ArrayRef sections, + StringRef name); // The file that this chunk was created from. - ObjFile *File; + ObjFile *file; + // Pointer to the COFF section header in the input file. + const coff_section *header; + // The COMDAT leader symbol if this is a COMDAT chunk. - DefinedRegular *Sym = nullptr; + DefinedRegular *sym = nullptr; - ArrayRef Relocs; + // The CRC of the contents as described in the COFF spec 4.5.5. + // Auxiliary Format 5: Section Definitions. Used for ICF. + uint32_t checksum = 0; // Used by the garbage collector. - bool Live; + bool live; - // When inserting a thunk, we need to adjust a relocation to point to - // the thunk instead of the actual original target Symbol. - std::vector RelocTargets; + // Whether this section needs to be kept distinct from other sections during + // ICF. This is set by the driver using address-significance tables. + bool keepUnique = false; + // The COMDAT selection if this is a COMDAT chunk. + llvm::COFF::COMDATType selection = (llvm::COFF::COMDATType)0; + + // A pointer pointing to a replacement for this chunk. + // Initially it points to "this" object. If this chunk is merged + // with other chunk by ICF, it points to another chunk, + // and this chunk is considered as dead. + SectionChunk *repl; + private: - StringRef SectionName; - std::vector AssocChildren; + SectionChunk *assocChildren = nullptr; // Used for ICF (Identical COMDAT Folding) - void replace(SectionChunk *Other); - uint32_t Class[2] = {0, 0}; + void replace(SectionChunk *other); + uint32_t eqClass[2] = {0, 0}; + + // Relocations for this section. Size is stored below. + const coff_relocation *relocsData; + + // Section name string. Size is stored below. + const char *sectionNameData; + + uint32_t relocsSize = 0; + uint32_t sectionNameSize = 0; }; +// Inline methods to implement faux-virtual dispatch for SectionChunk. + +inline size_t Chunk::getSize() const { + if (isa(this)) + return static_cast(this)->getSize(); + else + return static_cast(this)->getSize(); +} + +inline uint32_t Chunk::getOutputCharacteristics() const { + if (isa(this)) + return static_cast(this)->getOutputCharacteristics(); + else + return static_cast(this) + ->getOutputCharacteristics(); +} + +inline void Chunk::writeTo(uint8_t *buf) const { + if (isa(this)) + static_cast(this)->writeTo(buf); + else + static_cast(this)->writeTo(buf); +} + +inline StringRef Chunk::getSectionName() const { + if (isa(this)) + return static_cast(this)->getSectionName(); + else + return static_cast(this)->getSectionName(); +} + +inline void Chunk::getBaserels(std::vector *res) { + if (isa(this)) + static_cast(this)->getBaserels(res); + else + static_cast(this)->getBaserels(res); +} + +inline StringRef Chunk::getDebugName() const { + if (isa(this)) + return static_cast(this)->getDebugName(); + else + return static_cast(this)->getDebugName(); +} + // This class is used to implement an lld-specific feature (not implemented in // MSVC) that minimizes the output size by finding string literals sharing tail // parts and merging them. // // If string tail merging is enabled and a section is identified as containing a // string literal, it is added to a MergeChunk with an appropriate alignment. // The MergeChunk then tail merges the strings using the StringTableBuilder // class and assigns RVAs and section offsets to each of the member chunks based // on the offsets assigned by the StringTableBuilder. -class MergeChunk : public Chunk { +class MergeChunk : public NonSectionChunk { public: - MergeChunk(uint32_t Alignment); - static void addSection(SectionChunk *C); - void finalizeContents() override; + MergeChunk(uint32_t alignment); + static void addSection(SectionChunk *c); + void finalizeContents(); + void assignSubsectionRVAs(); uint32_t getOutputCharacteristics() const override; StringRef getSectionName() const override { return ".rdata"; } size_t getSize() const override; - void writeTo(uint8_t *Buf) const override; + void writeTo(uint8_t *buf) const override; - static std::map Instances; - std::vector Sections; + static MergeChunk *instances[Log2MaxSectionAlignment + 1]; + std::vector sections; private: - llvm::StringTableBuilder Builder; - bool Finalized = false; + llvm::StringTableBuilder builder; + bool finalized = false; }; // A chunk for common symbols. Common chunks don't have actual data. -class CommonChunk : public Chunk { +class CommonChunk : public NonSectionChunk { public: - CommonChunk(const COFFSymbolRef Sym); - size_t getSize() const override { return Sym.getValue(); } - bool hasData() const override { return false; } + CommonChunk(const COFFSymbolRef sym); + size_t getSize() const override { return sym.getValue(); } uint32_t getOutputCharacteristics() const override; StringRef getSectionName() const override { return ".bss"; } private: - const COFFSymbolRef Sym; + const COFFSymbolRef sym; }; // A chunk for linker-created strings. -class StringChunk : public Chunk { +class StringChunk : public NonSectionChunk { public: - explicit StringChunk(StringRef S) : Str(S) {} - size_t getSize() const override { return Str.size() + 1; } - void writeTo(uint8_t *Buf) const override; + explicit StringChunk(StringRef s) : str(s) {} + size_t getSize() const override { return str.size() + 1; } + void writeTo(uint8_t *buf) const override; private: - StringRef Str; + StringRef str; }; -static const uint8_t ImportThunkX86[] = { +static const uint8_t importThunkX86[] = { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // JMP *0x0 }; -static const uint8_t ImportThunkARM[] = { +static const uint8_t importThunkARM[] = { 0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 0xdc, 0xf8, 0x00, 0xf0, // ldr.w pc, [ip] }; -static const uint8_t ImportThunkARM64[] = { +static const uint8_t importThunkARM64[] = { 0x10, 0x00, 0x00, 0x90, // adrp x16, #0 0x10, 0x02, 0x40, 0xf9, // ldr x16, [x16] 0x00, 0x02, 0x1f, 0xd6, // br x16 }; // Windows-specific. // A chunk for DLL import jump table entry. In a final output, its // contents will be a JMP instruction to some __imp_ symbol. -class ImportThunkChunkX64 : public Chunk { +class ImportThunkChunk : public NonSectionChunk { public: - explicit ImportThunkChunkX64(Defined *S); - size_t getSize() const override { return sizeof(ImportThunkX86); } - void writeTo(uint8_t *Buf) const override; + ImportThunkChunk(Defined *s) + : NonSectionChunk(ImportThunkKind), impSymbol(s) {} + static bool classof(const Chunk *c) { return c->kind() == ImportThunkKind; } -private: - Defined *ImpSymbol; +protected: + Defined *impSymbol; }; -class ImportThunkChunkX86 : public Chunk { +class ImportThunkChunkX64 : public ImportThunkChunk { public: - explicit ImportThunkChunkX86(Defined *S) : ImpSymbol(S) {} - size_t getSize() const override { return sizeof(ImportThunkX86); } - void getBaserels(std::vector *Res) override; - void writeTo(uint8_t *Buf) const override; + explicit ImportThunkChunkX64(Defined *s); + size_t getSize() const override { return sizeof(importThunkX86); } + void writeTo(uint8_t *buf) const override; +}; -private: - Defined *ImpSymbol; +class ImportThunkChunkX86 : public ImportThunkChunk { +public: + explicit ImportThunkChunkX86(Defined *s) : ImportThunkChunk(s) {} + size_t getSize() const override { return sizeof(importThunkX86); } + void getBaserels(std::vector *res) override; + void writeTo(uint8_t *buf) const override; }; -class ImportThunkChunkARM : public Chunk { +class ImportThunkChunkARM : public ImportThunkChunk { public: - explicit ImportThunkChunkARM(Defined *S) : ImpSymbol(S) {} - size_t getSize() const override { return sizeof(ImportThunkARM); } - void getBaserels(std::vector *Res) override; - void writeTo(uint8_t *Buf) const override; + explicit ImportThunkChunkARM(Defined *s) : ImportThunkChunk(s) {} + size_t getSize() const override { return sizeof(importThunkARM); } + void getBaserels(std::vector *res) override; + void writeTo(uint8_t *buf) const override; +}; -private: - Defined *ImpSymbol; +class ImportThunkChunkARM64 : public ImportThunkChunk { +public: + explicit ImportThunkChunkARM64(Defined *s) : ImportThunkChunk(s) {} + size_t getSize() const override { return sizeof(importThunkARM64); } + void writeTo(uint8_t *buf) const override; }; -class ImportThunkChunkARM64 : public Chunk { +class RangeExtensionThunkARM : public NonSectionChunk { public: - explicit ImportThunkChunkARM64(Defined *S) : ImpSymbol(S) {} - size_t getSize() const override { return sizeof(ImportThunkARM64); } - void writeTo(uint8_t *Buf) const override; + explicit RangeExtensionThunkARM(Defined *t) : target(t) {} + size_t getSize() const override; + void writeTo(uint8_t *buf) const override; -private: - Defined *ImpSymbol; + Defined *target; }; -class RangeExtensionThunk : public Chunk { +class RangeExtensionThunkARM64 : public NonSectionChunk { public: - explicit RangeExtensionThunk(Defined *T) : Target(T) {} + explicit RangeExtensionThunkARM64(Defined *t) : target(t) {} size_t getSize() const override; - void writeTo(uint8_t *Buf) const override; + void writeTo(uint8_t *buf) const override; - Defined *Target; + Defined *target; }; // Windows-specific. // See comments for DefinedLocalImport class. -class LocalImportChunk : public Chunk { +class LocalImportChunk : public NonSectionChunk { public: - explicit LocalImportChunk(Defined *S) : Sym(S) { - Alignment = Config->Wordsize; + explicit LocalImportChunk(Defined *s) : sym(s) { + setAlignment(config->wordsize); } size_t getSize() const override; - void getBaserels(std::vector *Res) override; - void writeTo(uint8_t *Buf) const override; + void getBaserels(std::vector *res) override; + void writeTo(uint8_t *buf) const override; private: - Defined *Sym; + Defined *sym; }; // Duplicate RVAs are not allowed in RVA tables, so unique symbols by chunk and // offset into the chunk. Order does not matter as the RVA table will be sorted // later. struct ChunkAndOffset { - Chunk *InputChunk; - uint32_t Offset; + Chunk *inputChunk; + uint32_t offset; struct DenseMapInfo { static ChunkAndOffset getEmptyKey() { return {llvm::DenseMapInfo::getEmptyKey(), 0}; } static ChunkAndOffset getTombstoneKey() { return {llvm::DenseMapInfo::getTombstoneKey(), 0}; } - static unsigned getHashValue(const ChunkAndOffset &CO) { + static unsigned getHashValue(const ChunkAndOffset &co) { return llvm::DenseMapInfo>::getHashValue( - {CO.InputChunk, CO.Offset}); + {co.inputChunk, co.offset}); } - static bool isEqual(const ChunkAndOffset &LHS, const ChunkAndOffset &RHS) { - return LHS.InputChunk == RHS.InputChunk && LHS.Offset == RHS.Offset; + static bool isEqual(const ChunkAndOffset &lhs, const ChunkAndOffset &rhs) { + return lhs.inputChunk == rhs.inputChunk && lhs.offset == rhs.offset; } }; }; using SymbolRVASet = llvm::DenseSet; // Table which contains symbol RVAs. Used for /safeseh and /guard:cf. -class RVATableChunk : public Chunk { +class RVATableChunk : public NonSectionChunk { public: - explicit RVATableChunk(SymbolRVASet S) : Syms(std::move(S)) {} - size_t getSize() const override { return Syms.size() * 4; } - void writeTo(uint8_t *Buf) const override; + explicit RVATableChunk(SymbolRVASet s) : syms(std::move(s)) {} + size_t getSize() const override { return syms.size() * 4; } + void writeTo(uint8_t *buf) const override; private: - SymbolRVASet Syms; + SymbolRVASet syms; }; // Windows-specific. // This class represents a block in .reloc section. // See the PE/COFF spec 5.6 for details. -class BaserelChunk : public Chunk { +class BaserelChunk : public NonSectionChunk { public: - BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End); - size_t getSize() const override { return Data.size(); } - void writeTo(uint8_t *Buf) const override; + BaserelChunk(uint32_t page, Baserel *begin, Baserel *end); + size_t getSize() const override { return data.size(); } + void writeTo(uint8_t *buf) const override; private: - std::vector Data; + std::vector data; }; class Baserel { public: - Baserel(uint32_t V, uint8_t Ty) : RVA(V), Type(Ty) {} - explicit Baserel(uint32_t V) : Baserel(V, getDefaultType()) {} + Baserel(uint32_t v, uint8_t ty) : rva(v), type(ty) {} + explicit Baserel(uint32_t v) : Baserel(v, getDefaultType()) {} uint8_t getDefaultType(); - uint32_t RVA; - uint8_t Type; + uint32_t rva; + uint8_t type; }; // This is a placeholder Chunk, to allow attaching a DefinedSynthetic to a // specific place in a section, without any data. This is used for the MinGW // specific symbol __RUNTIME_PSEUDO_RELOC_LIST_END__, even though the concept // of an empty chunk isn't MinGW specific. -class EmptyChunk : public Chunk { +class EmptyChunk : public NonSectionChunk { public: EmptyChunk() {} size_t getSize() const override { return 0; } - void writeTo(uint8_t *Buf) const override {} + void writeTo(uint8_t *buf) const override {} }; // MinGW specific, for the "automatic import of variables from DLLs" feature. // This provides the table of runtime pseudo relocations, for variable // references that turned out to need to be imported from a DLL even though // the reference didn't use the dllimport attribute. The MinGW runtime will // process this table after loading, before handling control over to user // code. -class PseudoRelocTableChunk : public Chunk { +class PseudoRelocTableChunk : public NonSectionChunk { public: - PseudoRelocTableChunk(std::vector &Relocs) - : Relocs(std::move(Relocs)) { - Alignment = 4; + PseudoRelocTableChunk(std::vector &relocs) + : relocs(std::move(relocs)) { + setAlignment(4); } size_t getSize() const override; - void writeTo(uint8_t *Buf) const override; + void writeTo(uint8_t *buf) const override; private: - std::vector Relocs; + std::vector relocs; }; // MinGW specific; information about one individual location in the image // that needs to be fixed up at runtime after loading. This represents // one individual element in the PseudoRelocTableChunk table. class RuntimePseudoReloc { public: - RuntimePseudoReloc(Defined *Sym, SectionChunk *Target, uint32_t TargetOffset, - int Flags) - : Sym(Sym), Target(Target), TargetOffset(TargetOffset), Flags(Flags) {} + RuntimePseudoReloc(Defined *sym, SectionChunk *target, uint32_t targetOffset, + int flags) + : sym(sym), target(target), targetOffset(targetOffset), flags(flags) {} - Defined *Sym; - SectionChunk *Target; - uint32_t TargetOffset; + Defined *sym; + SectionChunk *target; + uint32_t targetOffset; // The Flags field contains the size of the relocation, in bits. No other // flags are currently defined. - int Flags; + int flags; }; // MinGW specific. A Chunk that contains one pointer-sized absolute value. -class AbsolutePointerChunk : public Chunk { +class AbsolutePointerChunk : public NonSectionChunk { public: - AbsolutePointerChunk(uint64_t Value) : Value(Value) { - Alignment = getSize(); + AbsolutePointerChunk(uint64_t value) : value(value) { + setAlignment(getSize()); } size_t getSize() const override; - void writeTo(uint8_t *Buf) const override; + void writeTo(uint8_t *buf) const override; private: - uint64_t Value; + uint64_t value; }; -void applyMOV32T(uint8_t *Off, uint32_t V); -void applyBranch24T(uint8_t *Off, int32_t V); +// Return true if this file has the hotpatch flag set to true in the S_COMPILE3 +// record in codeview debug info. Also returns true for some thunks synthesized +// by the linker. +inline bool Chunk::isHotPatchable() const { + if (auto *sc = dyn_cast(this)) + return sc->file->hotPatchable; + else if (isa(this)) + return true; + return false; +} -void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift); -void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit); -void applyArm64Branch26(uint8_t *Off, int64_t V); +void applyMOV32T(uint8_t *off, uint32_t v); +void applyBranch24T(uint8_t *off, int32_t v); + +void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift); +void applyArm64Imm(uint8_t *off, uint64_t imm, uint32_t rangeLimit); +void applyArm64Branch26(uint8_t *off, int64_t v); } // namespace coff } // namespace lld namespace llvm { template <> struct DenseMapInfo : lld::coff::ChunkAndOffset::DenseMapInfo {}; } #endif Index: vendor/lld/dist/COFF/Config.h =================================================================== --- vendor/lld/dist/COFF/Config.h (revision 351287) +++ vendor/lld/dist/COFF/Config.h (revision 351288) @@ -1,212 +1,231 @@ //===- Config.h -------------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #ifndef LLD_COFF_CONFIG_H #define LLD_COFF_CONFIG_H #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/COFF.h" #include "llvm/Support/CachePruning.h" #include #include #include #include namespace lld { namespace coff { using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; using llvm::COFF::WindowsSubsystem; using llvm::StringRef; class DefinedAbsolute; class DefinedRelative; class StringChunk; class Symbol; +class InputFile; // Short aliases. static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64; static const auto ARM64 = llvm::COFF::IMAGE_FILE_MACHINE_ARM64; static const auto ARMNT = llvm::COFF::IMAGE_FILE_MACHINE_ARMNT; static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386; // Represents an /export option. struct Export { - StringRef Name; // N in /export:N or /export:E=N - StringRef ExtName; // E in /export:E=N - Symbol *Sym = nullptr; - uint16_t Ordinal = 0; - bool Noname = false; - bool Data = false; - bool Private = false; - bool Constant = false; + StringRef name; // N in /export:N or /export:E=N + StringRef extName; // E in /export:E=N + Symbol *sym = nullptr; + uint16_t ordinal = 0; + bool noname = false; + bool data = false; + bool isPrivate = false; + bool constant = false; // If an export is a form of /export:foo=dllname.bar, that means // that foo should be exported as an alias to bar in the DLL. - // ForwardTo is set to "dllname.bar" part. Usually empty. - StringRef ForwardTo; - StringChunk *ForwardChunk = nullptr; + // forwardTo is set to "dllname.bar" part. Usually empty. + StringRef forwardTo; + StringChunk *forwardChunk = nullptr; // True if this /export option was in .drectves section. - bool Directives = false; - StringRef SymbolName; - StringRef ExportName; // Name in DLL + bool directives = false; + StringRef symbolName; + StringRef exportName; // Name in DLL - bool operator==(const Export &E) { - return (Name == E.Name && ExtName == E.ExtName && - Ordinal == E.Ordinal && Noname == E.Noname && - Data == E.Data && Private == E.Private); + bool operator==(const Export &e) { + return (name == e.name && extName == e.extName && + ordinal == e.ordinal && noname == e.noname && + data == e.data && isPrivate == e.isPrivate); } }; enum class DebugType { None = 0x0, CV = 0x1, /// CodeView PData = 0x2, /// Procedure Data Fixup = 0x4, /// Relocation Table }; enum class GuardCFLevel { Off, NoLongJmp, // Emit gfids but no longjmp tables Full, // Enable all protections. }; // Global configuration. struct Configuration { enum ManifestKind { SideBySide, Embed, No }; - bool is64() { return Machine == AMD64 || Machine == ARM64; } + bool is64() { return machine == AMD64 || machine == ARM64; } - llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN; - size_t Wordsize; - bool Verbose = false; - WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN; - Symbol *Entry = nullptr; - bool NoEntry = false; - std::string OutputFile; - std::string ImportName; - bool DoGC = true; - bool DoICF = true; - bool TailMerge; - bool Relocatable = true; - bool ForceMultiple = false; - bool ForceUnresolved = false; - bool Debug = false; - bool DebugDwarf = false; - bool DebugGHashes = false; - bool DebugSymtab = false; - bool ShowTiming = false; - unsigned DebugTypes = static_cast(DebugType::None); - std::vector NatvisFiles; - llvm::SmallString<128> PDBAltPath; - llvm::SmallString<128> PDBPath; - llvm::SmallString<128> PDBSourcePath; - std::vector Argv; + llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN; + size_t wordsize; + bool verbose = false; + WindowsSubsystem subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN; + Symbol *entry = nullptr; + bool noEntry = false; + std::string outputFile; + std::string importName; + bool demangle = true; + bool doGC = true; + bool doICF = true; + bool tailMerge; + bool relocatable = true; + bool forceMultiple = false; + bool forceMultipleRes = false; + bool forceUnresolved = false; + bool debug = false; + bool debugDwarf = false; + bool debugGHashes = false; + bool debugSymtab = false; + bool showTiming = false; + bool showSummary = false; + unsigned debugTypes = static_cast(DebugType::None); + std::vector natvisFiles; + llvm::SmallString<128> pdbAltPath; + llvm::SmallString<128> pdbPath; + llvm::SmallString<128> pdbSourcePath; + std::vector argv; // Symbols in this set are considered as live by the garbage collector. - std::vector GCRoot; + std::vector gcroot; - std::set NoDefaultLibs; - bool NoDefaultLibAll = false; + std::set noDefaultLibs; + bool noDefaultLibAll = false; // True if we are creating a DLL. - bool DLL = false; - StringRef Implib; - std::vector Exports; - std::set DelayLoads; - std::map DLLOrder; - Symbol *DelayLoadHelper = nullptr; + bool dll = false; + StringRef implib; + std::vector exports; + std::set delayLoads; + std::map dllOrder; + Symbol *delayLoadHelper = nullptr; - bool SaveTemps = false; + bool saveTemps = false; // /guard:cf - GuardCFLevel GuardCF = GuardCFLevel::Off; + GuardCFLevel guardCF = GuardCFLevel::Off; // Used for SafeSEH. - Symbol *SEHTable = nullptr; - Symbol *SEHCount = nullptr; + bool safeSEH = false; + Symbol *sehTable = nullptr; + Symbol *sehCount = nullptr; // Used for /opt:lldlto=N - unsigned LTOO = 2; + unsigned ltoo = 2; // Used for /opt:lldltojobs=N - unsigned ThinLTOJobs = 0; + unsigned thinLTOJobs = 0; // Used for /opt:lldltopartitions=N - unsigned LTOPartitions = 1; + unsigned ltoPartitions = 1; // Used for /opt:lldltocache=path - StringRef LTOCache; + StringRef ltoCache; // Used for /opt:lldltocachepolicy=policy - llvm::CachePruningPolicy LTOCachePolicy; + llvm::CachePruningPolicy ltoCachePolicy; // Used for /merge:from=to (e.g. /merge:.rdata=.text) - std::map Merge; + std::map merge; // Used for /section=.name,{DEKPRSW} to set section attributes. - std::map Section; + std::map section; // Options for manifest files. - ManifestKind Manifest = No; - int ManifestID = 1; - StringRef ManifestDependency; - bool ManifestUAC = true; - std::vector ManifestInput; - StringRef ManifestLevel = "'asInvoker'"; - StringRef ManifestUIAccess = "'false'"; - StringRef ManifestFile; + ManifestKind manifest = No; + int manifestID = 1; + StringRef manifestDependency; + bool manifestUAC = true; + std::vector manifestInput; + StringRef manifestLevel = "'asInvoker'"; + StringRef manifestUIAccess = "'false'"; + StringRef manifestFile; // Used for /aligncomm. - std::map AlignComm; + std::map alignComm; // Used for /failifmismatch. - std::map MustMatch; + std::map> mustMatch; // Used for /alternatename. - std::map AlternateNames; + std::map alternateNames; // Used for /order. - llvm::StringMap Order; + llvm::StringMap order; // Used for /lldmap. - std::string MapFile; + std::string mapFile; - uint64_t ImageBase = -1; - uint64_t StackReserve = 1024 * 1024; - uint64_t StackCommit = 4096; - uint64_t HeapReserve = 1024 * 1024; - uint64_t HeapCommit = 4096; - uint32_t MajorImageVersion = 0; - uint32_t MinorImageVersion = 0; - uint32_t MajorOSVersion = 6; - uint32_t MinorOSVersion = 0; - uint32_t Timestamp = 0; - 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; - bool WarnMissingOrderSymbol = true; - bool WarnLocallyDefinedImported = true; - bool WarnDebugInfoUnusable = true; - bool Incremental = true; - bool IntegrityCheck = false; - bool KillAt = false; - bool Repro = false; + // Used for /thinlto-index-only: + llvm::StringRef thinLTOIndexOnlyArg; + + // Used for /thinlto-object-prefix-replace: + std::pair thinLTOPrefixReplace; + + // Used for /thinlto-object-suffix-replace: + std::pair thinLTOObjectSuffixReplace; + + uint64_t imageBase = -1; + uint64_t fileAlign = 512; + uint64_t stackReserve = 1024 * 1024; + uint64_t stackCommit = 4096; + uint64_t heapReserve = 1024 * 1024; + uint64_t heapCommit = 4096; + uint32_t majorImageVersion = 0; + uint32_t minorImageVersion = 0; + uint32_t majorOSVersion = 6; + uint32_t minorOSVersion = 0; + uint32_t timestamp = 0; + uint32_t functionPadMin = 0; + 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; + bool warnMissingOrderSymbol = true; + bool warnLocallyDefinedImported = true; + bool warnDebugInfoUnusable = true; + bool incremental = true; + bool integrityCheck = false; + bool killAt = false; + bool repro = false; + bool swaprunCD = false; + bool swaprunNet = false; + bool thinLTOEmitImportsFiles; + bool thinLTOIndexOnly; }; -extern Configuration *Config; +extern Configuration *config; } // namespace coff } // namespace lld #endif Index: vendor/lld/dist/COFF/DLL.cpp =================================================================== --- vendor/lld/dist/COFF/DLL.cpp (revision 351287) +++ vendor/lld/dist/COFF/DLL.cpp (revision 351288) @@ -1,631 +1,736 @@ //===- DLL.cpp ------------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// // // This file defines various types of chunks for the DLL import or export // descriptor tables. They are inherently Windows-specific. // You need to read Microsoft PE/COFF spec to understand details // about the data structures. // // If you are not particularly interested in linking against Windows // DLL, you can skip this file, and you should still be able to // understand the rest of the linker. // //===----------------------------------------------------------------------===// #include "DLL.h" #include "Chunks.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Path.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::COFF; namespace lld { namespace coff { namespace { // Import table // A chunk for the import descriptor table. -class HintNameChunk : public Chunk { +class HintNameChunk : public NonSectionChunk { public: - HintNameChunk(StringRef N, uint16_t H) : Name(N), Hint(H) {} + HintNameChunk(StringRef n, uint16_t h) : name(n), hint(h) {} size_t getSize() const override { // Starts with 2 byte Hint field, followed by a null-terminated string, // ends with 0 or 1 byte padding. - return alignTo(Name.size() + 3, 2); + return alignTo(name.size() + 3, 2); } - void writeTo(uint8_t *Buf) const override { - write16le(Buf + OutputSectionOff, Hint); - memcpy(Buf + OutputSectionOff + 2, Name.data(), Name.size()); + void writeTo(uint8_t *buf) const override { + memset(buf, 0, getSize()); + write16le(buf, hint); + memcpy(buf + 2, name.data(), name.size()); } private: - StringRef Name; - uint16_t Hint; + StringRef name; + uint16_t hint; }; // A chunk for the import descriptor table. -class LookupChunk : public Chunk { +class LookupChunk : public NonSectionChunk { public: - explicit LookupChunk(Chunk *C) : HintName(C) { Alignment = Config->Wordsize; } - size_t getSize() const override { return Config->Wordsize; } + explicit LookupChunk(Chunk *c) : hintName(c) { + setAlignment(config->wordsize); + } + size_t getSize() const override { return config->wordsize; } - void writeTo(uint8_t *Buf) const override { - write32le(Buf + OutputSectionOff, HintName->getRVA()); + void writeTo(uint8_t *buf) const override { + if (config->is64()) + write64le(buf, hintName->getRVA()); + else + write32le(buf, hintName->getRVA()); } - Chunk *HintName; + Chunk *hintName; }; // A chunk for the import descriptor table. // This chunk represent import-by-ordinal symbols. // See Microsoft PE/COFF spec 7.1. Import Header for details. -class OrdinalOnlyChunk : public Chunk { +class OrdinalOnlyChunk : public NonSectionChunk { public: - explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) { - Alignment = Config->Wordsize; + explicit OrdinalOnlyChunk(uint16_t v) : ordinal(v) { + setAlignment(config->wordsize); } - size_t getSize() const override { return Config->Wordsize; } + size_t getSize() const override { return config->wordsize; } - void writeTo(uint8_t *Buf) const override { + void writeTo(uint8_t *buf) const override { // An import-by-ordinal slot has MSB 1 to indicate that // this is import-by-ordinal (and not import-by-name). - if (Config->is64()) { - write64le(Buf + OutputSectionOff, (1ULL << 63) | Ordinal); + if (config->is64()) { + write64le(buf, (1ULL << 63) | ordinal); } else { - write32le(Buf + OutputSectionOff, (1ULL << 31) | Ordinal); + write32le(buf, (1ULL << 31) | ordinal); } } - uint16_t Ordinal; + uint16_t ordinal; }; // A chunk for the import descriptor table. -class ImportDirectoryChunk : public Chunk { +class ImportDirectoryChunk : public NonSectionChunk { public: - explicit ImportDirectoryChunk(Chunk *N) : DLLName(N) {} + explicit ImportDirectoryChunk(Chunk *n) : dllName(n) {} size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); } - void writeTo(uint8_t *Buf) const override { - auto *E = (coff_import_directory_table_entry *)(Buf + OutputSectionOff); - E->ImportLookupTableRVA = LookupTab->getRVA(); - E->NameRVA = DLLName->getRVA(); - E->ImportAddressTableRVA = AddressTab->getRVA(); + void writeTo(uint8_t *buf) const override { + memset(buf, 0, getSize()); + + auto *e = (coff_import_directory_table_entry *)(buf); + e->ImportLookupTableRVA = lookupTab->getRVA(); + e->NameRVA = dllName->getRVA(); + e->ImportAddressTableRVA = addressTab->getRVA(); } - Chunk *DLLName; - Chunk *LookupTab; - Chunk *AddressTab; + Chunk *dllName; + Chunk *lookupTab; + Chunk *addressTab; }; // A chunk representing null terminator in the import table. // Contents of this chunk is always null bytes. -class NullChunk : public Chunk { +class NullChunk : public NonSectionChunk { public: - explicit NullChunk(size_t N) : Size(N) {} - bool hasData() const override { return false; } - size_t getSize() const override { return Size; } + explicit NullChunk(size_t n) : size(n) { hasData = false; } + size_t getSize() const override { return size; } + void writeTo(uint8_t *buf) const override { + memset(buf, 0, size); + } + private: - size_t Size; + size_t size; }; static std::vector> -binImports(const std::vector &Imports) { +binImports(const std::vector &imports) { // Group DLL-imported symbols by DLL name because that's how // symbols are layed out in the import descriptor table. - auto Less = [](const std::string &A, const std::string &B) { - return Config->DLLOrder[A] < Config->DLLOrder[B]; + auto less = [](const std::string &a, const std::string &b) { + return config->dllOrder[a] < config->dllOrder[b]; }; std::map, - bool(*)(const std::string &, const std::string &)> M(Less); - for (DefinedImportData *Sym : Imports) - M[Sym->getDLLName().lower()].push_back(Sym); + bool(*)(const std::string &, const std::string &)> m(less); + for (DefinedImportData *sym : imports) + m[sym->getDLLName().lower()].push_back(sym); - std::vector> V; - for (auto &KV : M) { + std::vector> v; + for (auto &kv : m) { // Sort symbols by name for each group. - std::vector &Syms = KV.second; - std::sort(Syms.begin(), Syms.end(), - [](DefinedImportData *A, DefinedImportData *B) { - return A->getName() < B->getName(); + std::vector &syms = kv.second; + std::sort(syms.begin(), syms.end(), + [](DefinedImportData *a, DefinedImportData *b) { + return a->getName() < b->getName(); }); - V.push_back(std::move(Syms)); + v.push_back(std::move(syms)); } - return V; + return v; } // Export table // See Microsoft PE/COFF spec 4.3 for details. // A chunk for the delay import descriptor table etnry. -class DelayDirectoryChunk : public Chunk { +class DelayDirectoryChunk : public NonSectionChunk { public: - explicit DelayDirectoryChunk(Chunk *N) : DLLName(N) {} + explicit DelayDirectoryChunk(Chunk *n) : dllName(n) {} size_t getSize() const override { return sizeof(delay_import_directory_table_entry); } - void writeTo(uint8_t *Buf) const override { - auto *E = (delay_import_directory_table_entry *)(Buf + OutputSectionOff); - E->Attributes = 1; - E->Name = DLLName->getRVA(); - E->ModuleHandle = ModuleHandle->getRVA(); - E->DelayImportAddressTable = AddressTab->getRVA(); - E->DelayImportNameTable = NameTab->getRVA(); + void writeTo(uint8_t *buf) const override { + memset(buf, 0, getSize()); + + auto *e = (delay_import_directory_table_entry *)(buf); + e->Attributes = 1; + e->Name = dllName->getRVA(); + e->ModuleHandle = moduleHandle->getRVA(); + e->DelayImportAddressTable = addressTab->getRVA(); + e->DelayImportNameTable = nameTab->getRVA(); } - Chunk *DLLName; - Chunk *ModuleHandle; - Chunk *AddressTab; - Chunk *NameTab; + Chunk *dllName; + Chunk *moduleHandle; + Chunk *addressTab; + Chunk *nameTab; }; // Initial contents for delay-loaded functions. // This code calls __delayLoadHelper2 function to resolve a symbol // and then overwrites its jump table slot with the result // for subsequent function calls. -static const uint8_t ThunkX64[] = { +static const uint8_t thunkX64[] = { + 0x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_] + 0xE9, 0, 0, 0, 0, // jmp __tailMerge_ +}; + +static const uint8_t tailMergeX64[] = { 0x51, // push rcx 0x52, // push rdx 0x41, 0x50, // push r8 0x41, 0x51, // push r9 0x48, 0x83, 0xEC, 0x48, // sub rsp, 48h 0x66, 0x0F, 0x7F, 0x04, 0x24, // movdqa xmmword ptr [rsp], xmm0 0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x10, // movdqa xmmword ptr [rsp+10h], xmm1 0x66, 0x0F, 0x7F, 0x54, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h], xmm2 0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h], xmm3 - 0x48, 0x8D, 0x15, 0, 0, 0, 0, // lea rdx, [__imp_] + 0x48, 0x8B, 0xD0, // mov rdx, rax 0x48, 0x8D, 0x0D, 0, 0, 0, 0, // lea rcx, [___DELAY_IMPORT_...] 0xE8, 0, 0, 0, 0, // call __delayLoadHelper2 0x66, 0x0F, 0x6F, 0x04, 0x24, // movdqa xmm0, xmmword ptr [rsp] 0x66, 0x0F, 0x6F, 0x4C, 0x24, 0x10, // movdqa xmm1, xmmword ptr [rsp+10h] 0x66, 0x0F, 0x6F, 0x54, 0x24, 0x20, // movdqa xmm2, xmmword ptr [rsp+20h] 0x66, 0x0F, 0x6F, 0x5C, 0x24, 0x30, // movdqa xmm3, xmmword ptr [rsp+30h] 0x48, 0x83, 0xC4, 0x48, // add rsp, 48h 0x41, 0x59, // pop r9 0x41, 0x58, // pop r8 0x5A, // pop rdx 0x59, // pop rcx 0xFF, 0xE0, // jmp rax }; -static const uint8_t ThunkX86[] = { +static const uint8_t thunkX86[] = { + 0xB8, 0, 0, 0, 0, // mov eax, offset ___imp__ + 0xE9, 0, 0, 0, 0, // jmp __tailMerge_ +}; + +static const uint8_t tailMergeX86[] = { 0x51, // push ecx 0x52, // push edx - 0x68, 0, 0, 0, 0, // push offset ___imp__ + 0x50, // push eax 0x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR__dll 0xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@8 0x5A, // pop edx 0x59, // pop ecx 0xFF, 0xE0, // jmp eax }; -static const uint8_t ThunkARM[] = { +static const uint8_t thunkARM[] = { 0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 __imp_ 0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 __imp_ + 0x00, 0xf0, 0x00, 0xb8, // b.w __tailMerge_ +}; + +static const uint8_t tailMergeARM[] = { 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 }; -static const uint8_t ThunkARM64[] = { +static const uint8_t thunkARM64[] = { 0x11, 0x00, 0x00, 0x90, // adrp x17, #0 __imp_ 0x31, 0x02, 0x00, 0x91, // add x17, x17, #0 :lo12:__imp_ + 0x00, 0x00, 0x00, 0x14, // b __tailMerge_ +}; + +static const uint8_t tailMergeARM64[] = { 0xfd, 0x7b, 0xb3, 0xa9, // stp x29, x30, [sp, #-208]! 0xfd, 0x03, 0x00, 0x91, // mov x29, sp 0xe0, 0x07, 0x01, 0xa9, // stp x0, x1, [sp, #16] 0xe2, 0x0f, 0x02, 0xa9, // stp x2, x3, [sp, #32] 0xe4, 0x17, 0x03, 0xa9, // stp x4, x5, [sp, #48] 0xe6, 0x1f, 0x04, 0xa9, // stp x6, x7, [sp, #64] 0xe0, 0x87, 0x02, 0xad, // stp q0, q1, [sp, #80] 0xe2, 0x8f, 0x03, 0xad, // stp q2, q3, [sp, #112] 0xe4, 0x97, 0x04, 0xad, // stp q4, q5, [sp, #144] 0xe6, 0x9f, 0x05, 0xad, // stp q6, q7, [sp, #176] 0xe1, 0x03, 0x11, 0xaa, // mov x1, x17 0x00, 0x00, 0x00, 0x90, // adrp x0, #0 DELAY_IMPORT_DESCRIPTOR 0x00, 0x00, 0x00, 0x91, // add x0, x0, #0 :lo12:DELAY_IMPORT_DESCRIPTOR 0x00, 0x00, 0x00, 0x94, // bl #0 __delayLoadHelper2 0xf0, 0x03, 0x00, 0xaa, // mov x16, x0 0xe6, 0x9f, 0x45, 0xad, // ldp q6, q7, [sp, #176] 0xe4, 0x97, 0x44, 0xad, // ldp q4, q5, [sp, #144] 0xe2, 0x8f, 0x43, 0xad, // ldp q2, q3, [sp, #112] 0xe0, 0x87, 0x42, 0xad, // ldp q0, q1, [sp, #80] 0xe6, 0x1f, 0x44, 0xa9, // ldp x6, x7, [sp, #64] 0xe4, 0x17, 0x43, 0xa9, // ldp x4, x5, [sp, #48] 0xe2, 0x0f, 0x42, 0xa9, // ldp x2, x3, [sp, #32] 0xe0, 0x07, 0x41, 0xa9, // ldp x0, x1, [sp, #16] 0xfd, 0x7b, 0xcd, 0xa8, // ldp x29, x30, [sp], #208 0x00, 0x02, 0x1f, 0xd6, // br x16 }; // A chunk for the delay import thunk. -class ThunkChunkX64 : public Chunk { +class ThunkChunkX64 : public NonSectionChunk { public: - ThunkChunkX64(Defined *I, Chunk *D, Defined *H) - : Imp(I), Desc(D), Helper(H) {} + ThunkChunkX64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} - size_t getSize() const override { return sizeof(ThunkX64); } + size_t getSize() const override { return sizeof(thunkX64); } - void writeTo(uint8_t *Buf) const override { - memcpy(Buf + OutputSectionOff, ThunkX64, sizeof(ThunkX64)); - write32le(Buf + OutputSectionOff + 36, Imp->getRVA() - RVA - 40); - write32le(Buf + OutputSectionOff + 43, Desc->getRVA() - RVA - 47); - write32le(Buf + OutputSectionOff + 48, Helper->getRVA() - RVA - 52); + void writeTo(uint8_t *buf) const override { + memcpy(buf, thunkX64, sizeof(thunkX64)); + write32le(buf + 3, imp->getRVA() - rva - 7); + write32le(buf + 8, tailMerge->getRVA() - rva - 12); } - Defined *Imp = nullptr; - Chunk *Desc = nullptr; - Defined *Helper = nullptr; + Defined *imp = nullptr; + Chunk *tailMerge = nullptr; }; -class ThunkChunkX86 : public Chunk { +class TailMergeChunkX64 : public NonSectionChunk { public: - ThunkChunkX86(Defined *I, Chunk *D, Defined *H) - : Imp(I), Desc(D), Helper(H) {} + TailMergeChunkX64(Chunk *d, Defined *h) : desc(d), helper(h) {} - size_t getSize() const override { return sizeof(ThunkX86); } + size_t getSize() const override { return sizeof(tailMergeX64); } - void writeTo(uint8_t *Buf) const override { - memcpy(Buf + OutputSectionOff, ThunkX86, sizeof(ThunkX86)); - write32le(Buf + OutputSectionOff + 3, Imp->getRVA() + Config->ImageBase); - write32le(Buf + OutputSectionOff + 8, Desc->getRVA() + Config->ImageBase); - write32le(Buf + OutputSectionOff + 13, Helper->getRVA() - RVA - 17); + void writeTo(uint8_t *buf) const override { + memcpy(buf, tailMergeX64, sizeof(tailMergeX64)); + write32le(buf + 39, desc->getRVA() - rva - 43); + write32le(buf + 44, helper->getRVA() - rva - 48); } - void getBaserels(std::vector *Res) override { - Res->emplace_back(RVA + 3); - Res->emplace_back(RVA + 8); + Chunk *desc = nullptr; + Defined *helper = nullptr; +}; + +class ThunkChunkX86 : public NonSectionChunk { +public: + ThunkChunkX86(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} + + size_t getSize() const override { return sizeof(thunkX86); } + + void writeTo(uint8_t *buf) const override { + memcpy(buf, thunkX86, sizeof(thunkX86)); + write32le(buf + 1, imp->getRVA() + config->imageBase); + write32le(buf + 6, tailMerge->getRVA() - rva - 10); } - Defined *Imp = nullptr; - Chunk *Desc = nullptr; - Defined *Helper = nullptr; + void getBaserels(std::vector *res) override { + res->emplace_back(rva + 1); + } + + Defined *imp = nullptr; + Chunk *tailMerge = nullptr; }; -class ThunkChunkARM : public Chunk { +class TailMergeChunkX86 : public NonSectionChunk { public: - ThunkChunkARM(Defined *I, Chunk *D, Defined *H) - : Imp(I), Desc(D), Helper(H) {} + TailMergeChunkX86(Chunk *d, Defined *h) : desc(d), helper(h) {} - size_t getSize() const override { return sizeof(ThunkARM); } + size_t getSize() const override { return sizeof(tailMergeX86); } - 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 writeTo(uint8_t *buf) const override { + memcpy(buf, tailMergeX86, sizeof(tailMergeX86)); + write32le(buf + 4, desc->getRVA() + config->imageBase); + write32le(buf + 9, helper->getRVA() - rva - 13); } - void getBaserels(std::vector *Res) override { - Res->emplace_back(RVA + 0, IMAGE_REL_BASED_ARM_MOV32T); - Res->emplace_back(RVA + 22, IMAGE_REL_BASED_ARM_MOV32T); + void getBaserels(std::vector *res) override { + res->emplace_back(rva + 4); } - Defined *Imp = nullptr; - Chunk *Desc = nullptr; - Defined *Helper = nullptr; + Chunk *desc = nullptr; + Defined *helper = nullptr; }; -class ThunkChunkARM64 : public Chunk { +class ThunkChunkARM : public NonSectionChunk { public: - ThunkChunkARM64(Defined *I, Chunk *D, Defined *H) - : Imp(I), Desc(D), Helper(H) {} + ThunkChunkARM(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} - size_t getSize() const override { return sizeof(ThunkARM64); } + size_t getSize() const override { return sizeof(thunkARM); } - void writeTo(uint8_t *Buf) const override { - memcpy(Buf + OutputSectionOff, ThunkARM64, sizeof(ThunkARM64)); - applyArm64Addr(Buf + OutputSectionOff + 0, Imp->getRVA(), RVA + 0, 12); - applyArm64Imm(Buf + OutputSectionOff + 4, Imp->getRVA() & 0xfff, 0); - applyArm64Addr(Buf + OutputSectionOff + 52, Desc->getRVA(), RVA + 52, 12); - applyArm64Imm(Buf + OutputSectionOff + 56, Desc->getRVA() & 0xfff, 0); - applyArm64Branch26(Buf + OutputSectionOff + 60, - Helper->getRVA() - RVA - 60); + void writeTo(uint8_t *buf) const override { + memcpy(buf, thunkARM, sizeof(thunkARM)); + applyMOV32T(buf + 0, imp->getRVA() + config->imageBase); + applyBranch24T(buf + 8, tailMerge->getRVA() - rva - 12); } - Defined *Imp = nullptr; - Chunk *Desc = nullptr; - Defined *Helper = nullptr; + void getBaserels(std::vector *res) override { + res->emplace_back(rva + 0, IMAGE_REL_BASED_ARM_MOV32T); + } + + Defined *imp = nullptr; + Chunk *tailMerge = nullptr; }; +class TailMergeChunkARM : public NonSectionChunk { +public: + TailMergeChunkARM(Chunk *d, Defined *h) : desc(d), helper(h) {} + + size_t getSize() const override { return sizeof(tailMergeARM); } + + void writeTo(uint8_t *buf) const override { + memcpy(buf, tailMergeARM, sizeof(tailMergeARM)); + applyMOV32T(buf + 14, desc->getRVA() + config->imageBase); + applyBranch24T(buf + 22, helper->getRVA() - rva - 26); + } + + void getBaserels(std::vector *res) override { + res->emplace_back(rva + 14, IMAGE_REL_BASED_ARM_MOV32T); + } + + Chunk *desc = nullptr; + Defined *helper = nullptr; +}; + +class ThunkChunkARM64 : public NonSectionChunk { +public: + ThunkChunkARM64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} + + size_t getSize() const override { return sizeof(thunkARM64); } + + void writeTo(uint8_t *buf) const override { + memcpy(buf, thunkARM64, sizeof(thunkARM64)); + applyArm64Addr(buf + 0, imp->getRVA(), rva + 0, 12); + applyArm64Imm(buf + 4, imp->getRVA() & 0xfff, 0); + applyArm64Branch26(buf + 8, tailMerge->getRVA() - rva - 8); + } + + Defined *imp = nullptr; + Chunk *tailMerge = nullptr; +}; + +class TailMergeChunkARM64 : public NonSectionChunk { +public: + TailMergeChunkARM64(Chunk *d, Defined *h) : desc(d), helper(h) {} + + size_t getSize() const override { return sizeof(tailMergeARM64); } + + void writeTo(uint8_t *buf) const override { + memcpy(buf, tailMergeARM64, sizeof(tailMergeARM64)); + applyArm64Addr(buf + 44, desc->getRVA(), rva + 44, 12); + applyArm64Imm(buf + 48, desc->getRVA() & 0xfff, 0); + applyArm64Branch26(buf + 52, helper->getRVA() - rva - 52); + } + + Chunk *desc = nullptr; + Defined *helper = nullptr; +}; + // A chunk for the import descriptor table. -class DelayAddressChunk : public Chunk { +class DelayAddressChunk : public NonSectionChunk { public: - explicit DelayAddressChunk(Chunk *C) : Thunk(C) { - Alignment = Config->Wordsize; + explicit DelayAddressChunk(Chunk *c) : thunk(c) { + setAlignment(config->wordsize); } - size_t getSize() const override { return Config->Wordsize; } + size_t getSize() const override { return config->wordsize; } - void writeTo(uint8_t *Buf) const override { - if (Config->is64()) { - write64le(Buf + OutputSectionOff, Thunk->getRVA() + Config->ImageBase); + void writeTo(uint8_t *buf) const override { + if (config->is64()) { + write64le(buf, thunk->getRVA() + config->imageBase); } else { - uint32_t Bit = 0; + 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); + if (config->machine == ARMNT) + bit = 1; + write32le(buf, (thunk->getRVA() + config->imageBase) | bit); } } - void getBaserels(std::vector *Res) override { - Res->emplace_back(RVA); + void getBaserels(std::vector *res) override { + res->emplace_back(rva); } - Chunk *Thunk; + Chunk *thunk; }; // Export table // Read Microsoft PE/COFF spec 5.3 for details. // A chunk for the export descriptor table. -class ExportDirectoryChunk : public Chunk { +class ExportDirectoryChunk : public NonSectionChunk { public: - ExportDirectoryChunk(int I, int J, Chunk *D, Chunk *A, Chunk *N, Chunk *O) - : MaxOrdinal(I), NameTabSize(J), DLLName(D), AddressTab(A), NameTab(N), - OrdinalTab(O) {} + ExportDirectoryChunk(int i, int j, Chunk *d, Chunk *a, Chunk *n, Chunk *o) + : maxOrdinal(i), nameTabSize(j), dllName(d), addressTab(a), nameTab(n), + ordinalTab(o) {} size_t getSize() const override { return sizeof(export_directory_table_entry); } - void writeTo(uint8_t *Buf) const override { - auto *E = (export_directory_table_entry *)(Buf + OutputSectionOff); - E->NameRVA = DLLName->getRVA(); - E->OrdinalBase = 0; - E->AddressTableEntries = MaxOrdinal + 1; - E->NumberOfNamePointers = NameTabSize; - E->ExportAddressTableRVA = AddressTab->getRVA(); - E->NamePointerRVA = NameTab->getRVA(); - E->OrdinalTableRVA = OrdinalTab->getRVA(); + void writeTo(uint8_t *buf) const override { + memset(buf, 0, getSize()); + + auto *e = (export_directory_table_entry *)(buf); + e->NameRVA = dllName->getRVA(); + e->OrdinalBase = 0; + e->AddressTableEntries = maxOrdinal + 1; + e->NumberOfNamePointers = nameTabSize; + e->ExportAddressTableRVA = addressTab->getRVA(); + e->NamePointerRVA = nameTab->getRVA(); + e->OrdinalTableRVA = ordinalTab->getRVA(); } - uint16_t MaxOrdinal; - uint16_t NameTabSize; - Chunk *DLLName; - Chunk *AddressTab; - Chunk *NameTab; - Chunk *OrdinalTab; + uint16_t maxOrdinal; + uint16_t nameTabSize; + Chunk *dllName; + Chunk *addressTab; + Chunk *nameTab; + Chunk *ordinalTab; }; -class AddressTableChunk : public Chunk { +class AddressTableChunk : public NonSectionChunk { public: - explicit AddressTableChunk(size_t MaxOrdinal) : Size(MaxOrdinal + 1) {} - size_t getSize() const override { return Size * 4; } + explicit AddressTableChunk(size_t maxOrdinal) : size(maxOrdinal + 1) {} + size_t getSize() const override { return size * 4; } - void writeTo(uint8_t *Buf) const override { - memset(Buf + OutputSectionOff, 0, getSize()); + void writeTo(uint8_t *buf) const override { + memset(buf, 0, getSize()); - for (const Export &E : Config->Exports) { - uint8_t *P = Buf + OutputSectionOff + E.Ordinal * 4; - uint32_t Bit = 0; + for (const Export &e : config->exports) { + uint8_t *p = buf + e.ordinal * 4; + uint32_t bit = 0; // Pointer to thumb code must have the LSB set, so adjust it. - if (Config->Machine == ARMNT && !E.Data) - Bit = 1; - if (E.ForwardChunk) { - write32le(P, E.ForwardChunk->getRVA() | Bit); + if (config->machine == ARMNT && !e.data) + bit = 1; + if (e.forwardChunk) { + write32le(p, e.forwardChunk->getRVA() | bit); } else { - write32le(P, cast(E.Sym)->getRVA() | Bit); + write32le(p, cast(e.sym)->getRVA() | bit); } } } private: - size_t Size; + size_t size; }; -class NamePointersChunk : public Chunk { +class NamePointersChunk : public NonSectionChunk { public: - explicit NamePointersChunk(std::vector &V) : Chunks(V) {} - size_t getSize() const override { return Chunks.size() * 4; } + explicit NamePointersChunk(std::vector &v) : chunks(v) {} + size_t getSize() const override { return chunks.size() * 4; } - void writeTo(uint8_t *Buf) const override { - uint8_t *P = Buf + OutputSectionOff; - for (Chunk *C : Chunks) { - write32le(P, C->getRVA()); - P += 4; + void writeTo(uint8_t *buf) const override { + for (Chunk *c : chunks) { + write32le(buf, c->getRVA()); + buf += 4; } } private: - std::vector Chunks; + std::vector chunks; }; -class ExportOrdinalChunk : public Chunk { +class ExportOrdinalChunk : public NonSectionChunk { public: - explicit ExportOrdinalChunk(size_t I) : Size(I) {} - size_t getSize() const override { return Size * 2; } + explicit ExportOrdinalChunk(size_t i) : size(i) {} + size_t getSize() const override { return size * 2; } - void writeTo(uint8_t *Buf) const override { - uint8_t *P = Buf + OutputSectionOff; - for (Export &E : Config->Exports) { - if (E.Noname) + void writeTo(uint8_t *buf) const override { + for (Export &e : config->exports) { + if (e.noname) continue; - write16le(P, E.Ordinal); - P += 2; + write16le(buf, e.ordinal); + buf += 2; } } private: - size_t Size; + size_t size; }; } // anonymous namespace void IdataContents::create() { - std::vector> V = binImports(Imports); + std::vector> v = binImports(imports); // Create .idata contents for each DLL. - for (std::vector &Syms : V) { + for (std::vector &syms : v) { // Create lookup and address tables. If they have external names, - // we need to create HintName chunks to store the names. + // we need to create hintName chunks to store the names. // If they don't (if they are import-by-ordinals), we store only // ordinal values to the table. - size_t Base = Lookups.size(); - for (DefinedImportData *S : Syms) { - uint16_t Ord = S->getOrdinal(); - if (S->getExternalName().empty()) { - Lookups.push_back(make(Ord)); - Addresses.push_back(make(Ord)); + size_t base = lookups.size(); + for (DefinedImportData *s : syms) { + uint16_t ord = s->getOrdinal(); + if (s->getExternalName().empty()) { + lookups.push_back(make(ord)); + addresses.push_back(make(ord)); continue; } - auto *C = make(S->getExternalName(), Ord); - Lookups.push_back(make(C)); - Addresses.push_back(make(C)); - Hints.push_back(C); + auto *c = make(s->getExternalName(), ord); + lookups.push_back(make(c)); + addresses.push_back(make(c)); + hints.push_back(c); } // Terminate with null values. - Lookups.push_back(make(Config->Wordsize)); - Addresses.push_back(make(Config->Wordsize)); + lookups.push_back(make(config->wordsize)); + addresses.push_back(make(config->wordsize)); - for (int I = 0, E = Syms.size(); I < E; ++I) - Syms[I]->setLocation(Addresses[Base + I]); + for (int i = 0, e = syms.size(); i < e; ++i) + syms[i]->setLocation(addresses[base + i]); // Create the import table header. - DLLNames.push_back(make(Syms[0]->getDLLName())); - auto *Dir = make(DLLNames.back()); - Dir->LookupTab = Lookups[Base]; - Dir->AddressTab = Addresses[Base]; - Dirs.push_back(Dir); + dllNames.push_back(make(syms[0]->getDLLName())); + auto *dir = make(dllNames.back()); + dir->lookupTab = lookups[base]; + dir->addressTab = addresses[base]; + dirs.push_back(dir); } // Add null terminator. - Dirs.push_back(make(sizeof(ImportDirectoryTableEntry))); + dirs.push_back(make(sizeof(ImportDirectoryTableEntry))); } std::vector DelayLoadContents::getChunks() { - std::vector V; - V.insert(V.end(), Dirs.begin(), Dirs.end()); - V.insert(V.end(), Names.begin(), Names.end()); - V.insert(V.end(), HintNames.begin(), HintNames.end()); - V.insert(V.end(), DLLNames.begin(), DLLNames.end()); - return V; + std::vector v; + v.insert(v.end(), dirs.begin(), dirs.end()); + v.insert(v.end(), names.begin(), names.end()); + v.insert(v.end(), hintNames.begin(), hintNames.end()); + v.insert(v.end(), dllNames.begin(), dllNames.end()); + return v; } std::vector DelayLoadContents::getDataChunks() { - std::vector V; - V.insert(V.end(), ModuleHandles.begin(), ModuleHandles.end()); - V.insert(V.end(), Addresses.begin(), Addresses.end()); - return V; + std::vector v; + v.insert(v.end(), moduleHandles.begin(), moduleHandles.end()); + v.insert(v.end(), addresses.begin(), addresses.end()); + return v; } uint64_t DelayLoadContents::getDirSize() { - return Dirs.size() * sizeof(delay_import_directory_table_entry); + return dirs.size() * sizeof(delay_import_directory_table_entry); } -void DelayLoadContents::create(Defined *H) { - Helper = H; - std::vector> V = binImports(Imports); +void DelayLoadContents::create(Defined *h) { + helper = h; + std::vector> v = binImports(imports); // Create .didat contents for each DLL. - for (std::vector &Syms : V) { + for (std::vector &syms : v) { // Create the delay import table header. - DLLNames.push_back(make(Syms[0]->getDLLName())); - auto *Dir = make(DLLNames.back()); + dllNames.push_back(make(syms[0]->getDLLName())); + auto *dir = make(dllNames.back()); - size_t Base = Addresses.size(); - for (DefinedImportData *S : Syms) { - Chunk *T = newThunkChunk(S, Dir); - auto *A = make(T); - Addresses.push_back(A); - Thunks.push_back(T); - StringRef ExtName = S->getExternalName(); - if (ExtName.empty()) { - Names.push_back(make(S->getOrdinal())); + size_t base = addresses.size(); + Chunk *tm = newTailMergeChunk(dir); + for (DefinedImportData *s : syms) { + Chunk *t = newThunkChunk(s, tm); + auto *a = make(t); + addresses.push_back(a); + thunks.push_back(t); + StringRef extName = s->getExternalName(); + if (extName.empty()) { + names.push_back(make(s->getOrdinal())); } else { - auto *C = make(ExtName, 0); - Names.push_back(make(C)); - HintNames.push_back(C); + auto *c = make(extName, 0); + names.push_back(make(c)); + hintNames.push_back(c); } } + thunks.push_back(tm); // Terminate with null values. - Addresses.push_back(make(8)); - Names.push_back(make(8)); + addresses.push_back(make(8)); + names.push_back(make(8)); - for (int I = 0, E = Syms.size(); I < E; ++I) - Syms[I]->setLocation(Addresses[Base + I]); - auto *MH = make(8); - MH->Alignment = 8; - ModuleHandles.push_back(MH); + for (int i = 0, e = syms.size(); i < e; ++i) + syms[i]->setLocation(addresses[base + i]); + auto *mh = make(8); + mh->setAlignment(8); + moduleHandles.push_back(mh); // Fill the delay import table header fields. - Dir->ModuleHandle = MH; - Dir->AddressTab = Addresses[Base]; - Dir->NameTab = Names[Base]; - Dirs.push_back(Dir); + dir->moduleHandle = mh; + dir->addressTab = addresses[base]; + dir->nameTab = names[base]; + dirs.push_back(dir); } // Add null terminator. - Dirs.push_back(make(sizeof(delay_import_directory_table_entry))); + dirs.push_back(make(sizeof(delay_import_directory_table_entry))); } -Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *S, Chunk *Dir) { - switch (Config->Machine) { +Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) { + switch (config->machine) { case AMD64: - return make(S, Dir, Helper); + return make(dir, helper); case I386: - return make(S, Dir, Helper); + return make(dir, helper); case ARMNT: - return make(S, Dir, Helper); + return make(dir, helper); case ARM64: - return make(S, Dir, Helper); + return make(dir, helper); default: llvm_unreachable("unsupported machine type"); } } +Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s, + Chunk *tailMerge) { + switch (config->machine) { + case AMD64: + return make(s, tailMerge); + case I386: + return make(s, tailMerge); + case ARMNT: + return make(s, tailMerge); + case ARM64: + return make(s, tailMerge); + default: + llvm_unreachable("unsupported machine type"); + } +} + EdataContents::EdataContents() { - uint16_t MaxOrdinal = 0; - for (Export &E : Config->Exports) - MaxOrdinal = std::max(MaxOrdinal, E.Ordinal); + uint16_t maxOrdinal = 0; + for (Export &e : config->exports) + maxOrdinal = std::max(maxOrdinal, e.ordinal); - auto *DLLName = make(sys::path::filename(Config->OutputFile)); - auto *AddressTab = make(MaxOrdinal); - std::vector Names; - for (Export &E : Config->Exports) - if (!E.Noname) - Names.push_back(make(E.ExportName)); + auto *dllName = make(sys::path::filename(config->outputFile)); + auto *addressTab = make(maxOrdinal); + std::vector names; + for (Export &e : config->exports) + if (!e.noname) + names.push_back(make(e.exportName)); - std::vector Forwards; - for (Export &E : Config->Exports) { - if (E.ForwardTo.empty()) + std::vector forwards; + for (Export &e : config->exports) { + if (e.forwardTo.empty()) continue; - E.ForwardChunk = make(E.ForwardTo); - Forwards.push_back(E.ForwardChunk); + e.forwardChunk = make(e.forwardTo); + forwards.push_back(e.forwardChunk); } - auto *NameTab = make(Names); - auto *OrdinalTab = make(Names.size()); - auto *Dir = make(MaxOrdinal, Names.size(), DLLName, - AddressTab, NameTab, OrdinalTab); - Chunks.push_back(Dir); - Chunks.push_back(DLLName); - Chunks.push_back(AddressTab); - Chunks.push_back(NameTab); - Chunks.push_back(OrdinalTab); - Chunks.insert(Chunks.end(), Names.begin(), Names.end()); - Chunks.insert(Chunks.end(), Forwards.begin(), Forwards.end()); + auto *nameTab = make(names); + auto *ordinalTab = make(names.size()); + auto *dir = make(maxOrdinal, names.size(), dllName, + addressTab, nameTab, ordinalTab); + chunks.push_back(dir); + chunks.push_back(dllName); + chunks.push_back(addressTab); + chunks.push_back(nameTab); + chunks.push_back(ordinalTab); + chunks.insert(chunks.end(), names.begin(), names.end()); + chunks.insert(chunks.end(), forwards.begin(), forwards.end()); } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/DLL.h =================================================================== --- vendor/lld/dist/COFF/DLL.h (revision 351287) +++ vendor/lld/dist/COFF/DLL.h (revision 351288) @@ -1,82 +1,82 @@ //===- DLL.h ----------------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #ifndef LLD_COFF_DLL_H #define LLD_COFF_DLL_H #include "Chunks.h" #include "Symbols.h" namespace lld { namespace coff { // Windows-specific. // IdataContents creates all chunks for the DLL import table. // You are supposed to call add() to add symbols and then // call create() to populate the chunk vectors. class IdataContents { public: - void add(DefinedImportData *Sym) { Imports.push_back(Sym); } - bool empty() { return Imports.empty(); } + void add(DefinedImportData *sym) { imports.push_back(sym); } + bool empty() { return imports.empty(); } void create(); - std::vector Imports; - std::vector Dirs; - std::vector Lookups; - std::vector Addresses; - std::vector Hints; - std::vector DLLNames; + std::vector imports; + std::vector dirs; + std::vector lookups; + std::vector addresses; + std::vector hints; + std::vector dllNames; }; // Windows-specific. // DelayLoadContents creates all chunks for the delay-load DLL import table. class DelayLoadContents { public: - void add(DefinedImportData *Sym) { Imports.push_back(Sym); } - bool empty() { return Imports.empty(); } - void create(Defined *Helper); + void add(DefinedImportData *sym) { imports.push_back(sym); } + bool empty() { return imports.empty(); } + void create(Defined *helper); std::vector getChunks(); std::vector getDataChunks(); - ArrayRef getCodeChunks() { return Thunks; } + ArrayRef getCodeChunks() { return thunks; } - uint64_t getDirRVA() { return Dirs[0]->getRVA(); } + uint64_t getDirRVA() { return dirs[0]->getRVA(); } uint64_t getDirSize(); private: - Chunk *newThunkChunk(DefinedImportData *S, Chunk *Dir); + Chunk *newThunkChunk(DefinedImportData *s, Chunk *tailMerge); + Chunk *newTailMergeChunk(Chunk *dir); - Defined *Helper; - std::vector Imports; - std::vector Dirs; - std::vector ModuleHandles; - std::vector Addresses; - std::vector Names; - std::vector HintNames; - std::vector Thunks; - std::vector DLLNames; + Defined *helper; + std::vector imports; + std::vector dirs; + std::vector moduleHandles; + std::vector addresses; + std::vector names; + std::vector hintNames; + std::vector thunks; + std::vector dllNames; }; // Windows-specific. // EdataContents creates all chunks for the DLL export table. class EdataContents { public: EdataContents(); - std::vector Chunks; + std::vector chunks; - uint64_t getRVA() { return Chunks[0]->getRVA(); } + uint64_t getRVA() { return chunks[0]->getRVA(); } uint64_t getSize() { - return Chunks.back()->getRVA() + Chunks.back()->getSize() - getRVA(); + return chunks.back()->getRVA() + chunks.back()->getSize() - getRVA(); } }; } // namespace coff } // namespace lld #endif Index: vendor/lld/dist/COFF/DebugTypes.cpp =================================================================== --- vendor/lld/dist/COFF/DebugTypes.cpp (nonexistent) +++ vendor/lld/dist/COFF/DebugTypes.cpp (revision 351288) @@ -0,0 +1,268 @@ +//===- DebugTypes.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DebugTypes.h" +#include "Driver.h" +#include "InputFiles.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/Support/Path.h" + +using namespace lld; +using namespace lld::coff; +using namespace llvm; +using namespace llvm::codeview; + +namespace { +// The TypeServerSource class represents a PDB type server, a file referenced by +// OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ +// files, therefore there must be only once instance per OBJ lot. The file path +// is discovered from the dependent OBJ's debug type stream. The +// TypeServerSource object is then queued and loaded by the COFF Driver. The +// debug type stream for such PDB files will be merged first in the final PDB, +// before any dependent OBJ. +class TypeServerSource : public TpiSource { +public: + explicit TypeServerSource(MemoryBufferRef m, llvm::pdb::NativeSession *s) + : TpiSource(PDB, nullptr), session(s), mb(m) {} + + // Queue a PDB type server for loading in the COFF Driver + static void enqueue(const ObjFile *dependentFile, + const TypeServer2Record &ts); + + // Create an instance + static Expected getInstance(MemoryBufferRef m); + + // Fetch the PDB instance loaded for a corresponding dependent OBJ. + static Expected + findFromFile(const ObjFile *dependentFile); + + static std::map> + instances; + + // The interface to the PDB (if it was opened successfully) + std::unique_ptr session; + +private: + MemoryBufferRef mb; +}; + +// This class represents the debug type stream of an OBJ file that depends on a +// PDB type server (see TypeServerSource). +class UseTypeServerSource : public TpiSource { +public: + UseTypeServerSource(const ObjFile *f, const TypeServer2Record *ts) + : TpiSource(UsingPDB, f), typeServerDependency(*ts) {} + + // Information about the PDB type server dependency, that needs to be loaded + // in before merging this OBJ. + TypeServer2Record typeServerDependency; +}; + +// This class represents the debug type stream of a Microsoft precompiled +// headers OBJ (PCH OBJ). This OBJ kind needs to be merged first in the output +// PDB, before any other OBJs that depend on this. Note that only MSVC generate +// such files, clang does not. +class PrecompSource : public TpiSource { +public: + PrecompSource(const ObjFile *f) : TpiSource(PCH, f) {} +}; + +// This class represents the debug type stream of an OBJ file that depends on a +// Microsoft precompiled headers OBJ (see PrecompSource). +class UsePrecompSource : public TpiSource { +public: + UsePrecompSource(const ObjFile *f, const PrecompRecord *precomp) + : TpiSource(UsingPCH, f), precompDependency(*precomp) {} + + // Information about the Precomp OBJ dependency, that needs to be loaded in + // before merging this OBJ. + PrecompRecord precompDependency; +}; +} // namespace + +static std::vector> GC; + +TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) { + GC.push_back(std::unique_ptr(this)); +} + +TpiSource *lld::coff::makeTpiSource(const ObjFile *f) { + return new TpiSource(TpiSource::Regular, f); +} + +TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *f, + const TypeServer2Record *ts) { + TypeServerSource::enqueue(f, *ts); + return new UseTypeServerSource(f, ts); +} + +TpiSource *lld::coff::makePrecompSource(const ObjFile *f) { + return new PrecompSource(f); +} + +TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *f, + const PrecompRecord *precomp) { + return new UsePrecompSource(f, precomp); +} + +namespace lld { +namespace coff { +template <> +const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) { + assert(source->kind == TpiSource::UsingPCH); + return ((const UsePrecompSource *)source)->precompDependency; +} + +template <> +const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) { + assert(source->kind == TpiSource::UsingPDB); + return ((const UseTypeServerSource *)source)->typeServerDependency; +} +} // namespace coff +} // namespace lld + +std::map> + TypeServerSource::instances; + +// Make a PDB path assuming the PDB is in the same folder as the OBJ +static std::string getPdbBaseName(const ObjFile *file, StringRef tSPath) { + StringRef localPath = + !file->parentName.empty() ? file->parentName : file->getName(); + SmallString<128> path = sys::path::parent_path(localPath); + + // Currently, type server PDBs are only created by MSVC cl, which only runs + // on Windows, so we can assume type server paths are Windows style. + sys::path::append(path, sys::path::filename(tSPath, sys::path::Style::windows)); + return path.str(); +} + +// The casing of the PDB path stamped in the OBJ can differ from the actual path +// on disk. With this, we ensure to always use lowercase as a key for the +// PDBInputFile::Instances map, at least on Windows. +static std::string normalizePdbPath(StringRef path) { +#if defined(_WIN32) + return path.lower(); +#else // LINUX + return path; +#endif +} + +// If existing, return the actual PDB path on disk. +static Optional findPdbPath(StringRef pdbPath, + const ObjFile *dependentFile) { + // Ensure the file exists before anything else. In some cases, if the path + // points to a removable device, Driver::enqueuePath() would fail with an + // error (EAGAIN, "resource unavailable try again") which we want to skip + // silently. + if (llvm::sys::fs::exists(pdbPath)) + return normalizePdbPath(pdbPath); + std::string ret = getPdbBaseName(dependentFile, pdbPath); + if (llvm::sys::fs::exists(ret)) + return normalizePdbPath(ret); + return None; +} + +// Fetch the PDB instance that was already loaded by the COFF Driver. +Expected +TypeServerSource::findFromFile(const ObjFile *dependentFile) { + const TypeServer2Record &ts = + retrieveDependencyInfo(dependentFile->debugTypesObj); + + Optional p = findPdbPath(ts.Name, dependentFile); + if (!p) + return createFileError(ts.Name, errorCodeToError(std::error_code( + ENOENT, std::generic_category()))); + + auto it = TypeServerSource::instances.find(*p); + // The PDB file exists on disk, at this point we expect it to have been + // inserted in the map by TypeServerSource::loadPDB() + assert(it != TypeServerSource::instances.end()); + + std::pair &pdb = it->second; + + if (!pdb.second) + return createFileError( + *p, createStringError(inconvertibleErrorCode(), pdb.first.c_str())); + + pdb::PDBFile &pdbFile = (pdb.second)->session->getPDBFile(); + pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream()); + + // Just because a file with a matching name was found doesn't mean it can be + // used. The GUID must match between the PDB header and the OBJ + // TypeServer2 record. The 'Age' is used by MSVC incremental compilation. + if (info.getGuid() != ts.getGuid()) + return createFileError( + ts.Name, + make_error(pdb::pdb_error_code::signature_out_of_date)); + + return pdb.second; +} + +// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is +// moved here. +Expected +lld::coff::findTypeServerSource(const ObjFile *f) { + Expected ts = TypeServerSource::findFromFile(f); + if (!ts) + return ts.takeError(); + return ts.get()->session.get(); +} + +// Queue a PDB type server for loading in the COFF Driver +void TypeServerSource::enqueue(const ObjFile *dependentFile, + const TypeServer2Record &ts) { + // Start by finding where the PDB is located (either the record path or next + // to the OBJ file) + Optional p = findPdbPath(ts.Name, dependentFile); + if (!p) + return; + auto it = TypeServerSource::instances.emplace( + *p, std::pair{}); + if (!it.second) + return; // another OBJ already scheduled this PDB for load + + driver->enqueuePath(*p, false); +} + +// Create an instance of TypeServerSource or an error string if the PDB couldn't +// be loaded. The error message will be displayed later, when the referring OBJ +// will be merged in. NOTE - a PDB load failure is not a link error: some +// debug info will simply be missing from the final PDB - that is the default +// accepted behavior. +void lld::coff::loadTypeServerSource(llvm::MemoryBufferRef m) { + std::string path = normalizePdbPath(m.getBufferIdentifier()); + + Expected ts = TypeServerSource::getInstance(m); + if (!ts) + TypeServerSource::instances[path] = {toString(ts.takeError()), nullptr}; + else + TypeServerSource::instances[path] = {{}, *ts}; +} + +Expected TypeServerSource::getInstance(MemoryBufferRef m) { + std::unique_ptr iSession; + Error err = pdb::NativeSession::createFromPdb( + MemoryBuffer::getMemBuffer(m, false), iSession); + if (err) + return std::move(err); + + std::unique_ptr session( + static_cast(iSession.release())); + + pdb::PDBFile &pdbFile = session->getPDBFile(); + Expected info = pdbFile.getPDBInfoStream(); + // All PDB Files should have an Info stream. + if (!info) + return info.takeError(); + return new TypeServerSource(m, session.release()); +} Index: vendor/lld/dist/COFF/DebugTypes.h =================================================================== --- vendor/lld/dist/COFF/DebugTypes.h (nonexistent) +++ vendor/lld/dist/COFF/DebugTypes.h (revision 351288) @@ -0,0 +1,60 @@ +//===- DebugTypes.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COFF_DEBUGTYPES_H +#define LLD_COFF_DEBUGTYPES_H + +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" + +namespace llvm { +namespace codeview { +class PrecompRecord; +class TypeServer2Record; +} // namespace codeview +namespace pdb { +class NativeSession; +} +} // namespace llvm + +namespace lld { +namespace coff { + +class ObjFile; + +class TpiSource { +public: + enum TpiKind { Regular, PCH, UsingPCH, PDB, UsingPDB }; + + TpiSource(TpiKind k, const ObjFile *f); + virtual ~TpiSource() {} + + const TpiKind kind; + const ObjFile *file; +}; + +TpiSource *makeTpiSource(const ObjFile *f); +TpiSource *makeUseTypeServerSource(const ObjFile *f, + const llvm::codeview::TypeServer2Record *ts); +TpiSource *makePrecompSource(const ObjFile *f); +TpiSource *makeUsePrecompSource(const ObjFile *f, + const llvm::codeview::PrecompRecord *precomp); + +void loadTypeServerSource(llvm::MemoryBufferRef m); + +// Temporary interface to get the dependency +template const T &retrieveDependencyInfo(const TpiSource *source); + +// Temporary interface until we move PDBLinker::maybeMergeTypeServerPDB here +llvm::Expected +findTypeServerSource(const ObjFile *f); + +} // namespace coff +} // namespace lld + +#endif \ No newline at end of file Index: vendor/lld/dist/COFF/Driver.cpp =================================================================== --- vendor/lld/dist/COFF/Driver.cpp (revision 351287) +++ vendor/lld/dist/COFF/Driver.cpp (revision 351288) @@ -1,1681 +1,1891 @@ //===- Driver.cpp ---------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "Driver.h" #include "Config.h" +#include "DebugTypes.h" #include "ICF.h" #include "InputFiles.h" #include "MarkLive.h" #include "MinGW.h" #include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" #include "lld/Common/Args.h" #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/Filesystem.h" #include "lld/Common/Memory.h" +#include "lld/Common/Threads.h" #include "lld/Common/Timer.h" #include "lld/Common/Version.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Object/COFFModuleDefinition.h" +#include "llvm/Object/WindowsMachineFlag.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/Debug.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ToolDrivers/llvm-lib/LibDriver.h" #include #include #include using namespace llvm; using namespace llvm::object; using namespace llvm::COFF; using llvm::sys::Process; namespace lld { namespace coff { -static Timer InputFileTimer("Input File Reading", Timer::root()); +static Timer inputFileTimer("Input File Reading", Timer::root()); -Configuration *Config; -LinkerDriver *Driver; +Configuration *config; +LinkerDriver *driver; -bool link(ArrayRef Args, bool CanExitEarly, raw_ostream &Diag) { - errorHandler().LogName = args::getFilenameWithoutExe(Args[0]); - errorHandler().ErrorOS = &Diag; - errorHandler().ColorDiagnostics = Diag.has_colors(); - errorHandler().ErrorLimitExceededMsg = +bool link(ArrayRef args, bool canExitEarly, raw_ostream &diag) { + errorHandler().logName = args::getFilenameWithoutExe(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)"; - errorHandler().ExitEarly = CanExitEarly; - Config = make(); + errorHandler().exitEarly = canExitEarly; + config = make(); - Symtab = make(); + symtab = make(); - Driver = make(); - Driver->link(Args); + driver = make(); + driver->link(args); // Call exit() if we can to avoid calling destructors. - if (CanExitEarly) + if (canExitEarly) exitLld(errorCount() ? 1 : 0); freeArena(); - ObjFile::Instances.clear(); - ImportFile::Instances.clear(); - BitcodeFile::Instances.clear(); + ObjFile::instances.clear(); + ImportFile::instances.clear(); + BitcodeFile::instances.clear(); + memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances)); return !errorCount(); } +// Parse options of the form "old;new". +static std::pair getOldNewOptions(opt::InputArgList &args, + unsigned id) { + auto *arg = args.getLastArg(id); + if (!arg) + return {"", ""}; + + StringRef s = arg->getValue(); + std::pair ret = s.split(';'); + if (ret.second.empty()) + error(arg->getSpelling() + " expects 'old;new' format, but got " + s); + return ret; +} + // Drop directory components and replace extension with ".exe" or ".dll". -static std::string getOutputPath(StringRef Path) { - auto P = Path.find_last_of("\\/"); - StringRef S = (P == StringRef::npos) ? Path : Path.substr(P + 1); - const char* E = Config->DLL ? ".dll" : ".exe"; - return (S.substr(0, S.rfind('.')) + E).str(); +static std::string getOutputPath(StringRef path) { + auto p = path.find_last_of("\\/"); + StringRef s = (p == StringRef::npos) ? path : path.substr(p + 1); + const char* e = config->dll ? ".dll" : ".exe"; + return (s.substr(0, s.rfind('.')) + e).str(); } +// Returns true if S matches /crtend.?\.o$/. +static bool isCrtend(StringRef s) { + if (!s.endswith(".o")) + return false; + s = s.drop_back(2); + if (s.endswith("crtend")) + return true; + return !s.empty() && s.drop_back().endswith("crtend"); +} + // ErrorOr is not default constructible, so it cannot be used as the type // parameter of a future. // FIXME: We could open the file in createFutureForFile and avoid needing to // return an error here, but for the moment that would cost us a file descriptor // (a limited resource on Windows) for the duration that the future is pending. -typedef std::pair, std::error_code> MBErrPair; +using MBErrPair = std::pair, std::error_code>; // Create a std::future that opens and maps a file using the best strategy for // the host platform. -static std::future createFutureForFile(std::string Path) { +static std::future createFutureForFile(std::string path) { #if _WIN32 // On Windows, file I/O is relatively slow so it is best to do this // asynchronously. - auto Strategy = std::launch::async; + auto strategy = std::launch::async; #else - auto Strategy = std::launch::deferred; + auto strategy = std::launch::deferred; #endif - return std::async(Strategy, [=]() { - auto MBOrErr = MemoryBuffer::getFile(Path, + return std::async(strategy, [=]() { + auto mbOrErr = MemoryBuffer::getFile(path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false); - if (!MBOrErr) - return MBErrPair{nullptr, MBOrErr.getError()}; - return MBErrPair{std::move(*MBOrErr), std::error_code()}; + if (!mbOrErr) + return MBErrPair{nullptr, mbOrErr.getError()}; + return MBErrPair{std::move(*mbOrErr), std::error_code()}; }); } // Symbol names are mangled by prepending "_" on x86. -static StringRef mangle(StringRef Sym) { - assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN); - if (Config->Machine == I386) - return Saver.save("_" + Sym); - return Sym; +static StringRef mangle(StringRef sym) { + assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN); + if (config->machine == I386) + return saver.save("_" + sym); + return sym; } -static bool findUnderscoreMangle(StringRef Sym) { - StringRef Entry = Symtab->findMangle(mangle(Sym)); - return !Entry.empty() && !isa(Symtab->find(Entry)); +static bool findUnderscoreMangle(StringRef sym) { + Symbol *s = symtab->findMangle(mangle(sym)); + return s && !isa(s); } -MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr MB) { - MemoryBufferRef MBRef = *MB; - make>(std::move(MB)); // take ownership +MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr mb) { + MemoryBufferRef mbref = *mb; + make>(std::move(mb)); // take ownership - if (Driver->Tar) - Driver->Tar->append(relativeToRoot(MBRef.getBufferIdentifier()), - MBRef.getBuffer()); - return MBRef; + if (driver->tar) + driver->tar->append(relativeToRoot(mbref.getBufferIdentifier()), + mbref.getBuffer()); + return mbref; } -void LinkerDriver::addBuffer(std::unique_ptr MB, - bool WholeArchive) { - StringRef Filename = MB->getBufferIdentifier(); +void LinkerDriver::addBuffer(std::unique_ptr mb, + bool wholeArchive) { + StringRef filename = mb->getBufferIdentifier(); - MemoryBufferRef MBRef = takeBuffer(std::move(MB)); - FilePaths.push_back(Filename); + MemoryBufferRef mbref = takeBuffer(std::move(mb)); + filePaths.push_back(filename); // File type is detected by contents, not by file extension. - switch (identify_magic(MBRef.getBuffer())) { + switch (identify_magic(mbref.getBuffer())) { case file_magic::windows_resource: - Resources.push_back(MBRef); + resources.push_back(mbref); break; case file_magic::archive: - if (WholeArchive) { - std::unique_ptr File = - CHECK(Archive::create(MBRef), Filename + ": failed to parse archive"); + if (wholeArchive) { + std::unique_ptr file = + CHECK(Archive::create(mbref), filename + ": failed to parse archive"); - for (MemoryBufferRef M : getArchiveMembers(File.get())) - addArchiveBuffer(M, "", Filename); + for (MemoryBufferRef m : getArchiveMembers(file.get())) + addArchiveBuffer(m, "", filename, 0); return; } - Symtab->addFile(make(MBRef)); + symtab->addFile(make(mbref)); break; case file_magic::bitcode: - Symtab->addFile(make(MBRef)); + symtab->addFile(make(mbref, "", 0)); break; case file_magic::coff_object: case file_magic::coff_import_library: - Symtab->addFile(make(MBRef)); + symtab->addFile(make(mbref)); break; + case file_magic::pdb: + loadTypeServerSource(mbref); + break; case file_magic::coff_cl_gl_object: - error(Filename + ": is not a native COFF file. Recompile without /GL"); + error(filename + ": is not a native COFF file. Recompile without /GL"); break; case file_magic::pecoff_executable: - if (Filename.endswith_lower(".dll")) { - error(Filename + ": bad file type. Did you specify a DLL instead of an " + if (filename.endswith_lower(".dll")) { + error(filename + ": bad file type. Did you specify a DLL instead of an " "import library?"); break; } LLVM_FALLTHROUGH; default: - error(MBRef.getBufferIdentifier() + ": unknown file type"); + error(mbref.getBufferIdentifier() + ": unknown file type"); break; } } -void LinkerDriver::enqueuePath(StringRef Path, bool WholeArchive) { - auto Future = - std::make_shared>(createFutureForFile(Path)); - std::string PathStr = Path; +void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) { + auto future = + std::make_shared>(createFutureForFile(path)); + std::string pathStr = path; enqueueTask([=]() { - auto MBOrErr = Future->get(); - if (MBOrErr.second) - error("could not open " + PathStr + ": " + MBOrErr.second.message()); - else - Driver->addBuffer(std::move(MBOrErr.first), WholeArchive); + auto mbOrErr = future->get(); + if (mbOrErr.second) { + std::string msg = + "could not open '" + pathStr + "': " + mbOrErr.second.message(); + // Check if the filename is a typo for an option flag. OptTable thinks + // that all args that are not known options and that start with / are + // filenames, but e.g. `/nodefaultlibs` is more likely a typo for + // the option `/nodefaultlib` than a reference to a file in the root + // directory. + std::string nearest; + if (COFFOptTable().findNearest(pathStr, nearest) > 1) + error(msg); + else + error(msg + "; did you mean '" + nearest + "'"); + } else + driver->addBuffer(std::move(mbOrErr.first), wholeArchive); }); } -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(MB)); +void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName, + StringRef parentName, + uint64_t offsetInArchive) { + file_magic magic = identify_magic(mb.getBuffer()); + if (magic == file_magic::coff_import_library) { + InputFile *imp = make(mb); + imp->parentName = parentName; + symtab->addFile(imp); return; } - InputFile *Obj; - if (Magic == file_magic::coff_object) { - Obj = make(MB); - } else if (Magic == file_magic::bitcode) { - Obj = make(MB); + InputFile *obj; + if (magic == file_magic::coff_object) { + obj = make(mb); + } else if (magic == file_magic::bitcode) { + obj = make(mb, parentName, offsetInArchive); } else { - error("unknown file type: " + MB.getBufferIdentifier()); + error("unknown file type: " + mb.getBufferIdentifier()); return; } - Obj->ParentName = ParentName; - Symtab->addFile(Obj); - log("Loaded " + toString(Obj) + " for " + SymName); + obj->parentName = parentName; + symtab->addFile(obj); + log("Loaded " + toString(obj) + " for " + symName); } -void LinkerDriver::enqueueArchiveMember(const Archive::Child &C, - StringRef SymName, - StringRef ParentName) { - if (!C.getParent()->isThin()) { - MemoryBufferRef MB = CHECK( - C.getMemoryBufferRef(), - "could not get the buffer for the member defining symbol " + SymName); - enqueueTask([=]() { Driver->addArchiveBuffer(MB, SymName, ParentName); }); +void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, + StringRef symName, + StringRef parentName) { + + auto reportBufferError = [=](Error &&e, + StringRef childName) { + fatal("could not get the buffer for the member defining symbol " + + symName + ": " + parentName + "(" + childName + "): " + + toString(std::move(e))); + }; + + if (!c.getParent()->isThin()) { + uint64_t offsetInArchive = c.getChildOffset(); + Expected mbOrErr = c.getMemoryBufferRef(); + if (!mbOrErr) + reportBufferError(mbOrErr.takeError(), check(c.getFullName())); + MemoryBufferRef mb = mbOrErr.get(); + enqueueTask([=]() { + driver->addArchiveBuffer(mb, symName, parentName, offsetInArchive); + }); return; } - auto Future = std::make_shared>(createFutureForFile( - CHECK(C.getFullName(), - "could not get the filename for the member defining symbol " + - SymName))); + std::string childName = CHECK( + c.getFullName(), + "could not get the filename for the member defining symbol " + + symName); + auto future = std::make_shared>( + createFutureForFile(childName)); enqueueTask([=]() { - auto MBOrErr = Future->get(); - if (MBOrErr.second) - fatal("could not get the buffer for the member defining " + SymName + - ": " + MBOrErr.second.message()); - Driver->addArchiveBuffer(takeBuffer(std::move(MBOrErr.first)), SymName, - ParentName); + auto mbOrErr = future->get(); + if (mbOrErr.second) + reportBufferError(errorCodeToError(mbOrErr.second), childName); + driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), symName, + parentName, /* OffsetInArchive */ 0); }); } -static bool isDecorated(StringRef Sym) { - return Sym.startswith("@") || Sym.contains("@@") || Sym.startswith("?") || - (!Config->MinGW && Sym.contains('@')); +static bool isDecorated(StringRef sym) { + 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; +void LinkerDriver::parseDirectives(InputFile *file) { + StringRef s = file->getDirectives(); + if (s.empty()) + return; + + log("Directives: " + toString(file) + ": " + s); + + ArgParser parser; // .drectve is always tokenized using Windows shell rules. // /EXPORT: option can appear too many times, processing in fastpath. - opt::InputArgList Args; - std::vector Exports; - std::tie(Args, Exports) = Parser.parseDirectives(S); + opt::InputArgList args; + std::vector exports; + std::tie(args, exports) = parser.parseDirectives(s); - for (StringRef E : Exports) { + for (StringRef e : exports) { // If a common header file contains dllexported function // declarations, many object files may end up with having the // same /EXPORT options. In order to save cost of parsing them, // we dedup them first. - if (!DirectivesExports.insert(E).second) + if (!directivesExports.insert(e).second) continue; - Export Exp = parseExport(E); - if (Config->Machine == I386 && Config->MinGW) { - if (!isDecorated(Exp.Name)) - Exp.Name = Saver.save("_" + Exp.Name); - if (!Exp.ExtName.empty() && !isDecorated(Exp.ExtName)) - Exp.ExtName = Saver.save("_" + Exp.ExtName); + Export exp = parseExport(e); + if (config->machine == I386 && config->mingw) { + if (!isDecorated(exp.name)) + exp.name = saver.save("_" + exp.name); + if (!exp.extName.empty() && !isDecorated(exp.extName)) + exp.extName = saver.save("_" + exp.extName); } - Exp.Directives = true; - Config->Exports.push_back(Exp); + exp.directives = true; + config->exports.push_back(exp); } - for (auto *Arg : Args) { - switch (Arg->getOption().getUnaliasedOption().getID()) { + for (auto *arg : args) { + switch (arg->getOption().getID()) { case OPT_aligncomm: - parseAligncomm(Arg->getValue()); + parseAligncomm(arg->getValue()); break; case OPT_alternatename: - parseAlternateName(Arg->getValue()); + parseAlternateName(arg->getValue()); break; case OPT_defaultlib: - if (Optional Path = findLib(Arg->getValue())) - enqueuePath(*Path, false); + if (Optional path = findLib(arg->getValue())) + enqueuePath(*path, false); break; case OPT_entry: - Config->Entry = addUndefined(mangle(Arg->getValue())); + config->entry = addUndefined(mangle(arg->getValue())); break; case OPT_failifmismatch: - checkFailIfMismatch(Arg->getValue()); + checkFailIfMismatch(arg->getValue(), file); break; case OPT_incl: - addUndefined(Arg->getValue()); + addUndefined(arg->getValue()); break; case OPT_merge: - parseMerge(Arg->getValue()); + parseMerge(arg->getValue()); break; case OPT_nodefaultlib: - Config->NoDefaultLibs.insert(doFindLib(Arg->getValue())); + config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower()); break; case OPT_section: - parseSection(Arg->getValue()); + parseSection(arg->getValue()); break; case OPT_subsystem: - parseSubsystem(Arg->getValue(), &Config->Subsystem, - &Config->MajorOSVersion, &Config->MinorOSVersion); + parseSubsystem(arg->getValue(), &config->subsystem, + &config->majorOSVersion, &config->minorOSVersion); break; + // Only add flags here that link.exe accepts in + // `#pragma comment(linker, "/flag")`-generated sections. case OPT_editandcontinue: - case OPT_fastfail: case OPT_guardsym: - case OPT_natvis: case OPT_throwingnew: break; default: - error(Arg->getSpelling() + " is not allowed in .drectve"); + error(arg->getSpelling() + " is not allowed in .drectve"); } } } // Find file from search paths. You can omit ".obj", this function takes // care of that. Note that the returned path is not guaranteed to exist. -StringRef LinkerDriver::doFindFile(StringRef Filename) { - bool HasPathSep = (Filename.find_first_of("/\\") != StringRef::npos); - if (HasPathSep) - return Filename; - bool HasExt = Filename.contains('.'); - for (StringRef Dir : SearchPaths) { - SmallString<128> Path = Dir; - sys::path::append(Path, Filename); - if (sys::fs::exists(Path.str())) - return Saver.save(Path.str()); - if (!HasExt) { - Path.append(".obj"); - if (sys::fs::exists(Path.str())) - return Saver.save(Path.str()); +StringRef LinkerDriver::doFindFile(StringRef filename) { + bool hasPathSep = (filename.find_first_of("/\\") != StringRef::npos); + if (hasPathSep) + return filename; + bool hasExt = filename.contains('.'); + for (StringRef dir : searchPaths) { + SmallString<128> path = dir; + sys::path::append(path, filename); + if (sys::fs::exists(path.str())) + return saver.save(path.str()); + if (!hasExt) { + path.append(".obj"); + if (sys::fs::exists(path.str())) + return saver.save(path.str()); } } - return Filename; + return filename; } -static Optional getUniqueID(StringRef Path) { - sys::fs::UniqueID Ret; - if (sys::fs::getUniqueID(Path, Ret)) +static Optional getUniqueID(StringRef path) { + sys::fs::UniqueID ret; + if (sys::fs::getUniqueID(path, ret)) return None; - return Ret; + return ret; } // Resolves a file path. This never returns the same path // (in that case, it returns None). -Optional LinkerDriver::findFile(StringRef Filename) { - StringRef Path = doFindFile(Filename); +Optional LinkerDriver::findFile(StringRef filename) { + StringRef path = doFindFile(filename); - if (Optional ID = getUniqueID(Path)) { - bool Seen = !VisitedFiles.insert(*ID).second; - if (Seen) + if (Optional id = getUniqueID(path)) { + bool seen = !visitedFiles.insert(*id).second; + if (seen) return None; } - if (Path.endswith_lower(".lib")) - VisitedLibs.insert(sys::path::filename(Path)); - return Path; + if (path.endswith_lower(".lib")) + visitedLibs.insert(sys::path::filename(path)); + return path; } // MinGW specific. If an embedded directive specified to link to // foo.lib, but it isn't found, try libfoo.a instead. -StringRef LinkerDriver::doFindLibMinGW(StringRef Filename) { - if (Filename.contains('/') || Filename.contains('\\')) - return Filename; +StringRef LinkerDriver::doFindLibMinGW(StringRef filename) { + if (filename.contains('/') || filename.contains('\\')) + return filename; - SmallString<128> S = Filename; - sys::path::replace_extension(S, ".a"); - StringRef LibName = Saver.save("lib" + S.str()); - return doFindFile(LibName); + SmallString<128> s = filename; + sys::path::replace_extension(s, ".a"); + StringRef libName = saver.save("lib" + s.str()); + return doFindFile(libName); } // Find library file from search path. -StringRef LinkerDriver::doFindLib(StringRef Filename) { +StringRef LinkerDriver::doFindLib(StringRef filename) { // Add ".lib" to Filename if that has no file extension. - bool HasExt = Filename.contains('.'); - if (!HasExt) - Filename = Saver.save(Filename + ".lib"); - StringRef Ret = doFindFile(Filename); + bool hasExt = filename.contains('.'); + if (!hasExt) + filename = saver.save(filename + ".lib"); + StringRef ret = doFindFile(filename); // For MinGW, if the find above didn't turn up anything, try // looking for a MinGW formatted library name. - if (Config->MinGW && Ret == Filename) - return doFindLibMinGW(Filename); - return Ret; + if (config->mingw && ret == filename) + return doFindLibMinGW(filename); + return ret; } // Resolves a library path. /nodefaultlib options are taken into // consideration. This never returns the same path (in that case, // it returns None). -Optional LinkerDriver::findLib(StringRef Filename) { - if (Config->NoDefaultLibAll) +Optional LinkerDriver::findLib(StringRef filename) { + if (config->noDefaultLibAll) return None; - if (!VisitedLibs.insert(Filename.lower()).second) + if (!visitedLibs.insert(filename.lower()).second) return None; - StringRef Path = doFindLib(Filename); - if (Config->NoDefaultLibs.count(Path)) + StringRef path = doFindLib(filename); + if (config->noDefaultLibs.count(path.lower())) return None; - if (Optional ID = getUniqueID(Path)) - if (!VisitedFiles.insert(*ID).second) + if (Optional id = getUniqueID(path)) + if (!visitedFiles.insert(*id).second) return None; - return Path; + return path; } // Parses LIB environment which contains a list of search paths. void LinkerDriver::addLibSearchPaths() { - Optional EnvOpt = Process::GetEnv("LIB"); - if (!EnvOpt.hasValue()) + Optional envOpt = Process::GetEnv("LIB"); + if (!envOpt.hasValue()) return; - StringRef Env = Saver.save(*EnvOpt); - while (!Env.empty()) { - StringRef Path; - std::tie(Path, Env) = Env.split(';'); - SearchPaths.push_back(Path); + StringRef env = saver.save(*envOpt); + while (!env.empty()) { + StringRef path; + std::tie(path, env) = env.split(';'); + searchPaths.push_back(path); } } -Symbol *LinkerDriver::addUndefined(StringRef Name) { - Symbol *B = Symtab->addUndefined(Name); - if (!B->IsGCRoot) { - B->IsGCRoot = true; - Config->GCRoot.push_back(B); +Symbol *LinkerDriver::addUndefined(StringRef name) { + Symbol *b = symtab->addUndefined(name); + if (!b->isGCRoot) { + b->isGCRoot = true; + config->gcroot.push_back(b); } - return B; + return b; } +StringRef LinkerDriver::mangleMaybe(Symbol *s) { + // If the plain symbol name has already been resolved, do nothing. + Undefined *unmangled = dyn_cast(s); + if (!unmangled) + return ""; + + // Otherwise, see if a similar, mangled symbol exists in the symbol table. + Symbol *mangled = symtab->findMangle(unmangled->getName()); + if (!mangled) + return ""; + + // If we find a similar mangled symbol, make this an alias to it and return + // its name. + log(unmangled->getName() + " aliased to " + mangled->getName()); + unmangled->weakAlias = symtab->addUndefined(mangled->getName()); + return mangled->getName(); +} + // Windows specific -- find default entry point name. // // There are four different entry point functions for Windows executables, // each of which corresponds to a user-defined "main" function. This function // infers an entry point from a user-defined "main" function. StringRef LinkerDriver::findDefaultEntry() { - assert(Config->Subsystem != IMAGE_SUBSYSTEM_UNKNOWN && + assert(config->subsystem != IMAGE_SUBSYSTEM_UNKNOWN && "must handle /subsystem before calling this"); - if (Config->MinGW) - return mangle(Config->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI + if (config->mingw) + return mangle(config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI ? "WinMainCRTStartup" : "mainCRTStartup"); - if (Config->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { + if (config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { if (findUnderscoreMangle("wWinMain")) { if (!findUnderscoreMangle("WinMain")) return mangle("wWinMainCRTStartup"); warn("found both wWinMain and WinMain; using latter"); } return mangle("WinMainCRTStartup"); } if (findUnderscoreMangle("wmain")) { if (!findUnderscoreMangle("main")) return mangle("wmainCRTStartup"); warn("found both wmain and main; using latter"); } return mangle("mainCRTStartup"); } WindowsSubsystem LinkerDriver::inferSubsystem() { - if (Config->DLL) + if (config->dll) return IMAGE_SUBSYSTEM_WINDOWS_GUI; - if (Config->MinGW) + if (config->mingw) return IMAGE_SUBSYSTEM_WINDOWS_CUI; // Note that link.exe infers the subsystem from the presence of these // functions even if /entry: or /nodefaultlib are passed which causes them // to not be called. - bool HaveMain = findUnderscoreMangle("main"); - bool HaveWMain = findUnderscoreMangle("wmain"); - bool HaveWinMain = findUnderscoreMangle("WinMain"); - bool HaveWWinMain = findUnderscoreMangle("wWinMain"); - if (HaveMain || HaveWMain) { - if (HaveWinMain || HaveWWinMain) { - warn(std::string("found ") + (HaveMain ? "main" : "wmain") + " and " + - (HaveWinMain ? "WinMain" : "wWinMain") + + bool haveMain = findUnderscoreMangle("main"); + bool haveWMain = findUnderscoreMangle("wmain"); + bool haveWinMain = findUnderscoreMangle("WinMain"); + bool haveWWinMain = findUnderscoreMangle("wWinMain"); + if (haveMain || haveWMain) { + if (haveWinMain || haveWWinMain) { + warn(std::string("found ") + (haveMain ? "main" : "wmain") + " and " + + (haveWinMain ? "WinMain" : "wWinMain") + "; defaulting to /subsystem:console"); } return IMAGE_SUBSYSTEM_WINDOWS_CUI; } - if (HaveWinMain || HaveWWinMain) + if (haveWinMain || haveWWinMain) return IMAGE_SUBSYSTEM_WINDOWS_GUI; return IMAGE_SUBSYSTEM_UNKNOWN; } static uint64_t getDefaultImageBase() { - if (Config->is64()) - return Config->DLL ? 0x180000000 : 0x140000000; - return Config->DLL ? 0x10000000 : 0x400000; + if (config->is64()) + return config->dll ? 0x180000000 : 0x140000000; + return config->dll ? 0x10000000 : 0x400000; } -static std::string createResponseFile(const opt::InputArgList &Args, - ArrayRef FilePaths, - ArrayRef SearchPaths) { - SmallString<0> Data; - raw_svector_ostream OS(Data); +static std::string createResponseFile(const opt::InputArgList &args, + ArrayRef filePaths, + ArrayRef searchPaths) { + SmallString<0> data; + raw_svector_ostream os(data); - for (auto *Arg : Args) { - switch (Arg->getOption().getID()) { + for (auto *arg : args) { + switch (arg->getOption().getID()) { case OPT_linkrepro: 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; + case OPT_implib: + case OPT_pdb: + case OPT_out: + os << arg->getSpelling() << sys::path::filename(arg->getValue()) << "\n"; + break; default: - OS << toString(*Arg) << "\n"; + os << toString(*arg) << "\n"; } } - for (StringRef Path : SearchPaths) { - std::string RelPath = relativeToRoot(Path); - OS << "/libpath:" << quote(RelPath) << "\n"; + for (StringRef path : searchPaths) { + std::string relPath = relativeToRoot(path); + os << "/libpath:" << quote(relPath) << "\n"; } - for (StringRef Path : FilePaths) - OS << quote(relativeToRoot(Path)) << "\n"; + for (StringRef path : filePaths) + os << quote(relativeToRoot(path)) << "\n"; - return Data.str(); + return data.str(); } enum class DebugKind { Unknown, None, Full, FastLink, GHash, Dwarf, Symtab }; -static DebugKind parseDebugKind(const opt::InputArgList &Args) { - auto *A = Args.getLastArg(OPT_debug, OPT_debug_opt); - if (!A) +static DebugKind parseDebugKind(const opt::InputArgList &args) { + auto *a = args.getLastArg(OPT_debug, OPT_debug_opt); + if (!a) return DebugKind::None; - if (A->getNumValues() == 0) + if (a->getNumValues() == 0) return DebugKind::Full; - DebugKind Debug = StringSwitch(A->getValue()) + DebugKind debug = StringSwitch(a->getValue()) .CaseLower("none", DebugKind::None) .CaseLower("full", DebugKind::Full) .CaseLower("fastlink", DebugKind::FastLink) // LLD extensions .CaseLower("ghash", DebugKind::GHash) .CaseLower("dwarf", DebugKind::Dwarf) .CaseLower("symtab", DebugKind::Symtab) .Default(DebugKind::Unknown); - if (Debug == DebugKind::FastLink) { + if (debug == DebugKind::FastLink) { warn("/debug:fastlink unsupported; using /debug:full"); return DebugKind::Full; } - if (Debug == DebugKind::Unknown) { - error("/debug: unknown option: " + Twine(A->getValue())); + if (debug == DebugKind::Unknown) { + error("/debug: unknown option: " + Twine(a->getValue())); return DebugKind::None; } - return Debug; + return debug; } -static unsigned parseDebugTypes(const opt::InputArgList &Args) { - unsigned DebugTypes = static_cast(DebugType::None); +static unsigned parseDebugTypes(const opt::InputArgList &args) { + unsigned debugTypes = static_cast(DebugType::None); - if (auto *A = Args.getLastArg(OPT_debugtype)) { - SmallVector Types; - A->getSpelling().split(Types, ',', /*KeepEmpty=*/false); + if (auto *a = args.getLastArg(OPT_debugtype)) { + SmallVector types; + StringRef(a->getValue()) + .split(types, ',', /*MaxSplit=*/-1, /*KeepEmpty=*/false); - for (StringRef Type : Types) { - unsigned V = StringSwitch(Type.lower()) + for (StringRef type : types) { + unsigned v = StringSwitch(type.lower()) .Case("cv", static_cast(DebugType::CV)) .Case("pdata", static_cast(DebugType::PData)) .Case("fixup", static_cast(DebugType::Fixup)) .Default(0); - if (V == 0) { - warn("/debugtype: unknown option: " + Twine(A->getValue())); + if (v == 0) { + warn("/debugtype: unknown option '" + type + "'"); continue; } - DebugTypes |= V; + debugTypes |= v; } - return DebugTypes; + return debugTypes; } // Default debug types - DebugTypes = static_cast(DebugType::CV); - if (Args.hasArg(OPT_driver)) - DebugTypes |= static_cast(DebugType::PData); - if (Args.hasArg(OPT_profile)) - DebugTypes |= static_cast(DebugType::Fixup); + debugTypes = static_cast(DebugType::CV); + if (args.hasArg(OPT_driver)) + debugTypes |= static_cast(DebugType::PData); + if (args.hasArg(OPT_profile)) + debugTypes |= static_cast(DebugType::Fixup); - return DebugTypes; + return debugTypes; } -static std::string getMapFile(const opt::InputArgList &Args) { - auto *Arg = Args.getLastArg(OPT_lldmap, OPT_lldmap_file); - if (!Arg) +static std::string getMapFile(const opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_lldmap, OPT_lldmap_file); + if (!arg) return ""; - if (Arg->getOption().getID() == OPT_lldmap_file) - return Arg->getValue(); + if (arg->getOption().getID() == OPT_lldmap_file) + return arg->getValue(); - assert(Arg->getOption().getID() == OPT_lldmap); - StringRef OutFile = Config->OutputFile; - return (OutFile.substr(0, OutFile.rfind('.')) + ".map").str(); + assert(arg->getOption().getID() == OPT_lldmap); + StringRef outFile = config->outputFile; + return (outFile.substr(0, outFile.rfind('.')) + ".map").str(); } static std::string getImplibPath() { - if (!Config->Implib.empty()) - return Config->Implib; - SmallString<128> Out = StringRef(Config->OutputFile); - sys::path::replace_extension(Out, ".lib"); - return Out.str(); + if (!config->implib.empty()) + return config->implib; + SmallString<128> out = StringRef(config->outputFile); + sys::path::replace_extension(out, ".lib"); + return out.str(); } // // The import name is caculated as the following: // // | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY // -----+----------------+---------------------+------------------ // LINK | {value} | {value}.{.dll/.exe} | {output name} // LIB | {value} | {value}.dll | {output name}.dll // -static std::string getImportName(bool AsLib) { - SmallString<128> Out; +static std::string getImportName(bool asLib) { + SmallString<128> out; - if (Config->ImportName.empty()) { - Out.assign(sys::path::filename(Config->OutputFile)); - if (AsLib) - sys::path::replace_extension(Out, ".dll"); + if (config->importName.empty()) { + out.assign(sys::path::filename(config->outputFile)); + if (asLib) + sys::path::replace_extension(out, ".dll"); } else { - Out.assign(Config->ImportName); - if (!sys::path::has_extension(Out)) - sys::path::replace_extension(Out, - (Config->DLL || AsLib) ? ".dll" : ".exe"); + out.assign(config->importName); + if (!sys::path::has_extension(out)) + sys::path::replace_extension(out, + (config->dll || asLib) ? ".dll" : ".exe"); } - return Out.str(); + return out.str(); } -static void createImportLibrary(bool AsLib) { - std::vector Exports; - for (Export &E1 : Config->Exports) { - COFFShortExport E2; - E2.Name = E1.Name; - E2.SymbolName = E1.SymbolName; - E2.ExtName = E1.ExtName; - E2.Ordinal = E1.Ordinal; - E2.Noname = E1.Noname; - E2.Data = E1.Data; - E2.Private = E1.Private; - E2.Constant = E1.Constant; - Exports.push_back(E2); +static void createImportLibrary(bool asLib) { + std::vector exports; + for (Export &e1 : config->exports) { + COFFShortExport e2; + e2.Name = e1.name; + e2.SymbolName = e1.symbolName; + e2.ExtName = e1.extName; + e2.Ordinal = e1.ordinal; + e2.Noname = e1.noname; + e2.Data = e1.data; + e2.Private = e1.isPrivate; + e2.Constant = e1.constant; + exports.push_back(e2); } - auto HandleError = [](Error &&E) { - handleAllErrors(std::move(E), - [](ErrorInfoBase &EIB) { error(EIB.message()); }); + auto handleError = [](Error &&e) { + handleAllErrors(std::move(e), + [](ErrorInfoBase &eib) { error(eib.message()); }); }; - std::string LibName = getImportName(AsLib); - std::string Path = getImplibPath(); + std::string libName = getImportName(asLib); + std::string path = getImplibPath(); - if (!Config->Incremental) { - HandleError(writeImportLibrary(LibName, Path, Exports, Config->Machine, - Config->MinGW)); + if (!config->incremental) { + handleError(writeImportLibrary(libName, path, exports, config->machine, + config->mingw)); return; } // If the import library already exists, replace it only if the contents // have changed. - ErrorOr> OldBuf = MemoryBuffer::getFile( - Path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false); - if (!OldBuf) { - HandleError(writeImportLibrary(LibName, Path, Exports, Config->Machine, - Config->MinGW)); + ErrorOr> oldBuf = MemoryBuffer::getFile( + path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false); + if (!oldBuf) { + handleError(writeImportLibrary(libName, path, exports, config->machine, + config->mingw)); return; } - SmallString<128> TmpName; - if (std::error_code EC = - sys::fs::createUniqueFile(Path + ".tmp-%%%%%%%%.lib", TmpName)) - fatal("cannot create temporary file for import library " + Path + ": " + - EC.message()); + SmallString<128> tmpName; + if (std::error_code ec = + sys::fs::createUniqueFile(path + ".tmp-%%%%%%%%.lib", tmpName)) + fatal("cannot create temporary file for import library " + path + ": " + + ec.message()); - if (Error E = writeImportLibrary(LibName, TmpName, Exports, Config->Machine, - Config->MinGW)) { - HandleError(std::move(E)); + if (Error e = writeImportLibrary(libName, tmpName, exports, config->machine, + config->mingw)) { + handleError(std::move(e)); return; } - std::unique_ptr NewBuf = check(MemoryBuffer::getFile( - TmpName, /*FileSize*/ -1, /*RequiresNullTerminator*/ false)); - if ((*OldBuf)->getBuffer() != NewBuf->getBuffer()) { - OldBuf->reset(); - HandleError(errorCodeToError(sys::fs::rename(TmpName, Path))); + std::unique_ptr newBuf = check(MemoryBuffer::getFile( + tmpName, /*FileSize*/ -1, /*RequiresNullTerminator*/ false)); + if ((*oldBuf)->getBuffer() != newBuf->getBuffer()) { + oldBuf->reset(); + handleError(errorCodeToError(sys::fs::rename(tmpName, path))); } else { - sys::fs::remove(TmpName); + sys::fs::remove(tmpName); } } -static void parseModuleDefs(StringRef Path) { - std::unique_ptr MB = CHECK( - MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); - COFFModuleDefinition M = check(parseCOFFModuleDefinition( - MB->getMemBufferRef(), Config->Machine, Config->MinGW)); +static void parseModuleDefs(StringRef path) { + std::unique_ptr 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); - Config->ImportName = Saver.save(M.ImportName); - if (M.ImageBase) - Config->ImageBase = M.ImageBase; - if (M.StackReserve) - Config->StackReserve = M.StackReserve; - if (M.StackCommit) - Config->StackCommit = M.StackCommit; - if (M.HeapReserve) - Config->HeapReserve = M.HeapReserve; - if (M.HeapCommit) - Config->HeapCommit = M.HeapCommit; - if (M.MajorImageVersion) - Config->MajorImageVersion = M.MajorImageVersion; - if (M.MinorImageVersion) - Config->MinorImageVersion = M.MinorImageVersion; - if (M.MajorOSVersion) - Config->MajorOSVersion = M.MajorOSVersion; - if (M.MinorOSVersion) - Config->MinorOSVersion = M.MinorOSVersion; + if (config->outputFile.empty()) + config->outputFile = saver.save(m.OutputFile); + config->importName = saver.save(m.ImportName); + if (m.ImageBase) + config->imageBase = m.ImageBase; + if (m.StackReserve) + config->stackReserve = m.StackReserve; + if (m.StackCommit) + config->stackCommit = m.StackCommit; + if (m.HeapReserve) + config->heapReserve = m.HeapReserve; + if (m.HeapCommit) + config->heapCommit = m.HeapCommit; + if (m.MajorImageVersion) + config->majorImageVersion = m.MajorImageVersion; + if (m.MinorImageVersion) + config->minorImageVersion = m.MinorImageVersion; + if (m.MajorOSVersion) + config->majorOSVersion = m.MajorOSVersion; + if (m.MinorOSVersion) + config->minorOSVersion = m.MinorOSVersion; - for (COFFShortExport E1 : M.Exports) { - Export E2; + for (COFFShortExport e1 : m.Exports) { + Export e2; // In simple cases, only Name is set. Renamed exports are parsed // and set as "ExtName = Name". If Name has the form "OtherDll.Func", // it shouldn't be a normal exported function but a forward to another // DLL instead. This is supported by both MS and GNU linkers. - if (E1.ExtName != E1.Name && StringRef(E1.Name).contains('.')) { - E2.Name = Saver.save(E1.ExtName); - E2.ForwardTo = Saver.save(E1.Name); - Config->Exports.push_back(E2); + if (e1.ExtName != e1.Name && StringRef(e1.Name).contains('.')) { + e2.name = saver.save(e1.ExtName); + e2.forwardTo = saver.save(e1.Name); + config->exports.push_back(e2); continue; } - E2.Name = Saver.save(E1.Name); - E2.ExtName = Saver.save(E1.ExtName); - E2.Ordinal = E1.Ordinal; - E2.Noname = E1.Noname; - E2.Data = E1.Data; - E2.Private = E1.Private; - E2.Constant = E1.Constant; - Config->Exports.push_back(E2); + e2.name = saver.save(e1.Name); + e2.extName = saver.save(e1.ExtName); + e2.ordinal = e1.Ordinal; + e2.noname = e1.Noname; + e2.data = e1.Data; + e2.isPrivate = e1.Private; + e2.constant = e1.Constant; + config->exports.push_back(e2); } } -void LinkerDriver::enqueueTask(std::function Task) { - TaskQueue.push_back(std::move(Task)); +void LinkerDriver::enqueueTask(std::function task) { + taskQueue.push_back(std::move(task)); } bool LinkerDriver::run() { - ScopedTimer T(InputFileTimer); + ScopedTimer t(inputFileTimer); - bool DidWork = !TaskQueue.empty(); - while (!TaskQueue.empty()) { - TaskQueue.front()(); - TaskQueue.pop_front(); + bool didWork = !taskQueue.empty(); + while (!taskQueue.empty()) { + taskQueue.front()(); + taskQueue.pop_front(); } - return DidWork; + return didWork; } // Parse an /order file. If an option is given, the linker places // COMDAT sections in the same order as their names appear in the // given file. -static void parseOrderFile(StringRef Arg) { +static void parseOrderFile(StringRef arg) { // For some reason, the MSVC linker requires a filename to be // preceded by "@". - if (!Arg.startswith("@")) { + if (!arg.startswith("@")) { error("malformed /order option: '@' missing"); return; } // Get a list of all comdat sections for error checking. - DenseSet Set; - for (Chunk *C : Symtab->getChunks()) - if (auto *Sec = dyn_cast(C)) - if (Sec->Sym) - Set.insert(Sec->Sym->getName()); + DenseSet set; + for (Chunk *c : symtab->getChunks()) + if (auto *sec = dyn_cast(c)) + if (sec->sym) + set.insert(sec->sym->getName()); // Open a file. - StringRef Path = Arg.substr(1); - std::unique_ptr MB = CHECK( - MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); + StringRef path = arg.substr(1); + std::unique_ptr mb = CHECK( + MemoryBuffer::getFile(path, -1, false, true), "could not open " + path); // Parse a file. An order file contains one symbol per line. // All symbols that were not present in a given order file are // considered to have the lowest priority 0 and are placed at // end of an output section. - for (std::string S : args::getLines(MB->getMemBufferRef())) { - if (Config->Machine == I386 && !isDecorated(S)) - S = "_" + S; + for (std::string s : args::getLines(mb->getMemBufferRef())) { + if (config->machine == I386 && !isDecorated(s)) + s = "_" + s; - if (Set.count(S) == 0) { - if (Config->WarnMissingOrderSymbol) - warn("/order:" + Arg + ": missing symbol: " + S + " [LNK4037]"); + if (set.count(s) == 0) { + if (config->warnMissingOrderSymbol) + warn("/order:" + arg + ": missing symbol: " + s + " [LNK4037]"); } else - Config->Order[S] = INT_MIN + Config->Order.size(); + config->order[s] = INT_MIN + config->order.size(); } } -static void markAddrsig(Symbol *S) { - if (auto *D = dyn_cast_or_null(S)) - if (Chunk *C = D->getChunk()) - C->KeepUnique = true; +static void markAddrsig(Symbol *s) { + if (auto *d = dyn_cast_or_null(s)) + if (SectionChunk *c = dyn_cast_or_null(d->getChunk())) + c->keepUnique = true; } static void findKeepUniqueSections() { // Exported symbols could be address-significant in other executables or DSOs, // so we conservatively mark them as address-significant. - for (Export &R : Config->Exports) - markAddrsig(R.Sym); + for (Export &r : config->exports) + markAddrsig(r.sym); // Visit the address-significance table in each object file and mark each // referenced symbol as address-significant. - for (ObjFile *Obj : ObjFile::Instances) { - ArrayRef Syms = Obj->getSymbols(); - if (Obj->AddrsigSec) { - ArrayRef Contents; - Obj->getCOFFObj()->getSectionContents(Obj->AddrsigSec, Contents); - const uint8_t *Cur = Contents.begin(); - while (Cur != Contents.end()) { - unsigned Size; - const char *Err; - uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err); - if (Err) - fatal(toString(Obj) + ": could not decode addrsig section: " + Err); - if (SymIndex >= Syms.size()) - fatal(toString(Obj) + ": invalid symbol index in addrsig section"); - markAddrsig(Syms[SymIndex]); - Cur += Size; + for (ObjFile *obj : ObjFile::instances) { + ArrayRef syms = obj->getSymbols(); + if (obj->addrsigSec) { + ArrayRef contents; + cantFail( + obj->getCOFFObj()->getSectionContents(obj->addrsigSec, contents)); + const uint8_t *cur = contents.begin(); + while (cur != contents.end()) { + unsigned size; + const char *err; + uint64_t symIndex = decodeULEB128(cur, &size, contents.end(), &err); + if (err) + fatal(toString(obj) + ": could not decode addrsig section: " + err); + if (symIndex >= syms.size()) + fatal(toString(obj) + ": invalid symbol index in addrsig section"); + markAddrsig(syms[symIndex]); + cur += size; } } else { // If an object file does not have an address-significance table, // conservatively mark all of its symbols as address-significant. - for (Symbol *S : Syms) - markAddrsig(S); + for (Symbol *s : syms) + markAddrsig(s); } } } -// link.exe replaces each %foo% in AltPath with the contents of environment +// link.exe replaces each %foo% in altPath with the contents of environment // variable foo, and adds the two magic env vars _PDB (expands to the basename // of pdb's output path) and _EXT (expands to the extension of the output // binary). // lld only supports %_PDB% and %_EXT% and warns on references to all other env // vars. -static void parsePDBAltPath(StringRef AltPath) { - SmallString<128> Buf; - StringRef PDBBasename = - sys::path::filename(Config->PDBPath, sys::path::Style::windows); - StringRef BinaryExtension = - sys::path::extension(Config->OutputFile, sys::path::Style::windows); - if (!BinaryExtension.empty()) - BinaryExtension = BinaryExtension.substr(1); // %_EXT% does not include '.'. +static void parsePDBAltPath(StringRef altPath) { + SmallString<128> buf; + StringRef pdbBasename = + sys::path::filename(config->pdbPath, sys::path::Style::windows); + StringRef binaryExtension = + sys::path::extension(config->outputFile, sys::path::Style::windows); + if (!binaryExtension.empty()) + binaryExtension = binaryExtension.substr(1); // %_EXT% does not include '.'. // Invariant: - // +--------- Cursor ('a...' might be the empty string). - // | +----- FirstMark - // | | +- SecondMark + // +--------- cursor ('a...' might be the empty string). + // | +----- firstMark + // | | +- secondMark // v v v // a...%...%... - size_t Cursor = 0; - while (Cursor < AltPath.size()) { - size_t FirstMark, SecondMark; - if ((FirstMark = AltPath.find('%', Cursor)) == StringRef::npos || - (SecondMark = AltPath.find('%', FirstMark + 1)) == StringRef::npos) { + size_t cursor = 0; + while (cursor < altPath.size()) { + size_t firstMark, secondMark; + if ((firstMark = altPath.find('%', cursor)) == StringRef::npos || + (secondMark = altPath.find('%', firstMark + 1)) == StringRef::npos) { // Didn't find another full fragment, treat rest of string as literal. - Buf.append(AltPath.substr(Cursor)); + buf.append(altPath.substr(cursor)); break; } // Found a full fragment. Append text in front of first %, and interpret // text between first and second % as variable name. - Buf.append(AltPath.substr(Cursor, FirstMark - Cursor)); - StringRef Var = AltPath.substr(FirstMark, SecondMark - FirstMark + 1); - if (Var.equals_lower("%_pdb%")) - Buf.append(PDBBasename); - else if (Var.equals_lower("%_ext%")) - Buf.append(BinaryExtension); + buf.append(altPath.substr(cursor, firstMark - cursor)); + StringRef var = altPath.substr(firstMark, secondMark - firstMark + 1); + if (var.equals_lower("%_pdb%")) + buf.append(pdbBasename); + else if (var.equals_lower("%_ext%")) + buf.append(binaryExtension); else { warn("only %_PDB% and %_EXT% supported in /pdbaltpath:, keeping " + - Var + " as literal"); - Buf.append(Var); + var + " as literal"); + buf.append(var); } - Cursor = SecondMark + 1; + cursor = secondMark + 1; } - Config->PDBAltPath = Buf; + config->pdbAltPath = buf; } -void LinkerDriver::link(ArrayRef ArgsArr) { - // If the first command line argument is "/lib", link.exe acts like lib.exe. - // We call our own implementation of lib.exe that understands bitcode files. - if (ArgsArr.size() > 1 && StringRef(ArgsArr[1]).equals_lower("/lib")) { - if (llvm::libDriverMain(ArgsArr.slice(1)) != 0) - fatal("lib failed"); +/// Check that at most one resource obj file was used. +/// Call after ObjFile::Instances is complete. +static void diagnoseMultipleResourceObjFiles() { + // The .rsrc$01 section in a resource obj file contains a tree description + // of resources. Merging multiple resource obj files would require merging + // the trees instead of using usual linker section merging semantics. + // Since link.exe disallows linking more than one resource obj file with + // LNK4078, mirror that. The normal use of resource files is to give the + // linker many .res files, which are then converted to a single resource obj + // file internally, so this is not a big restriction in practice. + ObjFile *resourceObjFile = nullptr; + for (ObjFile *f : ObjFile::instances) { + if (!f->isResourceObjFile) + continue; + + if (!resourceObjFile) { + resourceObjFile = f; + continue; + } + + error(toString(f) + + ": more than one resource obj file not allowed, already got " + + toString(resourceObjFile)); + } +} + +// In MinGW, if no symbols are chosen to be exported, then all symbols are +// automatically exported by default. This behavior can be forced by the +// -export-all-symbols option, so that it happens even when exports are +// explicitly specified. The automatic behavior can be disabled using the +// -exclude-all-symbols option, so that lld-link behaves like link.exe rather +// than MinGW in the case that nothing is explicitly exported. +void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) { + if (!config->dll) return; + + if (!args.hasArg(OPT_export_all_symbols)) { + if (!config->exports.empty()) + return; + if (args.hasArg(OPT_exclude_all_symbols)) + return; } + AutoExporter exporter; + + for (auto *arg : args.filtered(OPT_wholearchive_file)) + if (Optional path = doFindFile(arg->getValue())) + exporter.addWholeArchive(*path); + + symtab->forEachSymbol([&](Symbol *s) { + auto *def = dyn_cast(s); + if (!exporter.shouldExport(def)) + return; + + Export e; + e.name = def->getName(); + e.sym = def; + if (Chunk *c = def->getChunk()) + if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) + e.data = true; + config->exports.push_back(e); + }); +} + +void LinkerDriver::link(ArrayRef argsArr) { // Needed for LTO. InitializeAllTargetInfos(); InitializeAllTargets(); InitializeAllTargetMCs(); InitializeAllAsmParsers(); InitializeAllAsmPrinters(); + // If the first command line argument is "/lib", link.exe acts like lib.exe. + // We call our own implementation of lib.exe that understands bitcode files. + if (argsArr.size() > 1 && StringRef(argsArr[1]).equals_lower("/lib")) { + if (llvm::libDriverMain(argsArr.slice(1)) != 0) + fatal("lib failed"); + return; + } + // Parse command line options. - ArgParser Parser; - opt::InputArgList Args = Parser.parseLINK(ArgsArr); + ArgParser parser; + opt::InputArgList args = parser.parseLINK(argsArr); // Parse and evaluate -mllvm options. - std::vector V; - V.push_back("lld-link (LLVM option parsing)"); - for (auto *Arg : Args.filtered(OPT_mllvm)) - V.push_back(Arg->getValue()); - cl::ParseCommandLineOptions(V.size(), V.data()); + std::vector v; + v.push_back("lld-link (LLVM option parsing)"); + for (auto *arg : args.filtered(OPT_mllvm)) + v.push_back(arg->getValue()); + cl::ParseCommandLineOptions(v.size(), v.data()); // Handle /errorlimit early, because error() depends on it. - if (auto *Arg = Args.getLastArg(OPT_errorlimit)) { - int N = 20; - StringRef S = Arg->getValue(); - if (S.getAsInteger(10, N)) - error(Arg->getSpelling() + " number expected, but got " + S); - errorHandler().ErrorLimit = N; + if (auto *arg = args.getLastArg(OPT_errorlimit)) { + int n = 20; + StringRef s = arg->getValue(); + if (s.getAsInteger(10, n)) + error(arg->getSpelling() + " number expected, but got " + s); + errorHandler().errorLimit = n; } // Handle /help - if (Args.hasArg(OPT_help)) { - printHelp(ArgsArr[0]); + if (args.hasArg(OPT_help)) { + printHelp(argsArr[0]); return; } - if (Args.hasArg(OPT_show_timing)) - Config->ShowTiming = true; + lld::threadsEnabled = args.hasFlag(OPT_threads, OPT_threads_no, true); - ScopedTimer T(Timer::root()); + if (args.hasArg(OPT_show_timing)) + config->showTiming = true; + + config->showSummary = args.hasArg(OPT_summary); + + ScopedTimer t(Timer::root()); // Handle --version, which is an lld extension. This option is a bit odd // because it doesn't start with "/", but we deliberately chose "--" to // avoid conflict with /version and for compatibility with clang-cl. - if (Args.hasArg(OPT_dash_dash_version)) { + 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); + 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"); + if (auto *arg = args.getLastArg(OPT_linkrepro)) { + SmallString<64> path = StringRef(arg->getValue()); + sys::path::append(path, "repro.tar"); - Expected> ErrOrWriter = - TarWriter::create(Path, "repro"); + Expected> errOrWriter = + TarWriter::create(path, "repro"); - if (ErrOrWriter) { - Tar = std::move(*ErrOrWriter); + if (errOrWriter) { + tar = std::move(*errOrWriter); } else { - error("/linkrepro: failed to open " + Path + ": " + - toString(ErrOrWriter.takeError())); + error("/linkrepro: failed to open " + path + ": " + + toString(errOrWriter.takeError())); } } - if (!Args.hasArg(OPT_INPUT)) { - if (Args.hasArg(OPT_deffile)) - Config->NoEntry = true; + if (!args.hasArg(OPT_INPUT)) { + if (args.hasArg(OPT_deffile)) + config->noEntry = true; else fatal("no input files"); } // Construct search path list. - SearchPaths.push_back(""); - for (auto *Arg : Args.filtered(OPT_libpath)) - SearchPaths.push_back(Arg->getValue()); + searchPaths.push_back(""); + for (auto *arg : args.filtered(OPT_libpath)) + searchPaths.push_back(arg->getValue()); addLibSearchPaths(); // Handle /ignore - for (auto *Arg : Args.filtered(OPT_ignore)) { - SmallVector Vec; - StringRef(Arg->getValue()).split(Vec, ','); - for (StringRef S : Vec) { - if (S == "4037") - Config->WarnMissingOrderSymbol = false; - else if (S == "4099") - Config->WarnDebugInfoUnusable = false; - else if (S == "4217") - Config->WarnLocallyDefinedImported = false; + for (auto *arg : args.filtered(OPT_ignore)) { + SmallVector vec; + StringRef(arg->getValue()).split(vec, ','); + for (StringRef s : vec) { + if (s == "4037") + config->warnMissingOrderSymbol = false; + else if (s == "4099") + config->warnDebugInfoUnusable = false; + else if (s == "4217") + config->warnLocallyDefinedImported = false; // Other warning numbers are ignored. } } // Handle /out - if (auto *Arg = Args.getLastArg(OPT_out)) - Config->OutputFile = Arg->getValue(); + if (auto *arg = args.getLastArg(OPT_out)) + config->outputFile = arg->getValue(); // Handle /verbose - if (Args.hasArg(OPT_verbose)) - Config->Verbose = true; - errorHandler().Verbose = Config->Verbose; + if (args.hasArg(OPT_verbose)) + config->verbose = true; + errorHandler().verbose = config->verbose; // Handle /force or /force:unresolved - if (Args.hasArg(OPT_force, OPT_force_unresolved)) - Config->ForceUnresolved = true; + if (args.hasArg(OPT_force, OPT_force_unresolved)) + config->forceUnresolved = true; // Handle /force or /force:multiple - if (Args.hasArg(OPT_force, OPT_force_multiple)) - Config->ForceMultiple = true; + if (args.hasArg(OPT_force, OPT_force_multiple)) + config->forceMultiple = true; + // Handle /force or /force:multipleres + if (args.hasArg(OPT_force, OPT_force_multipleres)) + config->forceMultipleRes = true; + // Handle /debug - DebugKind Debug = parseDebugKind(Args); - if (Debug == DebugKind::Full || Debug == DebugKind::Dwarf || - Debug == DebugKind::GHash) { - Config->Debug = true; - Config->Incremental = true; + DebugKind debug = parseDebugKind(args); + if (debug == DebugKind::Full || debug == DebugKind::Dwarf || + debug == DebugKind::GHash) { + config->debug = true; + config->incremental = true; } + // Handle /demangle + config->demangle = args.hasFlag(OPT_demangle, OPT_demangle_no); + // Handle /debugtype - Config->DebugTypes = parseDebugTypes(Args); + config->debugTypes = parseDebugTypes(args); // Handle /pdb - bool ShouldCreatePDB = - (Debug == DebugKind::Full || Debug == DebugKind::GHash); - if (ShouldCreatePDB) { - if (auto *Arg = Args.getLastArg(OPT_pdb)) - Config->PDBPath = Arg->getValue(); - if (auto *Arg = Args.getLastArg(OPT_pdbaltpath)) - Config->PDBAltPath = Arg->getValue(); - if (Args.hasArg(OPT_natvis)) - Config->NatvisFiles = Args.getAllArgValues(OPT_natvis); + bool shouldCreatePDB = + (debug == DebugKind::Full || debug == DebugKind::GHash); + if (shouldCreatePDB) { + if (auto *arg = args.getLastArg(OPT_pdb)) + config->pdbPath = arg->getValue(); + if (auto *arg = args.getLastArg(OPT_pdbaltpath)) + config->pdbAltPath = arg->getValue(); + if (args.hasArg(OPT_natvis)) + config->natvisFiles = args.getAllArgValues(OPT_natvis); - if (auto *Arg = Args.getLastArg(OPT_pdb_source_path)) - Config->PDBSourcePath = Arg->getValue(); + if (auto *arg = args.getLastArg(OPT_pdb_source_path)) + config->pdbSourcePath = arg->getValue(); } // Handle /noentry - if (Args.hasArg(OPT_noentry)) { - if (Args.hasArg(OPT_dll)) - Config->NoEntry = true; + if (args.hasArg(OPT_noentry)) { + if (args.hasArg(OPT_dll)) + config->noEntry = true; else error("/noentry must be specified with /dll"); } // Handle /dll - if (Args.hasArg(OPT_dll)) { - Config->DLL = true; - Config->ManifestID = 2; + if (args.hasArg(OPT_dll)) { + config->dll = true; + config->manifestID = 2; } // 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; + auto *dynamicBaseArg = args.getLastArg(OPT_dynamicbase, OPT_dynamicbase_no); + if (dynamicBaseArg && + dynamicBaseArg->getOption().getID() == OPT_dynamicbase_no) + config->dynamicBase = false; // MSDN claims "/FIXED:NO is the default setting for a DLL, and /FIXED is the // default setting for any other project type.", but link.exe defaults to // /FIXED:NO for exe outputs as well. Match behavior, not docs. - bool Fixed = Args.hasFlag(OPT_fixed, OPT_fixed_no, false); - if (Fixed) { - if (DynamicBaseArg && - DynamicBaseArg->getOption().getID() == OPT_dynamicbase) { + 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; - Config->DynamicBase = false; + config->relocatable = false; + config->dynamicBase = false; } } // Handle /appcontainer - Config->AppContainer = - Args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false); + config->appContainer = + args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false); // Handle /machine - if (auto *Arg = Args.getLastArg(OPT_machine)) - Config->Machine = getMachineType(Arg->getValue()); + if (auto *arg = args.getLastArg(OPT_machine)) { + config->machine = getMachineType(arg->getValue()); + if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) + fatal(Twine("unknown /machine argument: ") + arg->getValue()); + } // Handle /nodefaultlib: - for (auto *Arg : Args.filtered(OPT_nodefaultlib)) - Config->NoDefaultLibs.insert(doFindLib(Arg->getValue())); + for (auto *arg : args.filtered(OPT_nodefaultlib)) + config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower()); // Handle /nodefaultlib - if (Args.hasArg(OPT_nodefaultlib_all)) - Config->NoDefaultLibAll = true; + if (args.hasArg(OPT_nodefaultlib_all)) + config->noDefaultLibAll = true; // Handle /base - if (auto *Arg = Args.getLastArg(OPT_base)) - parseNumbers(Arg->getValue(), &Config->ImageBase); + if (auto *arg = args.getLastArg(OPT_base)) + parseNumbers(arg->getValue(), &config->imageBase); + // Handle /filealign + if (auto *arg = args.getLastArg(OPT_filealign)) { + parseNumbers(arg->getValue(), &config->fileAlign); + if (!isPowerOf2_64(config->fileAlign)) + error("/filealign: not a power of two: " + Twine(config->fileAlign)); + } + // Handle /stack - if (auto *Arg = Args.getLastArg(OPT_stack)) - parseNumbers(Arg->getValue(), &Config->StackReserve, &Config->StackCommit); + if (auto *arg = args.getLastArg(OPT_stack)) + parseNumbers(arg->getValue(), &config->stackReserve, &config->stackCommit); // Handle /guard:cf - if (auto *Arg = Args.getLastArg(OPT_guard)) - parseGuard(Arg->getValue()); + if (auto *arg = args.getLastArg(OPT_guard)) + parseGuard(arg->getValue()); // Handle /heap - if (auto *Arg = Args.getLastArg(OPT_heap)) - parseNumbers(Arg->getValue(), &Config->HeapReserve, &Config->HeapCommit); + if (auto *arg = args.getLastArg(OPT_heap)) + parseNumbers(arg->getValue(), &config->heapReserve, &config->heapCommit); // Handle /version - if (auto *Arg = Args.getLastArg(OPT_version)) - parseVersion(Arg->getValue(), &Config->MajorImageVersion, - &Config->MinorImageVersion); + if (auto *arg = args.getLastArg(OPT_version)) + parseVersion(arg->getValue(), &config->majorImageVersion, + &config->minorImageVersion); // Handle /subsystem - if (auto *Arg = Args.getLastArg(OPT_subsystem)) - parseSubsystem(Arg->getValue(), &Config->Subsystem, &Config->MajorOSVersion, - &Config->MinorOSVersion); + if (auto *arg = args.getLastArg(OPT_subsystem)) + parseSubsystem(arg->getValue(), &config->subsystem, &config->majorOSVersion, + &config->minorOSVersion); // Handle /timestamp - if (llvm::opt::Arg *Arg = Args.getLastArg(OPT_timestamp, OPT_repro)) { - if (Arg->getOption().getID() == OPT_repro) { - Config->Timestamp = 0; - Config->Repro = true; + if (llvm::opt::Arg *arg = args.getLastArg(OPT_timestamp, OPT_repro)) { + if (arg->getOption().getID() == OPT_repro) { + config->timestamp = 0; + config->repro = true; } else { - Config->Repro = false; - StringRef Value(Arg->getValue()); - if (Value.getAsInteger(0, Config->Timestamp)) - fatal(Twine("invalid timestamp: ") + Value + + config->repro = false; + StringRef value(arg->getValue()); + if (value.getAsInteger(0, config->timestamp)) + fatal(Twine("invalid timestamp: ") + value + ". Expected 32-bit integer"); } } else { - Config->Repro = false; - Config->Timestamp = time(nullptr); + config->repro = false; + config->timestamp = time(nullptr); } // Handle /alternatename - for (auto *Arg : Args.filtered(OPT_alternatename)) - parseAlternateName(Arg->getValue()); + for (auto *arg : args.filtered(OPT_alternatename)) + parseAlternateName(arg->getValue()); // Handle /include - for (auto *Arg : Args.filtered(OPT_incl)) - addUndefined(Arg->getValue()); + for (auto *arg : args.filtered(OPT_incl)) + addUndefined(arg->getValue()); // Handle /implib - if (auto *Arg = Args.getLastArg(OPT_implib)) - Config->Implib = Arg->getValue(); + if (auto *arg = args.getLastArg(OPT_implib)) + config->implib = arg->getValue(); // Handle /opt. - bool DoGC = Debug == DebugKind::None || Args.hasArg(OPT_profile); - unsigned ICFLevel = - Args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on - unsigned TailMerge = 1; - for (auto *Arg : Args.filtered(OPT_opt)) { - std::string Str = StringRef(Arg->getValue()).lower(); - SmallVector Vec; - StringRef(Str).split(Vec, ','); - for (StringRef S : Vec) { - 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 == "lldtailmerge") { - TailMerge = 2; - } else if (S == "nolldtailmerge") { - TailMerge = 0; - } else if (S.startswith("lldlto=")) { - StringRef OptLevel = S.substr(7); - if (OptLevel.getAsInteger(10, Config->LTOO) || Config->LTOO > 3) - error("/opt:lldlto: invalid optimization level: " + OptLevel); - } else if (S.startswith("lldltojobs=")) { - StringRef Jobs = S.substr(11); - if (Jobs.getAsInteger(10, Config->ThinLTOJobs) || - Config->ThinLTOJobs == 0) - error("/opt:lldltojobs: invalid job count: " + Jobs); - } 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); - } else if (S != "lbr" && S != "nolbr") - error("/opt: unknown option: " + S); + bool doGC = debug == DebugKind::None || args.hasArg(OPT_profile); + unsigned icfLevel = + args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on + unsigned tailMerge = 1; + for (auto *arg : args.filtered(OPT_opt)) { + std::string str = StringRef(arg->getValue()).lower(); + SmallVector vec; + StringRef(str).split(vec, ','); + for (StringRef s : vec) { + 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 == "lldtailmerge") { + tailMerge = 2; + } else if (s == "nolldtailmerge") { + tailMerge = 0; + } else if (s.startswith("lldlto=")) { + StringRef optLevel = s.substr(7); + if (optLevel.getAsInteger(10, config->ltoo) || config->ltoo > 3) + error("/opt:lldlto: invalid optimization level: " + optLevel); + } else if (s.startswith("lldltojobs=")) { + StringRef jobs = s.substr(11); + if (jobs.getAsInteger(10, config->thinLTOJobs) || + config->thinLTOJobs == 0) + error("/opt:lldltojobs: invalid job count: " + jobs); + } 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); + } 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; - Config->TailMerge = (TailMerge == 1 && Config->DoICF) || TailMerge == 2; + if (icfLevel == 1 && !doGC) + icfLevel = 0; + config->doGC = doGC; + config->doICF = icfLevel > 0; + config->tailMerge = (tailMerge == 1 && config->doICF) || tailMerge == 2; // Handle /lldsavetemps - if (Args.hasArg(OPT_lldsavetemps)) - Config->SaveTemps = true; + if (args.hasArg(OPT_lldsavetemps)) + config->saveTemps = true; // Handle /kill-at - if (Args.hasArg(OPT_kill_at)) - Config->KillAt = true; + if (args.hasArg(OPT_kill_at)) + config->killAt = true; // Handle /lldltocache - if (auto *Arg = Args.getLastArg(OPT_lldltocache)) - Config->LTOCache = Arg->getValue(); + 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()); + 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()); + for (auto *arg : args.filtered(OPT_failifmismatch)) + checkFailIfMismatch(arg->getValue(), nullptr); // Handle /merge - for (auto *Arg : Args.filtered(OPT_merge)) - parseMerge(Arg->getValue()); + for (auto *arg : args.filtered(OPT_merge)) + parseMerge(arg->getValue()); // Add default section merging rules after user rules. User rules take // precedence, but we will emit a warning if there is a conflict. parseMerge(".idata=.rdata"); parseMerge(".didat=.rdata"); parseMerge(".edata=.rdata"); parseMerge(".xdata=.rdata"); parseMerge(".bss=.data"); - if (Config->MinGW) { + if (config->mingw) { parseMerge(".ctors=.rdata"); parseMerge(".dtors=.rdata"); parseMerge(".CRT=.rdata"); } // Handle /section - for (auto *Arg : Args.filtered(OPT_section)) - parseSection(Arg->getValue()); + for (auto *arg : args.filtered(OPT_section)) + parseSection(arg->getValue()); // Handle /aligncomm - for (auto *Arg : Args.filtered(OPT_aligncomm)) - parseAligncomm(Arg->getValue()); + 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)) { - Config->ManifestDependency = Arg->getValue(); - Config->Manifest = Configuration::SideBySide; + if (auto *arg = args.getLastArg(OPT_manifestdependency)) { + config->manifestDependency = arg->getValue(); + config->manifest = Configuration::SideBySide; } // Handle /manifest and /manifest: - if (auto *Arg = Args.getLastArg(OPT_manifest, OPT_manifest_colon)) { - if (Arg->getOption().getID() == OPT_manifest) - Config->Manifest = Configuration::SideBySide; + if (auto *arg = args.getLastArg(OPT_manifest, OPT_manifest_colon)) { + if (arg->getOption().getID() == OPT_manifest) + config->manifest = Configuration::SideBySide; else - parseManifest(Arg->getValue()); + parseManifest(arg->getValue()); } // Handle /manifestuac - if (auto *Arg = Args.getLastArg(OPT_manifestuac)) - parseManifestUAC(Arg->getValue()); + if (auto *arg = args.getLastArg(OPT_manifestuac)) + parseManifestUAC(arg->getValue()); // Handle /manifestfile - if (auto *Arg = Args.getLastArg(OPT_manifestfile)) - Config->ManifestFile = Arg->getValue(); + if (auto *arg = args.getLastArg(OPT_manifestfile)) + config->manifestFile = arg->getValue(); // Handle /manifestinput - for (auto *Arg : Args.filtered(OPT_manifestinput)) - Config->ManifestInput.push_back(Arg->getValue()); + for (auto *arg : args.filtered(OPT_manifestinput)) + config->manifestInput.push_back(arg->getValue()); - if (!Config->ManifestInput.empty() && - Config->Manifest != Configuration::Embed) { + if (!config->manifestInput.empty() && + config->manifest != Configuration::Embed) { fatal("/manifestinput: requires /manifest:embed"); } + config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files); + config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) || + args.hasArg(OPT_thinlto_index_only_arg); + config->thinLTOIndexOnlyArg = + args.getLastArgValue(OPT_thinlto_index_only_arg); + config->thinLTOPrefixReplace = + getOldNewOptions(args, OPT_thinlto_prefix_replace); + config->thinLTOObjectSuffixReplace = + getOldNewOptions(args, OPT_thinlto_object_suffix_replace); // Handle miscellaneous boolean flags. - Config->AllowBind = Args.hasFlag(OPT_allowbind, OPT_allowbind_no, true); - Config->AllowIsolation = - Args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true); - Config->Incremental = - Args.hasFlag(OPT_incremental, OPT_incremental_no, - !Config->DoGC && !Config->DoICF && !Args.hasArg(OPT_order) && - !Args.hasArg(OPT_profile)); - Config->IntegrityCheck = - Args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false); - Config->NxCompat = Args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true); - Config->TerminalServerAware = - !Config->DLL && Args.hasFlag(OPT_tsaware, OPT_tsaware_no, true); - Config->DebugDwarf = Debug == DebugKind::Dwarf; - Config->DebugGHashes = Debug == DebugKind::GHash; - Config->DebugSymtab = Debug == DebugKind::Symtab; + config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true); + config->allowIsolation = + args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true); + config->incremental = + args.hasFlag(OPT_incremental, OPT_incremental_no, + !config->doGC && !config->doICF && !args.hasArg(OPT_order) && + !args.hasArg(OPT_profile)); + config->integrityCheck = + args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false); + config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true); + for (auto *arg : args.filtered(OPT_swaprun)) + parseSwaprun(arg->getValue()); + config->terminalServerAware = + !config->dll && args.hasFlag(OPT_tsaware, OPT_tsaware_no, true); + config->debugDwarf = debug == DebugKind::Dwarf; + config->debugGHashes = debug == DebugKind::GHash; + config->debugSymtab = debug == DebugKind::Symtab; - Config->MapFile = getMapFile(Args); + config->mapFile = getMapFile(args); - if (Config->Incremental && Args.hasArg(OPT_profile)) { + if (config->incremental && args.hasArg(OPT_profile)) { warn("ignoring '/incremental' due to '/profile' specification"); - Config->Incremental = false; + config->incremental = false; } - if (Config->Incremental && Args.hasArg(OPT_order)) { + if (config->incremental && args.hasArg(OPT_order)) { warn("ignoring '/incremental' due to '/order' specification"); - Config->Incremental = false; + config->incremental = false; } - if (Config->Incremental && Config->DoGC) { + if (config->incremental && config->doGC) { warn("ignoring '/incremental' because REF is enabled; use '/opt:noref' to " "disable"); - Config->Incremental = false; + config->incremental = false; } - if (Config->Incremental && Config->DoICF) { + if (config->incremental && config->doICF) { warn("ignoring '/incremental' because ICF is enabled; use '/opt:noicf' to " "disable"); - Config->Incremental = false; + config->incremental = false; } if (errorCount()) return; - std::set WholeArchives; - AutoExporter Exporter; - for (auto *Arg : Args.filtered(OPT_wholearchive_file)) { - if (Optional Path = doFindFile(Arg->getValue())) { - if (Optional ID = getUniqueID(*Path)) - WholeArchives.insert(*ID); - Exporter.addWholeArchive(*Path); - } - } + std::set wholeArchives; + for (auto *arg : args.filtered(OPT_wholearchive_file)) + if (Optional path = doFindFile(arg->getValue())) + if (Optional id = getUniqueID(*path)) + wholeArchives.insert(*id); // A predicate returning true if a given path is an argument for // /wholearchive:, or /wholearchive is enabled globally. // This function is a bit tricky because "foo.obj /wholearchive:././foo.obj" // needs to be handled as "/wholearchive:foo.obj foo.obj". - auto IsWholeArchive = [&](StringRef Path) -> bool { - if (Args.hasArg(OPT_wholearchive_flag)) + auto isWholeArchive = [&](StringRef path) -> bool { + if (args.hasArg(OPT_wholearchive_flag)) return true; - if (Optional ID = getUniqueID(Path)) - return WholeArchives.count(*ID); + if (Optional id = getUniqueID(path)) + return wholeArchives.count(*id); return false; }; // Create a list of input files. Files can be given as arguments // for /defaultlib option. - for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file)) - if (Optional Path = findFile(Arg->getValue())) - enqueuePath(*Path, IsWholeArchive(*Path)); + for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file)) + if (Optional path = findFile(arg->getValue())) + enqueuePath(*path, isWholeArchive(*path)); - for (auto *Arg : Args.filtered(OPT_defaultlib)) - if (Optional Path = findLib(Arg->getValue())) - enqueuePath(*Path, false); + for (auto *arg : args.filtered(OPT_defaultlib)) + if (Optional path = findLib(arg->getValue())) + enqueuePath(*path, false); // Windows specific -- Create a resource file containing a manifest file. - if (Config->Manifest == Configuration::Embed) + if (config->manifest == Configuration::Embed) addBuffer(createManifestRes(), false); // Read all input files given via the command line. run(); if (errorCount()) return; // We should have inferred a machine type by now from the input files, but if // not we assume x64. - if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) { + if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) { warn("/machine is not specified. x64 is assumed"); - Config->Machine = AMD64; + config->machine = AMD64; } - Config->Wordsize = Config->is64() ? 8 : 4; + config->wordsize = config->is64() ? 8 : 4; + // Handle /safeseh, x86 only, on by default, except for mingw. + if (config->machine == I386 && + args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw)) + config->safeSEH = true; + + // Handle /functionpadmin + for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt)) + parseFunctionPadMin(arg, config->machine); + // Input files can be Windows resource files (.res files). We use // WindowsResource to convert resource files to a regular COFF file, // then link the resulting file normally. - if (!Resources.empty()) - Symtab->addFile(make(convertResToCOFF(Resources))); + if (!resources.empty()) + symtab->addFile(make(convertResToCOFF(resources))); - if (Tar) - Tar->append("response.txt", - createResponseFile(Args, FilePaths, - ArrayRef(SearchPaths).slice(1))); + if (tar) + tar->append("response.txt", + createResponseFile(args, filePaths, + ArrayRef(searchPaths).slice(1))); // Handle /largeaddressaware - Config->LargeAddressAware = Args.hasFlag( - OPT_largeaddressaware, OPT_largeaddressaware_no, Config->is64()); + config->largeAddressAware = args.hasFlag( + OPT_largeaddressaware, OPT_largeaddressaware_no, config->is64()); // Handle /highentropyva - Config->HighEntropyVA = - Config->is64() && - Args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true); + config->highEntropyVA = + config->is64() && + args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true); - if (!Config->DynamicBase && - (Config->Machine == ARMNT || Config->Machine == ARM64)) + if (!config->dynamicBase && + (config->machine == ARMNT || config->machine == ARM64)) error("/dynamicbase:no is not compatible with " + - machineToStr(Config->Machine)); + machineToStr(config->machine)); // Handle /export - for (auto *Arg : Args.filtered(OPT_export)) { - Export E = parseExport(Arg->getValue()); - if (Config->Machine == I386) { - if (!isDecorated(E.Name)) - E.Name = Saver.save("_" + E.Name); - if (!E.ExtName.empty() && !isDecorated(E.ExtName)) - E.ExtName = Saver.save("_" + E.ExtName); + for (auto *arg : args.filtered(OPT_export)) { + Export e = parseExport(arg->getValue()); + if (config->machine == I386) { + if (!isDecorated(e.name)) + e.name = saver.save("_" + e.name); + if (!e.extName.empty() && !isDecorated(e.extName)) + e.extName = saver.save("_" + e.extName); } - Config->Exports.push_back(E); + config->exports.push_back(e); } // Handle /def - if (auto *Arg = Args.getLastArg(OPT_deffile)) { + if (auto *arg = args.getLastArg(OPT_deffile)) { // parseModuleDefs mutates Config object. - parseModuleDefs(Arg->getValue()); + parseModuleDefs(arg->getValue()); } // Handle generation of import library from a def file. - if (!Args.hasArg(OPT_INPUT)) { + if (!args.hasArg(OPT_INPUT)) { fixupExports(); - createImportLibrary(/*AsLib=*/true); + createImportLibrary(/*asLib=*/true); return; } // Windows specific -- if no /subsystem is given, we need to infer // that from entry point name. Must happen before /entry handling, // and after the early return when just writing an import library. - if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) { - Config->Subsystem = inferSubsystem(); - if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) + if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) { + config->subsystem = inferSubsystem(); + if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) fatal("subsystem must be defined"); } // Handle /entry and /dll - if (auto *Arg = Args.getLastArg(OPT_entry)) { - Config->Entry = addUndefined(mangle(Arg->getValue())); - } else if (!Config->Entry && !Config->NoEntry) { - if (Args.hasArg(OPT_dll)) { - StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12" + if (auto *arg = args.getLastArg(OPT_entry)) { + config->entry = addUndefined(mangle(arg->getValue())); + } else if (!config->entry && !config->noEntry) { + if (args.hasArg(OPT_dll)) { + StringRef s = (config->machine == I386) ? "__DllMainCRTStartup@12" : "_DllMainCRTStartup"; - Config->Entry = addUndefined(S); + 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()) + StringRef s = findDefaultEntry(); + if (s.empty()) fatal("entry point must be defined"); - Config->Entry = addUndefined(S); - log("Entry name inferred: " + S); + config->entry = addUndefined(s); + log("Entry name inferred: " + s); } } // Handle /delayload - for (auto *Arg : Args.filtered(OPT_delayload)) { - Config->DelayLoads.insert(StringRef(Arg->getValue()).lower()); - if (Config->Machine == I386) { - Config->DelayLoadHelper = addUndefined("___delayLoadHelper2@8"); + for (auto *arg : args.filtered(OPT_delayload)) { + config->delayLoads.insert(StringRef(arg->getValue()).lower()); + if (config->machine == I386) { + config->delayLoadHelper = addUndefined("___delayLoadHelper2@8"); } else { - Config->DelayLoadHelper = addUndefined("__delayLoadHelper2"); + config->delayLoadHelper = addUndefined("__delayLoadHelper2"); } } // Set default image name if neither /out or /def set it. - if (Config->OutputFile.empty()) { - Config->OutputFile = - getOutputPath((*Args.filtered(OPT_INPUT).begin())->getValue()); + if (config->outputFile.empty()) { + config->outputFile = + getOutputPath((*args.filtered(OPT_INPUT).begin())->getValue()); } - if (ShouldCreatePDB) { + // Fail early if an output file is not writable. + if (auto e = tryCreateFile(config->outputFile)) { + error("cannot open output file " + config->outputFile + ": " + e.message()); + return; + } + + if (shouldCreatePDB) { // Put the PDB next to the image if no /pdb flag was passed. - if (Config->PDBPath.empty()) { - Config->PDBPath = Config->OutputFile; - sys::path::replace_extension(Config->PDBPath, ".pdb"); + if (config->pdbPath.empty()) { + config->pdbPath = config->outputFile; + sys::path::replace_extension(config->pdbPath, ".pdb"); } // The embedded PDB path should be the absolute path to the PDB if no // /pdbaltpath flag was passed. - if (Config->PDBAltPath.empty()) { - Config->PDBAltPath = Config->PDBPath; + if (config->pdbAltPath.empty()) { + config->pdbAltPath = config->pdbPath; // It's important to make the path absolute and remove dots. This path // will eventually be written into the PE header, and certain Microsoft // tools won't work correctly if these assumptions are not held. - sys::fs::make_absolute(Config->PDBAltPath); - sys::path::remove_dots(Config->PDBAltPath); + sys::fs::make_absolute(config->pdbAltPath); + sys::path::remove_dots(config->pdbAltPath); } else { // Don't do this earlier, so that Config->OutputFile is ready. - parsePDBAltPath(Config->PDBAltPath); + parsePDBAltPath(config->pdbAltPath); } } // Set default image base if /base is not given. - if (Config->ImageBase == uint64_t(-1)) - Config->ImageBase = getDefaultImageBase(); + if (config->imageBase == uint64_t(-1)) + config->imageBase = getDefaultImageBase(); - Symtab->addSynthetic(mangle("__ImageBase"), nullptr); - if (Config->Machine == I386) { - Symtab->addAbsolute("___safe_se_handler_table", 0); - Symtab->addAbsolute("___safe_se_handler_count", 0); + 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(mangle("__guard_fids_count"), 0); - Symtab->addAbsolute(mangle("__guard_fids_table"), 0); - Symtab->addAbsolute(mangle("__guard_flags"), 0); - Symtab->addAbsolute(mangle("__guard_iat_count"), 0); - Symtab->addAbsolute(mangle("__guard_iat_table"), 0); - Symtab->addAbsolute(mangle("__guard_longjmp_count"), 0); - 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"), 0); + 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); + symtab->addAbsolute(mangle("__enclave_config"), 0); - if (Config->MinGW) { - Symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0); - Symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0); - Symtab->addAbsolute(mangle("__CTOR_LIST__"), 0); - Symtab->addAbsolute(mangle("__DTOR_LIST__"), 0); + if (config->mingw) { + symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0); + symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0); + symtab->addAbsolute(mangle("__CTOR_LIST__"), 0); + symtab->addAbsolute(mangle("__DTOR_LIST__"), 0); } // This code may add new undefined symbols to the link, which may enqueue more // symbol resolution tasks, so we need to continue executing tasks until we // converge. do { // Windows specific -- if entry point is not found, // search for its mangled names. - if (Config->Entry) - Symtab->mangleMaybe(Config->Entry); + if (config->entry) + mangleMaybe(config->entry); // Windows specific -- Make sure we resolve all dllexported symbols. - for (Export &E : Config->Exports) { - if (!E.ForwardTo.empty()) + for (Export &e : config->exports) { + if (!e.forwardTo.empty()) continue; - E.Sym = addUndefined(E.Name); - if (!E.Directives) - Symtab->mangleMaybe(E.Sym); + e.sym = addUndefined(e.name); + if (!e.directives) + e.symbolName = mangleMaybe(e.sym); } // Add weak aliases. Weak aliases is a mechanism to give remaining // undefined symbols final chance to be resolved successfully. - for (auto Pair : Config->AlternateNames) { - StringRef From = Pair.first; - StringRef To = Pair.second; - Symbol *Sym = Symtab->find(From); - if (!Sym) + for (auto pair : config->alternateNames) { + StringRef from = pair.first; + StringRef to = pair.second; + Symbol *sym = symtab->find(from); + if (!sym) continue; - if (auto *U = dyn_cast(Sym)) - if (!U->WeakAlias) - U->WeakAlias = Symtab->addUndefined(To); + if (auto *u = dyn_cast(sym)) + if (!u->weakAlias) + u->weakAlias = symtab->addUndefined(to); } // Windows specific -- if __load_config_used can be resolved, resolve it. - if (Symtab->findUnderscore("_load_config_used")) + if (symtab->findUnderscore("_load_config_used")) addUndefined(mangle("_load_config_used")); } while (run()); if (errorCount()) return; // Do LTO by compiling bitcode input files to a set of native COFF files then - // link those files. - Symtab->addCombinedLTOObjects(); + // link those files (unless -thinlto-index-only was given, in which case we + // resolve symbols and write indices, but don't generate native code or link). + symtab->addCombinedLTOObjects(); + + // If -thinlto-index-only is given, we should create only "index + // files" and not object files. Index file creation is already done + // in addCombinedLTOObject, so we are done if that's the case. + if (config->thinLTOIndexOnly) + return; + + // If we generated native object files from bitcode files, this resolves + // references to the symbols we use from them. run(); - if (Config->MinGW) { + if (args.hasArg(OPT_include_optional)) { + // Handle /includeoptional + for (auto *arg : args.filtered(OPT_include_optional)) + if (dyn_cast_or_null(symtab->find(arg->getValue()))) + addUndefined(arg->getValue()); + while (run()); + } + + if (config->mingw) { // Load any further object files that might be needed for doing automatic // imports. // // For cases with no automatically imported symbols, this iterates once // over the symbol table and doesn't do anything. // // For the normal case with a few automatically imported symbols, this // should only need to be run once, since each new object file imported // is an import library and wouldn't add any new undefined references, // but there's nothing stopping the __imp_ symbols from coming from a // normal object file as well (although that won't be used for the // actual autoimport later on). If this pass adds new undefined references, // we won't iterate further to resolve them. - Symtab->loadMinGWAutomaticImports(); + symtab->loadMinGWAutomaticImports(); run(); } // Make sure we have resolved all symbols. - Symtab->reportRemainingUndefines(); + symtab->reportRemainingUndefines(); if (errorCount()) return; - // Handle /safeseh. - if (Args.hasFlag(OPT_safeseh, OPT_safeseh_no, false)) { - for (ObjFile *File : ObjFile::Instances) - if (!File->hasSafeSEH()) - error("/safeseh: " + File->getName() + " is not compatible with SEH"); - if (errorCount()) - return; - } + if (config->mingw) { + // In MinGW, all symbols are automatically exported if no symbols + // are chosen to be exported. + maybeExportMinGWSymbols(args); - // 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))) { - Exporter.initSymbolExcludes(); - - Symtab->forEachSymbol([=](Symbol *S) { - auto *Def = dyn_cast(S); - if (!Exporter.shouldExport(Def)) - return; - Export E; - E.Name = Def->getName(); - E.Sym = Def; - if (Def->getChunk() && - !(Def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) - E.Data = true; - Config->Exports.push_back(E); - }); + // Make sure the crtend.o object is the last object file. This object + // file can contain terminating section chunks that need to be placed + // last. GNU ld processes files and static libraries explicitly in the + // order provided on the command line, while lld will pull in needed + // files from static libraries only after the last object file on the + // command line. + for (auto i = ObjFile::instances.begin(), e = ObjFile::instances.end(); + i != e; i++) { + ObjFile *file = *i; + if (isCrtend(file->getName())) { + ObjFile::instances.erase(i); + ObjFile::instances.push_back(file); + break; + } + } } // Windows specific -- when we are creating a .dll file, we also // need to create a .lib file. - if (!Config->Exports.empty() || Config->DLL) { + if (!config->exports.empty() || config->dll) { fixupExports(); - createImportLibrary(/*AsLib=*/false); + createImportLibrary(/*asLib=*/false); assignExportOrdinals(); } // Handle /output-def (MinGW specific). - if (auto *Arg = Args.getLastArg(OPT_output_def)) - writeDefFile(Arg->getValue()); + 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; + 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"); + Symbol *sym = symtab->find(name); + if (!sym) { + warn("/aligncomm symbol " + name + " not found"); continue; } // If the symbol isn't common, it must have been replaced with a regular // symbol, which will carry its own alignment. - auto *DC = dyn_cast(Sym); - if (!DC) + auto *dc = dyn_cast(sym); + if (!dc) continue; - CommonChunk *C = DC->getChunk(); - C->Alignment = std::max(C->Alignment, Alignment); + CommonChunk *c = dc->getChunk(); + c->setAlignment(std::max(c->getAlignment(), alignment)); } // Windows specific -- Create a side-by-side manifest file. - if (Config->Manifest == Configuration::SideBySide) + if (config->manifest == Configuration::SideBySide) createSideBySideManifest(); // Handle /order. We want to do this at this moment because we // need a complete list of comdat sections to warn on nonexistent // functions. - if (auto *Arg = Args.getLastArg(OPT_order)) - parseOrderFile(Arg->getValue()); + if (auto *arg = args.getLastArg(OPT_order)) + parseOrderFile(arg->getValue()); // Identify unreferenced COMDAT sections. - if (Config->DoGC) - markLive(Symtab->getChunks()); + if (config->doGC) + markLive(symtab->getChunks()); + // Needs to happen after the last call to addFile(). + diagnoseMultipleResourceObjFiles(); + // Identify identical COMDAT sections to merge them. - if (Config->DoICF) { + if (config->doICF) { findKeepUniqueSections(); - doICF(Symtab->getChunks()); + doICF(symtab->getChunks()); } // Write the result. writeResult(); // Stop early so we can print the results. Timer::root().stop(); - if (Config->ShowTiming) + if (config->showTiming) Timer::root().print(); } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/Driver.h =================================================================== --- vendor/lld/dist/COFF/Driver.h (revision 351287) +++ vendor/lld/dist/COFF/Driver.h (revision 351288) @@ -1,197 +1,202 @@ //===- Driver.h -------------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #ifndef LLD_COFF_DRIVER_H #define LLD_COFF_DRIVER_H #include "Config.h" #include "SymbolTable.h" #include "lld/Common/LLVM.h" #include "lld/Common/Reproduce.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/TarWriter.h" #include #include #include namespace lld { namespace coff { class LinkerDriver; -extern LinkerDriver *Driver; +extern LinkerDriver *driver; using llvm::COFF::MachineTypes; using llvm::COFF::WindowsSubsystem; using llvm::Optional; class COFFOptTable : public llvm::opt::OptTable { public: COFFOptTable(); }; class ArgParser { public: // Concatenate LINK environment variable and given arguments and parse them. - llvm::opt::InputArgList parseLINK(std::vector Args); + llvm::opt::InputArgList parseLINK(std::vector args); // Tokenizes a given string and then parses as command line options. - llvm::opt::InputArgList parse(StringRef S) { return parse(tokenize(S)); } + llvm::opt::InputArgList parse(StringRef s) { return parse(tokenize(s)); } // Tokenizes a given string and then parses as command line options in // .drectve section. /EXPORT options are returned in second element // to be processed in fastpath. std::pair> - parseDirectives(StringRef S); + parseDirectives(StringRef s); private: // Parses command line options. - llvm::opt::InputArgList parse(llvm::ArrayRef Args); + llvm::opt::InputArgList parse(llvm::ArrayRef args); - std::vector tokenize(StringRef S); + std::vector tokenize(StringRef s); - COFFOptTable Table; + COFFOptTable table; }; class LinkerDriver { public: - void link(llvm::ArrayRef Args); + void link(llvm::ArrayRef args); // Used by the resolver to parse .drectve section contents. - void parseDirectives(StringRef S); + void parseDirectives(InputFile *file); // Used by ArchiveFile to enqueue members. - void enqueueArchiveMember(const Archive::Child &C, StringRef SymName, - StringRef ParentName); + void enqueueArchiveMember(const Archive::Child &c, StringRef symName, + StringRef parentName); - MemoryBufferRef takeBuffer(std::unique_ptr MB); + MemoryBufferRef takeBuffer(std::unique_ptr mb); + void enqueuePath(StringRef path, bool wholeArchive); + private: - std::unique_ptr Tar; // for /linkrepro + std::unique_ptr tar; // for /linkrepro // Opens a file. Path has to be resolved already. - MemoryBufferRef openFile(StringRef Path); + MemoryBufferRef openFile(StringRef path); // Searches a file from search paths. - Optional findFile(StringRef Filename); - Optional findLib(StringRef Filename); - StringRef doFindFile(StringRef Filename); - StringRef doFindLib(StringRef Filename); - StringRef doFindLibMinGW(StringRef Filename); + Optional findFile(StringRef filename); + Optional findLib(StringRef filename); + StringRef doFindFile(StringRef filename); + StringRef doFindLib(StringRef filename); + StringRef doFindLibMinGW(StringRef filename); // Parses LIB environment which contains a list of search paths. void addLibSearchPaths(); // Library search path. The first element is always "" (current directory). - std::vector SearchPaths; + std::vector searchPaths; + void maybeExportMinGWSymbols(const llvm::opt::InputArgList &args); + // We don't want to add the same file more than once. // Files are uniquified by their filesystem and file number. - std::set VisitedFiles; + std::set visitedFiles; - std::set VisitedLibs; + std::set visitedLibs; - Symbol *addUndefined(StringRef Sym); + Symbol *addUndefined(StringRef sym); + StringRef mangleMaybe(Symbol *s); + // Windows specific -- "main" is not the only main function in Windows. // You can choose one from these four -- {w,}{WinMain,main}. // There are four different entry point functions for them, // {w,}{WinMain,main}CRTStartup, respectively. The linker needs to // choose the right one depending on which "main" function is defined. // This function looks up the symbol table and resolve corresponding // entry point name. StringRef findDefaultEntry(); WindowsSubsystem inferSubsystem(); - void addBuffer(std::unique_ptr MB, bool WholeArchive); - void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName, - StringRef ParentName); + void addBuffer(std::unique_ptr mb, bool wholeArchive); + void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName, + StringRef parentName, uint64_t offsetInArchive); - void enqueuePath(StringRef Path, bool WholeArchive); - - void enqueueTask(std::function Task); + void enqueueTask(std::function task); bool run(); - std::list> TaskQueue; - std::vector FilePaths; - std::vector Resources; + std::list> taskQueue; + std::vector filePaths; + std::vector resources; - llvm::StringSet<> DirectivesExports; + llvm::StringSet<> directivesExports; }; // Functions below this line are defined in DriverUtils.cpp. -void printHelp(const char *Argv0); +void printHelp(const char *argv0); -// For /machine option. -MachineTypes getMachineType(StringRef Arg); -StringRef machineToStr(MachineTypes MT); - // Parses a string in the form of "[,]". -void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size = nullptr); +void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr); -void parseGuard(StringRef Arg); +void parseGuard(StringRef arg); // Parses a string in the form of "[.]". // Minor's default value is 0. -void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor); +void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor); // Parses a string in the form of "[,[.]]". -void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major, - uint32_t *Minor); +void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, + uint32_t *minor); void parseAlternateName(StringRef); void parseMerge(StringRef); void parseSection(StringRef); void parseAligncomm(StringRef); +// Parses a string in the form of "[:]" +void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine); + // Parses a string in the form of "EMBED[,=]|NO". -void parseManifest(StringRef Arg); +void parseManifest(StringRef arg); // Parses a string in the form of "level=|uiAccess=" -void parseManifestUAC(StringRef Arg); +void parseManifestUAC(StringRef arg); +// Parses a string in the form of "cd|net[,(cd|net)]*" +void parseSwaprun(StringRef arg); + // Create a resource file containing a manifest XML. std::unique_ptr createManifestRes(); void createSideBySideManifest(); // Used for dllexported symbols. -Export parseExport(StringRef Arg); +Export parseExport(StringRef arg); void fixupExports(); void assignExportOrdinals(); // Parses a string in the form of "key=value" and check // if value matches previous values for the key. // This feature used in the directive section to reject // incompatible objects. -void checkFailIfMismatch(StringRef Arg); +void checkFailIfMismatch(StringRef arg, InputFile *source); // Convert Windows resource files (.res files) to a .obj file. -MemoryBufferRef convertResToCOFF(ArrayRef MBs); +MemoryBufferRef convertResToCOFF(ArrayRef mbs); -void runMSVCLinker(std::string Rsp, ArrayRef Objects); +void runMSVCLinker(std::string rsp, ArrayRef objects); // Create enum with OPT_xxx values for each option in Options.td enum { OPT_INVALID = 0, #define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID, #include "Options.inc" #undef OPTION }; } // namespace coff } // namespace lld #endif Index: vendor/lld/dist/COFF/DriverUtils.cpp =================================================================== --- vendor/lld/dist/COFF/DriverUtils.cpp (revision 351287) +++ vendor/lld/dist/COFF/DriverUtils.cpp (revision 351288) @@ -1,872 +1,897 @@ //===- DriverUtils.cpp ----------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// // // This file contains utility functions for the driver. Because there // are so many small functions, we created this separate file to make // Driver.cpp less cluttered. // //===----------------------------------------------------------------------===// #include "Config.h" #include "Driver.h" #include "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" #include "llvm/Object/COFF.h" #include "llvm/Object/WindowsResource.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileUtilities.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" #include "llvm/WindowsManifest/WindowsManifestMerger.h" #include using namespace llvm::COFF; using namespace llvm; using llvm::sys::Process; namespace lld { namespace coff { namespace { const uint16_t SUBLANG_ENGLISH_US = 0x0409; const uint16_t RT_MANIFEST = 24; class Executor { public: - explicit Executor(StringRef S) : Prog(Saver.save(S)) {} - void add(StringRef S) { Args.push_back(Saver.save(S)); } - void add(std::string &S) { Args.push_back(Saver.save(S)); } - void add(Twine S) { Args.push_back(Saver.save(S)); } - void add(const char *S) { Args.push_back(Saver.save(S)); } + explicit Executor(StringRef s) : prog(saver.save(s)) {} + void add(StringRef s) { args.push_back(saver.save(s)); } + void add(std::string &s) { args.push_back(saver.save(s)); } + void add(Twine s) { args.push_back(saver.save(s)); } + void add(const char *s) { args.push_back(saver.save(s)); } void run() { - ErrorOr ExeOrErr = sys::findProgramByName(Prog); - if (auto EC = ExeOrErr.getError()) - fatal("unable to find " + Prog + " in PATH: " + EC.message()); - StringRef Exe = Saver.save(*ExeOrErr); - Args.insert(Args.begin(), Exe); + ErrorOr exeOrErr = sys::findProgramByName(prog); + if (auto ec = exeOrErr.getError()) + fatal("unable to find " + prog + " in PATH: " + ec.message()); + StringRef exe = saver.save(*exeOrErr); + args.insert(args.begin(), exe); - if (sys::ExecuteAndWait(Args[0], Args) != 0) + if (sys::ExecuteAndWait(args[0], args) != 0) fatal("ExecuteAndWait failed: " + - llvm::join(Args.begin(), Args.end(), " ")); + llvm::join(args.begin(), args.end(), " ")); } private: - StringRef Prog; - std::vector Args; + StringRef prog; + std::vector args; }; } // anonymous namespace -// Returns /machine's value. -MachineTypes getMachineType(StringRef S) { - MachineTypes MT = StringSwitch(S.lower()) - .Cases("x64", "amd64", AMD64) - .Cases("x86", "i386", I386) - .Case("arm", ARMNT) - .Case("arm64", ARM64) - .Default(IMAGE_FILE_MACHINE_UNKNOWN); - if (MT != IMAGE_FILE_MACHINE_UNKNOWN) - return MT; - fatal("unknown /machine argument: " + S); -} - -StringRef machineToStr(MachineTypes MT) { - switch (MT) { - case ARMNT: - return "arm"; - case ARM64: - return "arm64"; - case AMD64: - return "x64"; - case I386: - return "x86"; - default: - llvm_unreachable("unknown machine type"); - } -} - // Parses a string in the form of "[,]". -void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size) { - StringRef S1, S2; - std::tie(S1, S2) = Arg.split(','); - if (S1.getAsInteger(0, *Addr)) - fatal("invalid number: " + S1); - if (Size && !S2.empty() && S2.getAsInteger(0, *Size)) - fatal("invalid number: " + S2); +void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) { + StringRef s1, s2; + std::tie(s1, s2) = arg.split(','); + if (s1.getAsInteger(0, *addr)) + fatal("invalid number: " + s1); + if (size && !s2.empty() && s2.getAsInteger(0, *size)) + fatal("invalid number: " + s2); } // Parses a string in the form of "[.]". // If second number is not present, Minor is set to 0. -void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor) { - StringRef S1, S2; - std::tie(S1, S2) = Arg.split('.'); - if (S1.getAsInteger(0, *Major)) - fatal("invalid number: " + S1); - *Minor = 0; - if (!S2.empty() && S2.getAsInteger(0, *Minor)) - fatal("invalid number: " + S2); +void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) { + StringRef s1, s2; + std::tie(s1, s2) = arg.split('.'); + if (s1.getAsInteger(0, *major)) + fatal("invalid number: " + s1); + *minor = 0; + if (!s2.empty() && s2.getAsInteger(0, *minor)) + fatal("invalid number: " + s2); } -void parseGuard(StringRef FullArg) { - SmallVector SplitArgs; - FullArg.split(SplitArgs, ","); - for (StringRef Arg : SplitArgs) { - if (Arg.equals_lower("no")) - Config->GuardCF = GuardCFLevel::Off; - else if (Arg.equals_lower("nolongjmp")) - Config->GuardCF = GuardCFLevel::NoLongJmp; - else if (Arg.equals_lower("cf") || Arg.equals_lower("longjmp")) - Config->GuardCF = GuardCFLevel::Full; +void parseGuard(StringRef fullArg) { + SmallVector splitArgs; + fullArg.split(splitArgs, ","); + for (StringRef arg : splitArgs) { + if (arg.equals_lower("no")) + config->guardCF = GuardCFLevel::Off; + else if (arg.equals_lower("nolongjmp")) + config->guardCF = GuardCFLevel::NoLongJmp; + else if (arg.equals_lower("cf") || arg.equals_lower("longjmp")) + config->guardCF = GuardCFLevel::Full; else - fatal("invalid argument to /guard: " + Arg); + fatal("invalid argument to /guard: " + arg); } } // Parses a string in the form of "[,[.]]". -void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major, - uint32_t *Minor) { - StringRef SysStr, Ver; - std::tie(SysStr, Ver) = Arg.split(','); - *Sys = StringSwitch(SysStr.lower()) +void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, + uint32_t *minor) { + StringRef sysStr, ver; + std::tie(sysStr, ver) = arg.split(','); + std::string sysStrLower = sysStr.lower(); + *sys = StringSwitch(sysStrLower) .Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION) .Case("console", IMAGE_SUBSYSTEM_WINDOWS_CUI) + .Case("default", IMAGE_SUBSYSTEM_UNKNOWN) .Case("efi_application", IMAGE_SUBSYSTEM_EFI_APPLICATION) .Case("efi_boot_service_driver", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) .Case("efi_rom", IMAGE_SUBSYSTEM_EFI_ROM) .Case("efi_runtime_driver", IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) .Case("native", IMAGE_SUBSYSTEM_NATIVE) .Case("posix", IMAGE_SUBSYSTEM_POSIX_CUI) .Case("windows", IMAGE_SUBSYSTEM_WINDOWS_GUI) .Default(IMAGE_SUBSYSTEM_UNKNOWN); - if (*Sys == IMAGE_SUBSYSTEM_UNKNOWN) - fatal("unknown subsystem: " + SysStr); - if (!Ver.empty()) - parseVersion(Ver, Major, Minor); + if (*sys == IMAGE_SUBSYSTEM_UNKNOWN && sysStrLower != "default") + fatal("unknown subsystem: " + sysStr); + if (!ver.empty()) + parseVersion(ver, major, minor); } // Parse a string of the form of "=". // Results are directly written to Config. -void parseAlternateName(StringRef S) { - StringRef From, To; - std::tie(From, To) = S.split('='); - if (From.empty() || To.empty()) - fatal("/alternatename: invalid argument: " + S); - auto It = Config->AlternateNames.find(From); - if (It != Config->AlternateNames.end() && It->second != To) - fatal("/alternatename: conflicts: " + S); - Config->AlternateNames.insert(It, std::make_pair(From, To)); +void parseAlternateName(StringRef s) { + StringRef from, to; + std::tie(from, to) = s.split('='); + if (from.empty() || to.empty()) + fatal("/alternatename: invalid argument: " + s); + auto it = config->alternateNames.find(from); + if (it != config->alternateNames.end() && it->second != to) + fatal("/alternatename: conflicts: " + s); + config->alternateNames.insert(it, std::make_pair(from, to)); } // Parse a string of the form of "=". // Results are directly written to Config. -void parseMerge(StringRef S) { - StringRef From, To; - std::tie(From, To) = S.split('='); - if (From.empty() || To.empty()) - fatal("/merge: invalid argument: " + S); - if (From == ".rsrc" || To == ".rsrc") +void parseMerge(StringRef s) { + StringRef from, to; + std::tie(from, to) = s.split('='); + if (from.empty() || to.empty()) + fatal("/merge: invalid argument: " + s); + if (from == ".rsrc" || to == ".rsrc") fatal("/merge: cannot merge '.rsrc' with any section"); - if (From == ".reloc" || To == ".reloc") + if (from == ".reloc" || to == ".reloc") fatal("/merge: cannot merge '.reloc' with any section"); - auto Pair = Config->Merge.insert(std::make_pair(From, To)); - bool Inserted = Pair.second; - if (!Inserted) { - StringRef Existing = Pair.first->second; - if (Existing != To) - warn(S + ": already merged into " + Existing); + auto pair = config->merge.insert(std::make_pair(from, to)); + bool inserted = pair.second; + if (!inserted) { + StringRef existing = pair.first->second; + if (existing != to) + warn(s + ": already merged into " + existing); } } -static uint32_t parseSectionAttributes(StringRef S) { - uint32_t Ret = 0; - for (char C : S.lower()) { - switch (C) { +static uint32_t parseSectionAttributes(StringRef s) { + uint32_t ret = 0; + for (char c : s.lower()) { + switch (c) { case 'd': - Ret |= IMAGE_SCN_MEM_DISCARDABLE; + ret |= IMAGE_SCN_MEM_DISCARDABLE; break; case 'e': - Ret |= IMAGE_SCN_MEM_EXECUTE; + ret |= IMAGE_SCN_MEM_EXECUTE; break; case 'k': - Ret |= IMAGE_SCN_MEM_NOT_CACHED; + ret |= IMAGE_SCN_MEM_NOT_CACHED; break; case 'p': - Ret |= IMAGE_SCN_MEM_NOT_PAGED; + ret |= IMAGE_SCN_MEM_NOT_PAGED; break; case 'r': - Ret |= IMAGE_SCN_MEM_READ; + ret |= IMAGE_SCN_MEM_READ; break; case 's': - Ret |= IMAGE_SCN_MEM_SHARED; + ret |= IMAGE_SCN_MEM_SHARED; break; case 'w': - Ret |= IMAGE_SCN_MEM_WRITE; + ret |= IMAGE_SCN_MEM_WRITE; break; default: - fatal("/section: invalid argument: " + S); + fatal("/section: invalid argument: " + s); } } - return Ret; + return ret; } // Parses /section option argument. -void parseSection(StringRef S) { - StringRef Name, Attrs; - std::tie(Name, Attrs) = S.split(','); - if (Name.empty() || Attrs.empty()) - fatal("/section: invalid argument: " + S); - Config->Section[Name] = parseSectionAttributes(Attrs); +void parseSection(StringRef s) { + StringRef name, attrs; + std::tie(name, attrs) = s.split(','); + if (name.empty() || attrs.empty()) + fatal("/section: invalid argument: " + 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); +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); + int v; + if (align.getAsInteger(0, v)) { + error("/aligncomm: invalid argument: " + s); return; } - Config->AlignComm[Name] = std::max(Config->AlignComm[Name], 1 << V); + config->alignComm[name] = std::max(config->alignComm[name], 1 << v); } +// Parses /functionpadmin option argument. +void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine) { + StringRef arg = a->getNumValues() ? a->getValue() : ""; + if (!arg.empty()) { + // Optional padding in bytes is given. + if (arg.getAsInteger(0, config->functionPadMin)) + error("/functionpadmin: invalid argument: " + arg); + return; + } + // No optional argument given. + // Set default padding based on machine, similar to link.exe. + // There is no default padding for ARM platforms. + if (machine == I386) { + config->functionPadMin = 5; + } else if (machine == AMD64) { + config->functionPadMin = 6; + } else { + error("/functionpadmin: invalid argument for this machine: " + arg); + } +} + // Parses a string in the form of "EMBED[,=]|NO". // Results are directly written to Config. -void parseManifest(StringRef Arg) { - if (Arg.equals_lower("no")) { - Config->Manifest = Configuration::No; +void parseManifest(StringRef arg) { + if (arg.equals_lower("no")) { + config->manifest = Configuration::No; return; } - if (!Arg.startswith_lower("embed")) - fatal("invalid option " + Arg); - Config->Manifest = Configuration::Embed; - Arg = Arg.substr(strlen("embed")); - if (Arg.empty()) + if (!arg.startswith_lower("embed")) + fatal("invalid option " + arg); + config->manifest = Configuration::Embed; + arg = arg.substr(strlen("embed")); + if (arg.empty()) return; - if (!Arg.startswith_lower(",id=")) - fatal("invalid option " + Arg); - Arg = Arg.substr(strlen(",id=")); - if (Arg.getAsInteger(0, Config->ManifestID)) - fatal("invalid option " + Arg); + if (!arg.startswith_lower(",id=")) + fatal("invalid option " + arg); + arg = arg.substr(strlen(",id=")); + if (arg.getAsInteger(0, config->manifestID)) + fatal("invalid option " + arg); } // Parses a string in the form of "level=|uiAccess=|NO". // Results are directly written to Config. -void parseManifestUAC(StringRef Arg) { - if (Arg.equals_lower("no")) { - Config->ManifestUAC = false; +void parseManifestUAC(StringRef arg) { + if (arg.equals_lower("no")) { + config->manifestUAC = false; return; } for (;;) { - Arg = Arg.ltrim(); - if (Arg.empty()) + arg = arg.ltrim(); + if (arg.empty()) return; - if (Arg.startswith_lower("level=")) { - Arg = Arg.substr(strlen("level=")); - std::tie(Config->ManifestLevel, Arg) = Arg.split(" "); + if (arg.startswith_lower("level=")) { + arg = arg.substr(strlen("level=")); + std::tie(config->manifestLevel, arg) = arg.split(" "); continue; } - if (Arg.startswith_lower("uiaccess=")) { - Arg = Arg.substr(strlen("uiaccess=")); - std::tie(Config->ManifestUIAccess, Arg) = Arg.split(" "); + if (arg.startswith_lower("uiaccess=")) { + arg = arg.substr(strlen("uiaccess=")); + std::tie(config->manifestUIAccess, arg) = arg.split(" "); continue; } - fatal("invalid option " + Arg); + fatal("invalid option " + arg); } } +// Parses a string in the form of "cd|net[,(cd|net)]*" +// Results are directly written to Config. +void parseSwaprun(StringRef arg) { + do { + StringRef swaprun, newArg; + std::tie(swaprun, newArg) = arg.split(','); + if (swaprun.equals_lower("cd")) + config->swaprunCD = true; + else if (swaprun.equals_lower("net")) + config->swaprunNet = true; + else if (swaprun.empty()) + error("/swaprun: missing argument"); + else + error("/swaprun: invalid argument: " + swaprun); + // To catch trailing commas, e.g. `/spawrun:cd,` + if (newArg.empty() && arg.endswith(",")) + error("/swaprun: missing argument"); + arg = newArg; + } while (!arg.empty()); +} + // An RAII temporary file class that automatically removes a temporary file. namespace { class TemporaryFile { public: - TemporaryFile(StringRef Prefix, StringRef Extn, StringRef Contents = "") { - SmallString<128> S; - if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S)) - fatal("cannot create a temporary file: " + EC.message()); - Path = S.str(); + TemporaryFile(StringRef prefix, StringRef extn, StringRef contents = "") { + SmallString<128> s; + if (auto ec = sys::fs::createTemporaryFile("lld-" + prefix, extn, s)) + 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("failed to open " + Path + ": " + EC.message()); - OS << Contents; + if (!contents.empty()) { + std::error_code ec; + raw_fd_ostream os(path, ec, sys::fs::F_None); + if (ec) + fatal("failed to open " + path + ": " + ec.message()); + os << contents; } } - TemporaryFile(TemporaryFile &&Obj) { - std::swap(Path, Obj.Path); + TemporaryFile(TemporaryFile &&obj) { + std::swap(path, obj.path); } ~TemporaryFile() { - if (Path.empty()) + if (path.empty()) return; - if (sys::fs::remove(Path)) - fatal("failed to remove " + Path); + if (sys::fs::remove(path)) + fatal("failed to remove " + path); } // Returns a memory buffer of this temporary file. // Note that this function does not leave the file open, // so it is safe to remove the file immediately after this function // is called (you cannot remove an opened file on Windows.) std::unique_ptr getMemoryBuffer() { - // IsVolatileSize=true forces MemoryBuffer to not use mmap(). - return CHECK(MemoryBuffer::getFile(Path, /*FileSize=*/-1, + // IsVolatile=true forces MemoryBuffer to not use mmap(). + return CHECK(MemoryBuffer::getFile(path, /*FileSize=*/-1, /*RequiresNullTerminator=*/false, - /*IsVolatileSize=*/true), - "could not open " + Path); + /*IsVolatile=*/true), + "could not open " + path); } - std::string Path; + std::string path; }; } static std::string createDefaultXml() { - std::string Ret; - raw_string_ostream OS(Ret); + 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. - OS << "\n" + os << "\n" << "\n"; - if (Config->ManifestUAC) { - OS << " \n" + if (config->manifestUAC) { + os << " \n" << " \n" << " \n" - << " \n" + << " \n" << " \n" << " \n" << " \n"; } - if (!Config->ManifestDependency.empty()) { - OS << " \n" + if (!config->manifestDependency.empty()) { + os << " \n" << " \n" - << " ManifestDependency << " />\n" + << " manifestDependency << " />\n" << " \n" << " \n"; } - OS << "\n"; - return OS.str(); + os << "\n"; + return os.str(); } -static std::string createManifestXmlWithInternalMt(StringRef DefaultXml) { - std::unique_ptr DefaultXmlCopy = - MemoryBuffer::getMemBufferCopy(DefaultXml); +static std::string createManifestXmlWithInternalMt(StringRef defaultXml) { + std::unique_ptr defaultXmlCopy = + MemoryBuffer::getMemBufferCopy(defaultXml); - windows_manifest::WindowsManifestMerger Merger; - if (auto E = Merger.merge(*DefaultXmlCopy.get())) + windows_manifest::WindowsManifestMerger merger; + if (auto e = merger.merge(*defaultXmlCopy.get())) fatal("internal manifest tool failed on default xml: " + - toString(std::move(E))); + toString(std::move(e))); - for (StringRef Filename : Config->ManifestInput) { - std::unique_ptr Manifest = - check(MemoryBuffer::getFile(Filename)); - if (auto E = Merger.merge(*Manifest.get())) - fatal("internal manifest tool failed on file " + Filename + ": " + - toString(std::move(E))); + for (StringRef filename : config->manifestInput) { + std::unique_ptr 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(); + return merger.getMergedManifest().get()->getBuffer(); } -static std::string createManifestXmlWithExternalMt(StringRef DefaultXml) { +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(); + 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(); // 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"); + TemporaryFile user("user", "manifest"); - Executor E("mt.exe"); - E.add("/manifest"); - E.add(Default.Path); - for (StringRef Filename : Config->ManifestInput) { - E.add("/manifest"); - E.add(Filename); + Executor e("mt.exe"); + e.add("/manifest"); + e.add(Default.path); + for (StringRef filename : config->manifestInput) { + e.add("/manifest"); + e.add(filename); } - E.add("/nologo"); - E.add("/out:" + StringRef(User.Path)); - E.run(); + e.add("/nologo"); + e.add("/out:" + StringRef(user.path)); + e.run(); - return CHECK(MemoryBuffer::getFile(User.Path), "could not open " + User.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; + std::string defaultXml = createDefaultXml(); + if (config->manifestInput.empty()) + return defaultXml; if (windows_manifest::isAvailable()) - return createManifestXmlWithInternalMt(DefaultXml); + return createManifestXmlWithInternalMt(defaultXml); - return createManifestXmlWithExternalMt(DefaultXml); + return createManifestXmlWithExternalMt(defaultXml); } static std::unique_ptr -createMemoryBufferForManifestRes(size_t ManifestSize) { - size_t ResSize = alignTo( +createMemoryBufferForManifestRes(size_t manifestSize) { + size_t resSize = alignTo( object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE + sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) + - sizeof(object::WinResHeaderSuffix) + ManifestSize, + sizeof(object::WinResHeaderSuffix) + manifestSize, object::WIN_RES_DATA_ALIGNMENT); - return WritableMemoryBuffer::getNewMemBuffer(ResSize, Config->OutputFile + + return WritableMemoryBuffer::getNewMemBuffer(resSize, config->outputFile + ".manifest.res"); } -static void writeResFileHeader(char *&Buf) { - memcpy(Buf, COFF::WinResMagic, sizeof(COFF::WinResMagic)); - Buf += sizeof(COFF::WinResMagic); - memset(Buf, 0, object::WIN_RES_NULL_ENTRY_SIZE); - Buf += object::WIN_RES_NULL_ENTRY_SIZE; +static void writeResFileHeader(char *&buf) { + memcpy(buf, COFF::WinResMagic, sizeof(COFF::WinResMagic)); + buf += sizeof(COFF::WinResMagic); + memset(buf, 0, object::WIN_RES_NULL_ENTRY_SIZE); + buf += object::WIN_RES_NULL_ENTRY_SIZE; } -static void writeResEntryHeader(char *&Buf, size_t ManifestSize) { +static void writeResEntryHeader(char *&buf, size_t manifestSize) { // Write the prefix. - auto *Prefix = reinterpret_cast(Buf); - Prefix->DataSize = ManifestSize; - Prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) + + auto *prefix = reinterpret_cast(buf); + prefix->DataSize = manifestSize; + prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) + sizeof(object::WinResHeaderSuffix); - Buf += sizeof(object::WinResHeaderPrefix); + buf += sizeof(object::WinResHeaderPrefix); // Write the Type/Name IDs. - auto *IDs = reinterpret_cast(Buf); - IDs->setType(RT_MANIFEST); - IDs->setName(Config->ManifestID); - Buf += sizeof(object::WinResIDs); + auto *iDs = reinterpret_cast(buf); + iDs->setType(RT_MANIFEST); + iDs->setName(config->manifestID); + buf += sizeof(object::WinResIDs); // Write the suffix. - auto *Suffix = reinterpret_cast(Buf); - Suffix->DataVersion = 0; - Suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE; - Suffix->Language = SUBLANG_ENGLISH_US; - Suffix->Version = 0; - Suffix->Characteristics = 0; - Buf += sizeof(object::WinResHeaderSuffix); + auto *suffix = reinterpret_cast(buf); + suffix->DataVersion = 0; + suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE; + suffix->Language = SUBLANG_ENGLISH_US; + suffix->Version = 0; + suffix->Characteristics = 0; + buf += sizeof(object::WinResHeaderSuffix); } // Create a resource file containing a manifest XML. std::unique_ptr createManifestRes() { - std::string Manifest = createManifestXml(); + std::string manifest = createManifestXml(); - std::unique_ptr Res = - createMemoryBufferForManifestRes(Manifest.size()); + std::unique_ptr res = + createMemoryBufferForManifestRes(manifest.size()); - char *Buf = Res->getBufferStart(); - writeResFileHeader(Buf); - writeResEntryHeader(Buf, Manifest.size()); + char *buf = res->getBufferStart(); + writeResFileHeader(buf); + writeResEntryHeader(buf, manifest.size()); // Copy the manifest data into the .res file. - std::copy(Manifest.begin(), Manifest.end(), Buf); - return std::move(Res); + std::copy(manifest.begin(), manifest.end(), buf); + return std::move(res); } void createSideBySideManifest() { - std::string Path = Config->ManifestFile; - if (Path == "") - Path = Config->OutputFile + ".manifest"; - std::error_code EC; - raw_fd_ostream Out(Path, EC, sys::fs::F_Text); - if (EC) - fatal("failed to create manifest: " + EC.message()); - Out << createManifestXml(); + std::string path = config->manifestFile; + if (path == "") + path = config->outputFile + ".manifest"; + std::error_code ec; + raw_fd_ostream out(path, ec, sys::fs::F_Text); + if (ec) + fatal("failed to create manifest: " + ec.message()); + out << createManifestXml(); } // Parse a string in the form of // "[=][,@ordinal[,NONAME]][,DATA][,PRIVATE]" // or "=.". // Used for parsing /export arguments. -Export parseExport(StringRef Arg) { - Export E; - StringRef Rest; - std::tie(E.Name, Rest) = Arg.split(","); - if (E.Name.empty()) +Export parseExport(StringRef arg) { + Export e; + StringRef rest; + std::tie(e.name, rest) = arg.split(","); + if (e.name.empty()) goto err; - if (E.Name.contains('=')) { - StringRef X, Y; - std::tie(X, Y) = E.Name.split("="); + if (e.name.contains('=')) { + StringRef x, y; + std::tie(x, y) = e.name.split("="); // If "=.". - if (Y.contains(".")) { - E.Name = X; - E.ForwardTo = Y; - return E; + if (y.contains(".")) { + e.name = x; + e.forwardTo = y; + return e; } - E.ExtName = X; - E.Name = Y; - if (E.Name.empty()) + e.extName = x; + e.name = y; + if (e.name.empty()) goto err; } // If "=[,@ordinal[,NONAME]][,DATA][,PRIVATE]" - while (!Rest.empty()) { - StringRef Tok; - std::tie(Tok, Rest) = Rest.split(","); - if (Tok.equals_lower("noname")) { - if (E.Ordinal == 0) + while (!rest.empty()) { + StringRef tok; + std::tie(tok, rest) = rest.split(","); + if (tok.equals_lower("noname")) { + if (e.ordinal == 0) goto err; - E.Noname = true; + e.noname = true; continue; } - if (Tok.equals_lower("data")) { - E.Data = true; + if (tok.equals_lower("data")) { + e.data = true; continue; } - if (Tok.equals_lower("constant")) { - E.Constant = true; + if (tok.equals_lower("constant")) { + e.constant = true; continue; } - if (Tok.equals_lower("private")) { - E.Private = true; + if (tok.equals_lower("private")) { + e.isPrivate = true; continue; } - if (Tok.startswith("@")) { - int32_t Ord; - if (Tok.substr(1).getAsInteger(0, Ord)) + if (tok.startswith("@")) { + int32_t ord; + if (tok.substr(1).getAsInteger(0, ord)) goto err; - if (Ord <= 0 || 65535 < Ord) + if (ord <= 0 || 65535 < ord) goto err; - E.Ordinal = Ord; + e.ordinal = ord; continue; } goto err; } - return E; + return e; err: - fatal("invalid /export: " + Arg); + fatal("invalid /export: " + arg); } -static StringRef undecorate(StringRef Sym) { - if (Config->Machine != I386) - return Sym; +static StringRef undecorate(StringRef sym) { + if (config->machine != I386) + return sym; // In MSVC mode, a fully decorated stdcall function is exported // as-is with the leading underscore (with type IMPORT_NAME). // In MinGW mode, a decorated stdcall function gets the underscore // removed, just like normal cdecl functions. - if (Sym.startswith("_") && Sym.contains('@') && !Config->MinGW) - return Sym; - return Sym.startswith("_") ? Sym.substr(1) : Sym; + if (sym.startswith("_") && sym.contains('@') && !config->mingw) + return sym; + return sym.startswith("_") ? sym.substr(1) : sym; } // Convert stdcall/fastcall style symbols into unsuffixed symbols, // with or without a leading underscore. (MinGW specific.) -static StringRef killAt(StringRef Sym, bool Prefix) { - if (Sym.empty()) - return Sym; +static StringRef killAt(StringRef sym, bool prefix) { + if (sym.empty()) + return sym; // Strip any trailing stdcall suffix - Sym = Sym.substr(0, Sym.find('@', 1)); - if (!Sym.startswith("@")) { - if (Prefix && !Sym.startswith("_")) - return Saver.save("_" + Sym); - return Sym; + sym = sym.substr(0, sym.find('@', 1)); + if (!sym.startswith("@")) { + if (prefix && !sym.startswith("_")) + return saver.save("_" + sym); + return sym; } // For fastcall, remove the leading @ and replace it with an // underscore, if prefixes are used. - Sym = Sym.substr(1); - if (Prefix) - Sym = Saver.save("_" + Sym); - return Sym; + sym = sym.substr(1); + if (prefix) + sym = saver.save("_" + sym); + return sym; } // Performs error checking on all /export arguments. // It also sets ordinals. void fixupExports() { // Symbol ordinals must be unique. - std::set Ords; - for (Export &E : Config->Exports) { - if (E.Ordinal == 0) + std::set ords; + for (Export &e : config->exports) { + if (e.ordinal == 0) continue; - if (!Ords.insert(E.Ordinal).second) - fatal("duplicate export ordinal: " + E.Name); + if (!ords.insert(e.ordinal).second) + fatal("duplicate export ordinal: " + e.name); } - for (Export &E : Config->Exports) { - Symbol *Sym = E.Sym; - if (!E.ForwardTo.empty() || !Sym) { - E.SymbolName = E.Name; + for (Export &e : config->exports) { + if (!e.forwardTo.empty()) { + e.exportName = undecorate(e.name); } else { - if (auto *U = dyn_cast(Sym)) - if (U->WeakAlias) - Sym = U->WeakAlias; - E.SymbolName = Sym->getName(); + e.exportName = undecorate(e.extName.empty() ? e.name : e.extName); } } - for (Export &E : Config->Exports) { - if (!E.ForwardTo.empty()) { - E.ExportName = undecorate(E.Name); - } else { - E.ExportName = undecorate(E.ExtName.empty() ? E.Name : E.ExtName); + if (config->killAt && config->machine == I386) { + for (Export &e : config->exports) { + e.name = killAt(e.name, true); + e.exportName = killAt(e.exportName, false); + e.extName = killAt(e.extName, true); + e.symbolName = killAt(e.symbolName, true); } } - if (Config->KillAt && Config->Machine == I386) { - for (Export &E : Config->Exports) { - E.Name = killAt(E.Name, true); - E.ExportName = killAt(E.ExportName, false); - E.ExtName = killAt(E.ExtName, true); - E.SymbolName = killAt(E.SymbolName, true); - } - } - // Uniquefy by name. - DenseMap Map(Config->Exports.size()); - std::vector V; - for (Export &E : Config->Exports) { - auto Pair = Map.insert(std::make_pair(E.ExportName, &E)); - bool Inserted = Pair.second; - if (Inserted) { - V.push_back(E); + DenseMap map(config->exports.size()); + std::vector v; + for (Export &e : config->exports) { + auto pair = map.insert(std::make_pair(e.exportName, &e)); + bool inserted = pair.second; + if (inserted) { + v.push_back(e); continue; } - Export *Existing = Pair.first->second; - if (E == *Existing || E.Name != Existing->Name) + Export *existing = pair.first->second; + if (e == *existing || e.name != existing->name) continue; - warn("duplicate /export option: " + E.Name); + warn("duplicate /export option: " + e.name); } - Config->Exports = std::move(V); + config->exports = std::move(v); // Sort by name. - std::sort(Config->Exports.begin(), Config->Exports.end(), - [](const Export &A, const Export &B) { - return A.ExportName < B.ExportName; + std::sort(config->exports.begin(), config->exports.end(), + [](const Export &a, const Export &b) { + return a.exportName < b.exportName; }); } void assignExportOrdinals() { // Assign unique ordinals if default (= 0). - uint16_t Max = 0; - for (Export &E : Config->Exports) - Max = std::max(Max, E.Ordinal); - for (Export &E : Config->Exports) - if (E.Ordinal == 0) - E.Ordinal = ++Max; + uint16_t max = 0; + for (Export &e : config->exports) + max = std::max(max, e.ordinal); + for (Export &e : config->exports) + if (e.ordinal == 0) + e.ordinal = ++max; } // Parses a string in the form of "key=value" and check // if value matches previous values for the same key. -void checkFailIfMismatch(StringRef Arg) { - StringRef K, V; - std::tie(K, V) = Arg.split('='); - if (K.empty() || V.empty()) - fatal("/failifmismatch: invalid argument: " + Arg); - StringRef Existing = Config->MustMatch[K]; - if (!Existing.empty() && V != Existing) - fatal("/failifmismatch: mismatch detected: " + Existing + " and " + V + - " for key " + K); - Config->MustMatch[K] = V; +void checkFailIfMismatch(StringRef arg, InputFile *source) { + StringRef k, v; + std::tie(k, v) = arg.split('='); + if (k.empty() || v.empty()) + fatal("/failifmismatch: invalid argument: " + arg); + std::pair existing = config->mustMatch[k]; + if (!existing.first.empty() && v != existing.first) { + std::string sourceStr = source ? toString(source) : "cmd-line"; + std::string existingStr = + existing.second ? toString(existing.second) : "cmd-line"; + fatal("/failifmismatch: mismatch detected for '" + k + "':\n>>> " + + existingStr + " has value " + existing.first + "\n>>> " + sourceStr + + " has value " + v); + } + config->mustMatch[k] = {v, source}; } // Convert Windows resource files (.res files) to a .obj file. -MemoryBufferRef convertResToCOFF(ArrayRef MBs) { - object::WindowsResourceParser Parser; +// Does what cvtres.exe does, but in-process and cross-platform. +MemoryBufferRef convertResToCOFF(ArrayRef mbs) { + object::WindowsResourceParser parser; - for (MemoryBufferRef MB : MBs) { - std::unique_ptr Bin = check(object::createBinary(MB)); - object::WindowsResource *RF = dyn_cast(Bin.get()); - if (!RF) + for (MemoryBufferRef mb : mbs) { + std::unique_ptr bin = check(object::createBinary(mb)); + object::WindowsResource *rf = dyn_cast(bin.get()); + if (!rf) fatal("cannot compile non-resource file as resource"); - if (auto EC = Parser.parse(RF)) - fatal("failed to parse .res file: " + toString(std::move(EC))); + + std::vector duplicates; + if (auto ec = parser.parse(rf, duplicates)) + fatal(toString(std::move(ec))); + + for (const auto &dupeDiag : duplicates) + if (config->forceMultipleRes) + warn(dupeDiag); + else + error(dupeDiag); } - Expected> E = - llvm::object::writeWindowsResourceCOFF(Config->Machine, Parser); - if (!E) - fatal("failed to write .res to COFF: " + toString(E.takeError())); + Expected> e = + llvm::object::writeWindowsResourceCOFF(config->machine, parser, + config->timestamp); + if (!e) + fatal("failed to write .res to COFF: " + toString(e.takeError())); - MemoryBufferRef MBRef = **E; - make>(std::move(*E)); // take ownership - return MBRef; + MemoryBufferRef mbref = **e; + make>(std::move(*e)); // take ownership + return mbref; } // Create OptTable // Create prefix string literals used in Options.td #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; #include "Options.inc" #undef PREFIX // Create table mapping all options defined in Options.td -static const llvm::opt::OptTable::Info InfoTable[] = { +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}, #include "Options.inc" #undef OPTION }; -COFFOptTable::COFFOptTable() : OptTable(InfoTable, true) {} +COFFOptTable::COFFOptTable() : OptTable(infoTable, true) {} // Set color diagnostics according to --color-diagnostics={auto,always,never} // or --no-color-diagnostics flags. -static void handleColorDiagnostics(opt::InputArgList &Args) { - auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq, +static void handleColorDiagnostics(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq, OPT_no_color_diagnostics); - if (!Arg) + if (!arg) return; - if (Arg->getOption().getID() == OPT_color_diagnostics) { - errorHandler().ColorDiagnostics = true; - } else if (Arg->getOption().getID() == OPT_no_color_diagnostics) { - errorHandler().ColorDiagnostics = false; + 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); + StringRef s = arg->getValue(); + if (s == "always") + errorHandler().colorDiagnostics = true; + else if (s == "never") + errorHandler().colorDiagnostics = false; + else if (s != "auto") + error("unknown option: --color-diagnostics=" + s); } } -static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) { - if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) { - StringRef S = Arg->getValue(); - if (S != "windows" && S != "posix") - error("invalid response file quoting: " + S); - if (S == "windows") +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 Argv) { +opt::InputArgList ArgParser::parse(ArrayRef argv) { // Make InputArgList from string vectors. - unsigned MissingIndex; - unsigned MissingCount; + unsigned missingIndex; + unsigned missingCount; // 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(Argv, MissingIndex, MissingCount); + opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount); // Expand response files (arguments in the form of @) // and then parse the argument again. - SmallVector ExpandedArgv(Argv.data(), Argv.data() + Argv.size()); - cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), ExpandedArgv); - Args = Table.ParseArgs(makeArrayRef(ExpandedArgv).drop_front(), MissingIndex, - MissingCount); + SmallVector expandedArgv(argv.data(), + argv.data() + argv.size()); + cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv); + args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex, + missingCount); // Print the real command line if response files are expanded. - if (Args.hasArg(OPT_verbose) && Argv.size() != ExpandedArgv.size()) { - std::string Msg = "Command line:"; - for (const char *S : ExpandedArgv) - Msg += " " + std::string(S); - message(Msg); + if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) { + std::string msg = "Command line:"; + for (const char *s : expandedArgv) + msg += " " + std::string(s); + message(msg); } // Save the command line after response file expansion so we can write it to // the PDB if necessary. - Config->Argv = {ExpandedArgv.begin(), ExpandedArgv.end()}; + config->argv = {expandedArgv.begin(), expandedArgv.end()}; // Handle /WX early since it converts missing argument warnings to errors. - errorHandler().FatalWarnings = Args.hasFlag(OPT_WX, OPT_WX_no, false); + errorHandler().fatalWarnings = args.hasFlag(OPT_WX, OPT_WX_no, false); - if (MissingCount) - fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); + if (missingCount) + fatal(Twine(args.getArgString(missingIndex)) + ": missing argument"); - handleColorDiagnostics(Args); + handleColorDiagnostics(args); - for (auto *Arg : Args.filtered(OPT_UNKNOWN)) - warn("ignoring unknown argument: " + Arg->getSpelling()); + for (auto *arg : args.filtered(OPT_UNKNOWN)) { + std::string nearest; + if (table.findNearest(arg->getAsString(args), nearest) > 1) + warn("ignoring unknown argument '" + arg->getAsString(args) + "'"); + else + warn("ignoring unknown argument '" + arg->getAsString(args) + + "', did you mean '" + nearest + "'"); + } - if (Args.hasArg(OPT_lib)) + if (args.hasArg(OPT_lib)) warn("ignoring /lib since it's not the first argument"); - return Args; + return args; } // Tokenizes and parses a given string as command line in .drective section. // /EXPORT options are processed in fastpath. std::pair> -ArgParser::parseDirectives(StringRef S) { - std::vector Exports; - SmallVector Rest; +ArgParser::parseDirectives(StringRef s) { + std::vector exports; + SmallVector rest; - for (StringRef Tok : tokenize(S)) { - if (Tok.startswith_lower("/export:") || Tok.startswith_lower("-export:")) - Exports.push_back(Tok.substr(strlen("/export:"))); + for (StringRef tok : tokenize(s)) { + if (tok.startswith_lower("/export:") || tok.startswith_lower("-export:")) + exports.push_back(tok.substr(strlen("/export:"))); else - Rest.push_back(Tok.data()); + rest.push_back(tok.data()); } // Make InputArgList from unparsed string vectors. - unsigned MissingIndex; - unsigned MissingCount; + unsigned missingIndex; + unsigned missingCount; - opt::InputArgList Args = Table.ParseArgs(Rest, MissingIndex, MissingCount); + opt::InputArgList args = table.ParseArgs(rest, missingIndex, missingCount); - if (MissingCount) - fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); - for (auto *Arg : Args.filtered(OPT_UNKNOWN)) - warn("ignoring unknown argument: " + Arg->getSpelling()); - return {std::move(Args), std::move(Exports)}; + if (missingCount) + fatal(Twine(args.getArgString(missingIndex)) + ": missing argument"); + for (auto *arg : args.filtered(OPT_UNKNOWN)) + warn("ignoring unknown argument: " + arg->getAsString(args)); + return {std::move(args), std::move(exports)}; } // 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 Argv) { +opt::InputArgList ArgParser::parseLINK(std::vector argv) { // Concatenate LINK env and command line arguments, and then parse them. - if (Optional S = Process::GetEnv("LINK")) { - std::vector V = tokenize(*S); - Argv.insert(std::next(Argv.begin()), V.begin(), V.end()); + if (Optional s = Process::GetEnv("LINK")) { + std::vector v = tokenize(*s); + argv.insert(std::next(argv.begin()), v.begin(), v.end()); } - if (Optional S = Process::GetEnv("_LINK_")) { - std::vector V = tokenize(*S); - Argv.insert(std::next(Argv.begin()), V.begin(), V.end()); + if (Optional s = Process::GetEnv("_LINK_")) { + std::vector v = tokenize(*s); + argv.insert(std::next(argv.begin()), v.begin(), v.end()); } - return parse(Argv); + return parse(argv); } -std::vector ArgParser::tokenize(StringRef S) { - SmallVector Tokens; - cl::TokenizeWindowsCommandLine(S, Saver, Tokens); - return std::vector(Tokens.begin(), Tokens.end()); +std::vector ArgParser::tokenize(StringRef s) { + SmallVector tokens; + cl::TokenizeWindowsCommandLine(s, saver, tokens); + return std::vector(tokens.begin(), tokens.end()); } -void printHelp(const char *Argv0) { +void printHelp(const char *argv0) { COFFOptTable().PrintHelp(outs(), - (std::string(Argv0) + " [options] file...").c_str(), + (std::string(argv0) + " [options] file...").c_str(), "LLVM Linker", false); } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/ICF.cpp =================================================================== --- vendor/lld/dist/COFF/ICF.cpp (revision 351287) +++ vendor/lld/dist/COFF/ICF.cpp (revision 351288) @@ -1,316 +1,317 @@ //===- ICF.cpp ------------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// // // ICF is short for Identical Code Folding. That is a size optimization to // identify and merge two or more read-only sections (typically functions) // that happened to have the same contents. It usually reduces output size // by a few percent. // // On Windows, ICF is enabled by default. // // See ELF/ICF.cpp for the details about the algortihm. // //===----------------------------------------------------------------------===// #include "ICF.h" #include "Chunks.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Threads.h" #include "lld/Common/Timer.h" #include "llvm/ADT/Hashing.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/xxhash.h" #include #include #include using namespace llvm; namespace lld { namespace coff { -static Timer ICFTimer("ICF", Timer::root()); +static Timer icfTimer("ICF", Timer::root()); class ICF { public: - void run(ArrayRef V); + void run(ArrayRef v); private: - void segregate(size_t Begin, size_t End, bool Constant); + void segregate(size_t begin, size_t end, bool constant); - bool assocEquals(const SectionChunk *A, const SectionChunk *B); + bool assocEquals(const SectionChunk *a, const SectionChunk *b); - bool equalsConstant(const SectionChunk *A, const SectionChunk *B); - bool equalsVariable(const SectionChunk *A, const SectionChunk *B); + bool equalsConstant(const SectionChunk *a, const SectionChunk *b); + bool equalsVariable(const SectionChunk *a, const SectionChunk *b); - uint32_t getHash(SectionChunk *C); - bool isEligible(SectionChunk *C); + bool isEligible(SectionChunk *c); - size_t findBoundary(size_t Begin, size_t End); + size_t findBoundary(size_t begin, size_t end); - void forEachClassRange(size_t Begin, size_t End, - std::function Fn); + void forEachClassRange(size_t begin, size_t end, + std::function fn); - void forEachClass(std::function Fn); + void forEachClass(std::function fn); - std::vector Chunks; - int Cnt = 0; - std::atomic Repeat = {false}; + std::vector chunks; + int cnt = 0; + std::atomic repeat = {false}; }; // Returns true if section S is subject of ICF. // // Microsoft's documentation // (https://msdn.microsoft.com/en-us/library/bxwfs976.aspx; visited April // 2017) says that /opt:icf folds both functions and read-only data. // Despite that, the MSVC linker folds only functions. We found // a few instances of programs that are not safe for data merging. // Therefore, we merge only functions just like the MSVC tool. However, we also // merge read-only sections in a couple of cases where the address of the // section is insignificant to the user program and the behaviour matches that // of the Visual C++ linker. -bool ICF::isEligible(SectionChunk *C) { +bool ICF::isEligible(SectionChunk *c) { // Non-comdat chunks, dead chunks, and writable chunks are not elegible. - bool Writable = C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE; - if (!C->isCOMDAT() || !C->Live || Writable) + bool writable = c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE; + if (!c->isCOMDAT() || !c->live || writable) return false; // Code sections are eligible. - if (C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE) + if (c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE) return true; // .pdata and .xdata unwind info sections are eligible. - StringRef OutSecName = C->getSectionName().split('$').first; - if (OutSecName == ".pdata" || OutSecName == ".xdata") + StringRef outSecName = c->getSectionName().split('$').first; + if (outSecName == ".pdata" || outSecName == ".xdata") return true; // So are vtables. - if (C->Sym && C->Sym->getName().startswith("??_7")) + if (c->sym && c->sym->getName().startswith("??_7")) return true; // Anything else not in an address-significance table is eligible. - return !C->KeepUnique; + return !c->keepUnique; } // Split an equivalence class into smaller classes. -void ICF::segregate(size_t Begin, size_t End, bool Constant) { - while (Begin < End) { +void ICF::segregate(size_t begin, size_t end, bool constant) { + while (begin < end) { // Divide [Begin, End) into two. Let Mid be the start index of the // second group. - auto Bound = std::stable_partition( - Chunks.begin() + Begin + 1, Chunks.begin() + End, [&](SectionChunk *S) { - if (Constant) - return equalsConstant(Chunks[Begin], S); - return equalsVariable(Chunks[Begin], S); + auto bound = std::stable_partition( + chunks.begin() + begin + 1, chunks.begin() + end, [&](SectionChunk *s) { + if (constant) + return equalsConstant(chunks[begin], s); + return equalsVariable(chunks[begin], s); }); - size_t Mid = Bound - Chunks.begin(); + size_t mid = bound - chunks.begin(); // Split [Begin, End) into [Begin, Mid) and [Mid, End). We use Mid as an // equivalence class ID because every group ends with a unique index. - for (size_t I = Begin; I < Mid; ++I) - Chunks[I]->Class[(Cnt + 1) % 2] = Mid; + for (size_t i = begin; i < mid; ++i) + chunks[i]->eqClass[(cnt + 1) % 2] = mid; // If we created a group, we need to iterate the main loop again. - if (Mid != End) - Repeat = true; + if (mid != end) + repeat = true; - Begin = Mid; + begin = mid; } } // Returns true if two sections' associative children are equal. -bool ICF::assocEquals(const SectionChunk *A, const SectionChunk *B) { - auto ChildClasses = [&](const SectionChunk *SC) { - std::vector Classes; - for (const SectionChunk *C : SC->children()) - if (!C->SectionName.startswith(".debug") && - C->SectionName != ".gfids$y" && C->SectionName != ".gljmp$y") - Classes.push_back(C->Class[Cnt % 2]); - return Classes; +bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) { + auto childClasses = [&](const SectionChunk *sc) { + std::vector classes; + for (const SectionChunk &c : sc->children()) + if (!c.getSectionName().startswith(".debug") && + c.getSectionName() != ".gfids$y" && c.getSectionName() != ".gljmp$y") + classes.push_back(c.eqClass[cnt % 2]); + return classes; }; - return ChildClasses(A) == ChildClasses(B); + return childClasses(a) == childClasses(b); } // Compare "non-moving" part of two sections, namely everything // except relocation targets. -bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) { - if (A->Relocs.size() != B->Relocs.size()) +bool ICF::equalsConstant(const SectionChunk *a, const SectionChunk *b) { + if (a->relocsSize != b->relocsSize) return false; // Compare relocations. - auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) { - if (R1.Type != R2.Type || - R1.VirtualAddress != R2.VirtualAddress) { + auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) { + if (r1.Type != r2.Type || + r1.VirtualAddress != r2.VirtualAddress) { return false; } - Symbol *B1 = A->File->getSymbol(R1.SymbolTableIndex); - Symbol *B2 = B->File->getSymbol(R2.SymbolTableIndex); - if (B1 == B2) + Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex); + Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex); + if (b1 == b2) return true; - if (auto *D1 = dyn_cast(B1)) - if (auto *D2 = dyn_cast(B2)) - return D1->getValue() == D2->getValue() && - D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2]; + if (auto *d1 = dyn_cast(b1)) + if (auto *d2 = dyn_cast(b2)) + return d1->getValue() == d2->getValue() && + d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2]; return false; }; - if (!std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq)) + if (!std::equal(a->getRelocs().begin(), a->getRelocs().end(), + b->getRelocs().begin(), eq)) return false; // Compare section attributes and contents. - return A->getOutputCharacteristics() == B->getOutputCharacteristics() && - A->SectionName == B->SectionName && - A->Header->SizeOfRawData == B->Header->SizeOfRawData && - A->Checksum == B->Checksum && A->getContents() == B->getContents() && - assocEquals(A, B); + return a->getOutputCharacteristics() == b->getOutputCharacteristics() && + a->getSectionName() == b->getSectionName() && + a->header->SizeOfRawData == b->header->SizeOfRawData && + a->checksum == b->checksum && a->getContents() == b->getContents() && + assocEquals(a, b); } // Compare "moving" part of two sections, namely relocation targets. -bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) { +bool ICF::equalsVariable(const SectionChunk *a, const SectionChunk *b) { // Compare relocations. - auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) { - Symbol *B1 = A->File->getSymbol(R1.SymbolTableIndex); - Symbol *B2 = B->File->getSymbol(R2.SymbolTableIndex); - if (B1 == B2) + auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) { + Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex); + Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex); + if (b1 == b2) return true; - if (auto *D1 = dyn_cast(B1)) - if (auto *D2 = dyn_cast(B2)) - return D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2]; + if (auto *d1 = dyn_cast(b1)) + if (auto *d2 = dyn_cast(b2)) + return d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2]; return false; }; - return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), - Eq) && - assocEquals(A, B); + return std::equal(a->getRelocs().begin(), a->getRelocs().end(), + b->getRelocs().begin(), eq) && + assocEquals(a, b); } // Find the first Chunk after Begin that has a different class from Begin. -size_t ICF::findBoundary(size_t Begin, size_t End) { - for (size_t I = Begin + 1; I < End; ++I) - if (Chunks[Begin]->Class[Cnt % 2] != Chunks[I]->Class[Cnt % 2]) - return I; - return End; +size_t ICF::findBoundary(size_t begin, size_t end) { + for (size_t i = begin + 1; i < end; ++i) + if (chunks[begin]->eqClass[cnt % 2] != chunks[i]->eqClass[cnt % 2]) + return i; + return end; } -void ICF::forEachClassRange(size_t Begin, size_t End, - std::function Fn) { - while (Begin < End) { - size_t Mid = findBoundary(Begin, End); - Fn(Begin, Mid); - Begin = Mid; +void ICF::forEachClassRange(size_t begin, size_t end, + std::function fn) { + while (begin < end) { + size_t mid = findBoundary(begin, end); + fn(begin, mid); + begin = mid; } } // Call Fn on each class group. -void ICF::forEachClass(std::function Fn) { +void ICF::forEachClass(std::function fn) { // If the number of sections are too small to use threading, // call Fn sequentially. - if (Chunks.size() < 1024) { - forEachClassRange(0, Chunks.size(), Fn); - ++Cnt; + if (chunks.size() < 1024) { + forEachClassRange(0, chunks.size(), fn); + ++cnt; return; } // Shard into non-overlapping intervals, and call Fn in parallel. // The sharding must be completed before any calls to Fn are made // so that Fn can modify the Chunks in its shard without causing data // races. - const size_t NumShards = 256; - size_t Step = Chunks.size() / NumShards; - size_t Boundaries[NumShards + 1]; - Boundaries[0] = 0; - Boundaries[NumShards] = Chunks.size(); - parallelForEachN(1, NumShards, [&](size_t I) { - Boundaries[I] = findBoundary((I - 1) * Step, Chunks.size()); + const size_t numShards = 256; + size_t step = chunks.size() / numShards; + size_t boundaries[numShards + 1]; + boundaries[0] = 0; + boundaries[numShards] = chunks.size(); + parallelForEachN(1, numShards, [&](size_t i) { + boundaries[i] = findBoundary((i - 1) * step, chunks.size()); }); - parallelForEachN(1, NumShards + 1, [&](size_t I) { - if (Boundaries[I - 1] < Boundaries[I]) { - forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn); + parallelForEachN(1, numShards + 1, [&](size_t i) { + if (boundaries[i - 1] < boundaries[i]) { + forEachClassRange(boundaries[i - 1], boundaries[i], fn); } }); - ++Cnt; + ++cnt; } // Merge identical COMDAT sections. // Two sections are considered the same if their section headers, // contents and relocations are all the same. -void ICF::run(ArrayRef Vec) { - ScopedTimer T(ICFTimer); +void ICF::run(ArrayRef vec) { + ScopedTimer t(icfTimer); // Collect only mergeable sections and group by hash value. - uint32_t NextId = 1; - for (Chunk *C : Vec) { - if (auto *SC = dyn_cast(C)) { - if (isEligible(SC)) - Chunks.push_back(SC); + uint32_t nextId = 1; + for (Chunk *c : vec) { + if (auto *sc = dyn_cast(c)) { + if (isEligible(sc)) + chunks.push_back(sc); else - SC->Class[0] = NextId++; + sc->eqClass[0] = nextId++; } } // Make sure that ICF doesn't merge sections that are being handled by string // tail merging. - for (auto &P : MergeChunk::Instances) - for (SectionChunk *SC : P.second->Sections) - SC->Class[0] = NextId++; + for (MergeChunk *mc : MergeChunk::instances) + if (mc) + for (SectionChunk *sc : mc->sections) + sc->eqClass[0] = nextId++; // Initially, we use hash values to partition sections. - parallelForEach(Chunks, [&](SectionChunk *SC) { - SC->Class[1] = xxHash64(SC->getContents()); + parallelForEach(chunks, [&](SectionChunk *sc) { + sc->eqClass[0] = xxHash64(sc->getContents()); }); // Combine the hashes of the sections referenced by each section into its // hash. - parallelForEach(Chunks, [&](SectionChunk *SC) { - uint32_t Hash = SC->Class[1]; - for (Symbol *B : SC->symbols()) - if (auto *Sym = dyn_cast_or_null(B)) - Hash ^= Sym->getChunk()->Class[1]; - // Set MSB to 1 to avoid collisions with non-hash classs. - SC->Class[0] = Hash | (1U << 31); - }); + for (unsigned cnt = 0; cnt != 2; ++cnt) { + parallelForEach(chunks, [&](SectionChunk *sc) { + uint32_t hash = sc->eqClass[cnt % 2]; + for (Symbol *b : sc->symbols()) + if (auto *sym = dyn_cast_or_null(b)) + hash += sym->getChunk()->eqClass[cnt % 2]; + // Set MSB to 1 to avoid collisions with non-hash classs. + sc->eqClass[(cnt + 1) % 2] = hash | (1U << 31); + }); + } // From now on, sections in Chunks are ordered so that sections in // the same group are consecutive in the vector. - std::stable_sort(Chunks.begin(), Chunks.end(), - [](SectionChunk *A, SectionChunk *B) { - return A->Class[0] < B->Class[0]; - }); + llvm::stable_sort(chunks, [](const SectionChunk *a, const SectionChunk *b) { + return a->eqClass[0] < b->eqClass[0]; + }); // Compare static contents and assign unique IDs for each static content. - forEachClass([&](size_t Begin, size_t End) { segregate(Begin, End, true); }); + forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); }); // Split groups by comparing relocations until convergence is obtained. do { - Repeat = false; + repeat = false; forEachClass( - [&](size_t Begin, size_t End) { segregate(Begin, End, false); }); - } while (Repeat); + [&](size_t begin, size_t end) { segregate(begin, end, false); }); + } while (repeat); - log("ICF needed " + Twine(Cnt) + " iterations"); + log("ICF needed " + Twine(cnt) + " iterations"); // Merge sections in the same classs. - forEachClass([&](size_t Begin, size_t End) { - if (End - Begin == 1) + forEachClass([&](size_t begin, size_t end) { + if (end - begin == 1) return; - log("Selected " + Chunks[Begin]->getDebugName()); - for (size_t I = Begin + 1; I < End; ++I) { - log(" Removed " + Chunks[I]->getDebugName()); - Chunks[Begin]->replace(Chunks[I]); + log("Selected " + chunks[begin]->getDebugName()); + for (size_t i = begin + 1; i < end; ++i) { + log(" Removed " + chunks[i]->getDebugName()); + chunks[begin]->replace(chunks[i]); } }); } // Entry point to ICF. -void doICF(ArrayRef Chunks) { ICF().run(Chunks); } +void doICF(ArrayRef chunks) { ICF().run(chunks); } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/ICF.h =================================================================== --- vendor/lld/dist/COFF/ICF.h (revision 351287) +++ vendor/lld/dist/COFF/ICF.h (revision 351288) @@ -1,26 +1,25 @@ //===- ICF.h --------------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #ifndef LLD_COFF_ICF_H #define LLD_COFF_ICF_H #include "lld/Common/LLVM.h" #include "llvm/ADT/ArrayRef.h" namespace lld { namespace coff { class Chunk; -void doICF(ArrayRef Chunks); +void doICF(ArrayRef chunks); } // namespace coff } // namespace lld #endif Index: vendor/lld/dist/COFF/InputFiles.cpp =================================================================== --- vendor/lld/dist/COFF/InputFiles.cpp (revision 351287) +++ vendor/lld/dist/COFF/InputFiles.cpp (revision 351288) @@ -1,590 +1,881 @@ //===- InputFiles.cpp -----------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Chunks.h" #include "Config.h" +#include "DebugTypes.h" #include "Driver.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" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/COFF.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Target/TargetOptions.h" #include #include #include using namespace llvm; using namespace llvm::COFF; +using namespace llvm::codeview; using namespace llvm::object; using namespace llvm::support::endian; using llvm::Triple; using llvm::support::ulittle32_t; namespace lld { namespace coff { -std::vector ObjFile::Instances; -std::vector ImportFile::Instances; -std::vector BitcodeFile::Instances; +std::vector ObjFile::instances; +std::vector ImportFile::instances; +std::vector 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, - Symbol *Source, Symbol *Target) { - if (auto *U = dyn_cast(Source)) { - if (U->WeakAlias && U->WeakAlias != Target) { +static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f, + Symbol *source, Symbol *target) { + if (auto *u = dyn_cast(source)) { + if (u->weakAlias && u->weakAlias != target) { // Weak aliases as produced by GCC are named in the form // .weak.., where is the name // of another symbol emitted near the weak symbol. // Just use the definition from the first object file that defined // this weak symbol. - if (Config->MinGW) + if (config->mingw) return; - Symtab->reportDuplicate(Source, F); + symtab->reportDuplicate(source, f); } - U->WeakAlias = Target; + u->weakAlias = target; } } -ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {} +ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {} void ArchiveFile::parse() { // Parse a MemoryBufferRef as an archive file. - File = CHECK(Archive::create(MB), this); + file = CHECK(Archive::create(mb), this); // Read the symbol table to construct Lazy objects. - for (const Archive::Symbol &Sym : File->symbols()) - Symtab->addLazy(this, Sym); + for (const Archive::Symbol &sym : file->symbols()) + symtab->addLazy(this, sym); } // 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(), - "could not get the member for symbol " + Sym->getName()); +void ArchiveFile::addMember(const Archive::Symbol *sym) { + const Archive::Child &c = + CHECK(sym->getMember(), + "could not get the member for symbol " + sym->getName()); // Return an empty buffer if we have already returned the same buffer. - if (!Seen.insert(C.getChildOffset()).second) + if (!seen.insert(c.getChildOffset()).second) return; - Driver->enqueueArchiveMember(C, Sym->getName(), getName()); + driver->enqueueArchiveMember(c, sym->getName(), getName()); } -std::vector getArchiveMembers(Archive *File) { - std::vector V; - Error Err = Error::success(); - for (const ErrorOr &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() + +std::vector getArchiveMembers(Archive *file) { + std::vector v; + Error err = Error::success(); + for (const ErrorOr &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); + v.push_back(mbref); } - if (Err) - fatal(File->getFileName() + - ": Archive::children failed: " + toString(std::move(Err))); - return V; + 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 Bin = CHECK(createBinary(MB), this); + std::unique_ptr bin = CHECK(createBinary(mb), this); - if (auto *Obj = dyn_cast(Bin.get())) { - Bin.release(); - COFFObj.reset(Obj); + if (auto *obj = dyn_cast(bin.get())) { + bin.release(); + coffObj.reset(obj); } else { fatal(toString(this) + " is not a COFF file"); } // Read section and symbol tables. initializeChunks(); initializeSymbols(); + initializeFlags(); + initializeDependencies(); } +const coff_section* ObjFile::getSection(uint32_t i) { + const coff_section *sec; + if (auto ec = coffObj->getSection(i, sec)) + fatal("getSection failed: #" + Twine(i) + ": " + ec.message()); + return sec; +} + // 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(1); +static SectionChunk *const pendingComdat = reinterpret_cast(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; - if (auto EC = COFFObj->getSection(I, Sec)) - fatal("getSection failed: #" + Twine(I) + ": " + EC.message()); - - if (Sec->Characteristics & IMAGE_SCN_LNK_COMDAT) - SparseChunks[I] = PendingComdat; + 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 = getSection(i); + if (sec->Characteristics & IMAGE_SCN_LNK_COMDAT) + sparseChunks[i] = pendingComdat; else - SparseChunks[I] = readSection(I, nullptr, ""); + sparseChunks[i] = readSection(i, nullptr, ""); } } -SectionChunk *ObjFile::readSection(uint32_t SectionNumber, - const coff_aux_section_definition *Def, - StringRef LeaderName) { - const coff_section *Sec; - if (auto EC = COFFObj->getSection(SectionNumber, Sec)) - fatal("getSection failed: #" + Twine(SectionNumber) + ": " + EC.message()); +SectionChunk *ObjFile::readSection(uint32_t sectionNumber, + const coff_aux_section_definition *def, + StringRef leaderName) { + const coff_section *sec = getSection(sectionNumber); - StringRef Name; - if (auto EC = COFFObj->getSectionName(Sec, Name)) - fatal("getSectionName failed: #" + Twine(SectionNumber) + ": " + - EC.message()); + StringRef name; + if (Expected e = coffObj->getSectionName(sec)) + name = *e; + else + fatal("getSectionName failed: #" + Twine(sectionNumber) + ": " + + toString(e.takeError())); - if (Name == ".drectve") { - ArrayRef Data; - COFFObj->getSectionContents(Sec, Data); - Directives = std::string((const char *)Data.data(), Data.size()); + if (name == ".drectve") { + ArrayRef data; + cantFail(coffObj->getSectionContents(sec, data)); + directives = StringRef((const char *)data.data(), data.size()); return nullptr; } - if (Name == ".llvm_addrsig") { - AddrsigSec = Sec; + if (name == ".llvm_addrsig") { + addrsigSec = sec; return nullptr; } // 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 linker support. We need to interpret debug info, // and then write it to a separate .pdb file. // Ignore DWARF debug info unless /debug is given. - if (!Config->Debug && Name.startswith(".debug_")) + if (!config->debug && name.startswith(".debug_")) return nullptr; - if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE) + if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE) return nullptr; - auto *C = make(this, Sec); - if (Def) - C->Checksum = Def->CheckSum; + auto *c = make(this, sec); + if (def) + c->checksum = def->CheckSum; + // link.exe uses the presence of .rsrc$01 for LNK4078, so match that. + if (name == ".rsrc$01") + isResourceObjFile = true; + // 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 if (Config->GuardCF != GuardCFLevel::Off && Name == ".gfids$y") - GuardFidChunks.push_back(C); - else if (Config->GuardCF != GuardCFLevel::Off && Name == ".gljmp$y") - GuardLJmpChunks.push_back(C); - else if (Name == ".sxdata") - SXDataChunks.push_back(C); - else if (Config->TailMerge && Sec->NumberOfRelocations == 0 && - Name == ".rdata" && LeaderName.startswith("??_C@")) + if (c->isCodeView()) + debugChunks.push_back(c); + else if (name == ".gfids$y") + guardFidChunks.push_back(c); + else if (name == ".gljmp$y") + guardLJmpChunks.push_back(c); + else if (name == ".sxdata") + sXDataChunks.push_back(c); + else if (config->tailMerge && sec->NumberOfRelocations == 0 && + name == ".rdata" && leaderName.startswith("??_C@")) // COFF sections that look like string literal sections (i.e. no // relocations, in .rdata, leader symbol name matches the MSVC name mangling // for string literals) are subject to string tail merging. - MergeChunk::addSection(C); + MergeChunk::addSection(c); else - Chunks.push_back(C); + chunks.push_back(c); - return C; + return c; } void ObjFile::readAssociativeDefinition( - COFFSymbolRef Sym, const coff_aux_section_definition *Def) { - readAssociativeDefinition(Sym, Def, Def->getNumber(Sym.isBigObj())); + COFFSymbolRef sym, const coff_aux_section_definition *def) { + readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj())); } -void ObjFile::readAssociativeDefinition(COFFSymbolRef Sym, - const coff_aux_section_definition *Def, - uint32_t ParentSection) { - SectionChunk *Parent = SparseChunks[ParentSection]; +void ObjFile::readAssociativeDefinition(COFFSymbolRef sym, + const coff_aux_section_definition *def, + uint32_t parentIndex) { + SectionChunk *parent = sparseChunks[parentIndex]; + int32_t sectionNumber = sym.getSectionNumber(); - // 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) + auto diag = [&]() { + StringRef name, parentName; + coffObj->getSymbolName(sym, name); + + const coff_section *parentSec = getSection(parentIndex); + if (Expected e = coffObj->getSectionName(parentSec)) + parentName = *e; + error(toString(this) + ": associative comdat " + name + " (sec " + + Twine(sectionNumber) + ") has invalid reference to section " + + parentName + " (sec " + Twine(parentIndex) + ")"); + }; + + if (parent == pendingComdat) { + // This can happen if an associative comdat refers to another associative + // comdat that appears after it (invalid per COFF spec) or to a section + // without any symbols. + diag(); 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]); + if (parent) { + SectionChunk *c = readSection(sectionNumber, def, ""); + sparseChunks[sectionNumber] = c; + if (c) { + c->selection = IMAGE_COMDAT_SELECT_ASSOCIATIVE; + parent->addAssociative(c); + } } else { - SparseChunks[SectionNumber] = nullptr; + sparseChunks[sectionNumber] = nullptr; } } void ObjFile::recordPrevailingSymbolForMingw( - COFFSymbolRef Sym, DenseMap &PrevailingSectionMap) { + COFFSymbolRef sym, DenseMap &prevailingSectionMap) { // For comdat symbols in executable sections, where this is the copy // of the section chunk we actually include instead of discarding it, // add the symbol to a map to allow using it for implicitly // associating .[px]data$ sections to it. - int32_t SectionNumber = Sym.getSectionNumber(); - SectionChunk *SC = SparseChunks[SectionNumber]; - if (SC && SC->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) { - StringRef Name; - COFFObj->getSymbolName(Sym, Name); - PrevailingSectionMap[Name] = SectionNumber; + int32_t sectionNumber = sym.getSectionNumber(); + SectionChunk *sc = sparseChunks[sectionNumber]; + if (sc && sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) { + StringRef name; + coffObj->getSymbolName(sym, name); + if (getMachineType() == I386) + name.consume_front("_"); + prevailingSectionMap[name] = sectionNumber; } } void ObjFile::maybeAssociateSEHForMingw( - COFFSymbolRef Sym, const coff_aux_section_definition *Def, - const DenseMap &PrevailingSectionMap) { - StringRef Name; - COFFObj->getSymbolName(Sym, Name); - if (Name.consume_front(".pdata$") || Name.consume_front(".xdata$")) { - // For MinGW, treat .[px]data$ as implicitly associative to - // the symbol . - auto ParentSym = PrevailingSectionMap.find(Name); - if (ParentSym != PrevailingSectionMap.end()) - readAssociativeDefinition(Sym, Def, ParentSym->second); + COFFSymbolRef sym, const coff_aux_section_definition *def, + const DenseMap &prevailingSectionMap) { + StringRef name; + coffObj->getSymbolName(sym, name); + if (name.consume_front(".pdata$") || name.consume_front(".xdata$") || + name.consume_front(".eh_frame$")) { + // For MinGW, treat .[px]data$ and .eh_frame$ as implicitly + // associative to the symbol . + auto parentSym = prevailingSectionMap.find(name); + if (parentSym != prevailingSectionMap.end()) + readAssociativeDefinition(sym, def, parentSym->second); } } -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); +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); // For MinGW symbols named .weak.* that point to a discarded section, // don't create an Undefined symbol. If nothing ever refers to the symbol, // everything should be fine. If something actually refers to the symbol // (e.g. the undefined weak alias), linking will fail due to undefined // references at the end. - if (Config->MinGW && Name.startswith(".weak.")) + if (config->mingw && name.startswith(".weak.")) return nullptr; - return Symtab->addUndefined(Name, this, false); + return symtab->addUndefined(name, this, false); } - if (SC) + if (sc) return make(this, /*Name*/ "", /*IsCOMDAT*/ false, - /*IsExternal*/ false, Sym.getGeneric(), SC); + /*IsExternal*/ false, sym.getGeneric(), sc); return nullptr; } void ObjFile::initializeSymbols() { - uint32_t NumSymbols = COFFObj->getNumberOfSymbols(); - Symbols.resize(NumSymbols); + uint32_t numSymbols = coffObj->getNumberOfSymbols(); + symbols.resize(numSymbols); - SmallVector, 8> WeakAliases; - std::vector PendingIndexes; - PendingIndexes.reserve(NumSymbols); + SmallVector, 8> weakAliases; + std::vector pendingIndexes; + pendingIndexes.reserve(numSymbols); - DenseMap PrevailingSectionMap; - std::vector ComdatDefs( - COFFObj->getNumberOfSections() + 1); + DenseMap prevailingSectionMap; + std::vector comdatDefs( + coffObj->getNumberOfSections() + 1); - for (uint32_t I = 0; I < NumSymbols; ++I) { - COFFSymbolRef COFFSym = check(COFFObj->getSymbol(I)); - bool PrevailingComdat; - if (COFFSym.isUndefined()) { - Symbols[I] = createUndefined(COFFSym); - } else if (COFFSym.isWeakExternal()) { - Symbols[I] = createUndefined(COFFSym); - uint32_t TagIndex = COFFSym.getAux()->TagIndex; - WeakAliases.emplace_back(Symbols[I], TagIndex); - } else if (Optional OptSym = - createDefined(COFFSym, ComdatDefs, PrevailingComdat)) { - Symbols[I] = *OptSym; - if (Config->MinGW && PrevailingComdat) - recordPrevailingSymbolForMingw(COFFSym, PrevailingSectionMap); + for (uint32_t i = 0; i < numSymbols; ++i) { + COFFSymbolRef coffSym = check(coffObj->getSymbol(i)); + bool prevailingComdat; + if (coffSym.isUndefined()) { + symbols[i] = createUndefined(coffSym); + } else if (coffSym.isWeakExternal()) { + symbols[i] = createUndefined(coffSym); + uint32_t tagIndex = coffSym.getAux()->TagIndex; + weakAliases.emplace_back(symbols[i], tagIndex); + } else if (Optional optSym = + createDefined(coffSym, comdatDefs, prevailingComdat)) { + symbols[i] = *optSym; + if (config->mingw && prevailingComdat) + recordPrevailingSymbolForMingw(coffSym, prevailingSectionMap); } else { // 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. + // 2) symbol belongs to a comdat section associated with another section. // 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); + pendingIndexes.push_back(i); } - I += COFFSym.getNumberOfAuxSymbols(); + i += coffSym.getNumberOfAuxSymbols(); } - for (uint32_t I : PendingIndexes) { - COFFSymbolRef Sym = check(COFFObj->getSymbol(I)); - if (const coff_aux_section_definition *Def = Sym.getSectionDefinition()) { - if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) - readAssociativeDefinition(Sym, Def); - else if (Config->MinGW) - maybeAssociateSEHForMingw(Sym, Def, PrevailingSectionMap); + for (uint32_t i : pendingIndexes) { + COFFSymbolRef sym = check(coffObj->getSymbol(i)); + if (const coff_aux_section_definition *def = sym.getSectionDefinition()) { + if (def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) + readAssociativeDefinition(sym, def); + else if (config->mingw) + maybeAssociateSEHForMingw(sym, def, prevailingSectionMap); } - if (SparseChunks[Sym.getSectionNumber()] == PendingComdat) { - StringRef Name; - COFFObj->getSymbolName(Sym, Name); - log("comdat section " + Name + + if (sparseChunks[sym.getSectionNumber()] == pendingComdat) { + StringRef name; + coffObj->getSymbolName(sym, name); + log("comdat section " + name + " without leader and unassociated, discarding"); continue; } - Symbols[I] = createRegular(Sym); + symbols[i] = createRegular(sym); } - for (auto &KV : WeakAliases) { - Symbol *Sym = KV.first; - uint32_t Idx = KV.second; - checkAndSetWeakAlias(Symtab, this, Sym, Symbols[Idx]); + for (auto &kv : weakAliases) { + Symbol *sym = kv.first; + uint32_t idx = kv.second; + checkAndSetWeakAlias(symtab, this, sym, symbols[idx]); } } -Symbol *ObjFile::createUndefined(COFFSymbolRef Sym) { - StringRef Name; - COFFObj->getSymbolName(Sym, Name); - return Symtab->addUndefined(Name, this, Sym.isWeakExternal()); +Symbol *ObjFile::createUndefined(COFFSymbolRef sym) { + StringRef name; + coffObj->getSymbolName(sym, name); + return symtab->addUndefined(name, this, sym.isWeakExternal()); } +void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection, + bool &prevailing, DefinedRegular *leader) { + if (prevailing) + return; + // There's already an existing comdat for this symbol: `Leader`. + // Use the comdats's selection field to determine if the new + // symbol in `Sym` should be discarded, produce a duplicate symbol + // error, etc. + + SectionChunk *leaderChunk = nullptr; + COMDATType leaderSelection = IMAGE_COMDAT_SELECT_ANY; + + if (leader->data) { + leaderChunk = leader->getChunk(); + leaderSelection = leaderChunk->selection; + } else { + // FIXME: comdats from LTO files don't know their selection; treat them + // as "any". + selection = leaderSelection; + } + + if ((selection == IMAGE_COMDAT_SELECT_ANY && + leaderSelection == IMAGE_COMDAT_SELECT_LARGEST) || + (selection == IMAGE_COMDAT_SELECT_LARGEST && + leaderSelection == IMAGE_COMDAT_SELECT_ANY)) { + // cl.exe picks "any" for vftables when building with /GR- and + // "largest" when building with /GR. To be able to link object files + // compiled with each flag, "any" and "largest" are merged as "largest". + leaderSelection = selection = IMAGE_COMDAT_SELECT_LARGEST; + } + + // Other than that, comdat selections must match. This is a bit more + // strict than link.exe which allows merging "any" and "largest" if "any" + // is the first symbol the linker sees, and it allows merging "largest" + // with everything (!) if "largest" is the first symbol the linker sees. + // Making this symmetric independent of which selection is seen first + // seems better though. + // (This behavior matches ModuleLinker::getComdatResult().) + if (selection != leaderSelection) { + log(("conflicting comdat type for " + toString(*leader) + ": " + + Twine((int)leaderSelection) + " in " + toString(leader->getFile()) + + " and " + Twine((int)selection) + " in " + toString(this)) + .str()); + symtab->reportDuplicate(leader, this); + return; + } + + switch (selection) { + case IMAGE_COMDAT_SELECT_NODUPLICATES: + symtab->reportDuplicate(leader, this); + break; + + case IMAGE_COMDAT_SELECT_ANY: + // Nothing to do. + break; + + case IMAGE_COMDAT_SELECT_SAME_SIZE: + if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) + symtab->reportDuplicate(leader, this); + break; + + case IMAGE_COMDAT_SELECT_EXACT_MATCH: { + SectionChunk newChunk(this, getSection(sym)); + // link.exe only compares section contents here and doesn't complain + // if the two comdat sections have e.g. different alignment. + // Match that. + if (leaderChunk->getContents() != newChunk.getContents()) + symtab->reportDuplicate(leader, this); + break; + } + + case IMAGE_COMDAT_SELECT_ASSOCIATIVE: + // createDefined() is never called for IMAGE_COMDAT_SELECT_ASSOCIATIVE. + // (This means lld-link doesn't produce duplicate symbol errors for + // associative comdats while link.exe does, but associate comdats + // are never extern in practice.) + llvm_unreachable("createDefined not called for associative comdats"); + + case IMAGE_COMDAT_SELECT_LARGEST: + if (leaderChunk->getSize() < getSection(sym)->SizeOfRawData) { + // Replace the existing comdat symbol with the new one. + StringRef name; + coffObj->getSymbolName(sym, name); + // FIXME: This is incorrect: With /opt:noref, the previous sections + // make it into the final executable as well. Correct handling would + // be to undo reading of the whole old section that's being replaced, + // or doing one pass that determines what the final largest comdat + // is for all IMAGE_COMDAT_SELECT_LARGEST comdats and then reading + // only the largest one. + replaceSymbol(leader, this, name, /*IsCOMDAT*/ true, + /*IsExternal*/ true, sym.getGeneric(), + nullptr); + prevailing = true; + } + break; + + case IMAGE_COMDAT_SELECT_NEWEST: + llvm_unreachable("should have been rejected earlier"); + } +} + Optional ObjFile::createDefined( - COFFSymbolRef Sym, - std::vector &ComdatDefs, - bool &Prevailing) { - Prevailing = false; - auto GetName = [&]() { - StringRef S; - COFFObj->getSymbolName(Sym, S); - return S; + COFFSymbolRef sym, + std::vector &comdatDefs, + bool &prevailing) { + prevailing = false; + auto getName = [&]() { + StringRef s; + coffObj->getSymbolName(sym, s); + return s; }; - if (Sym.isCommon()) { - auto *C = make(Sym); - Chunks.push_back(C); - return Symtab->addCommon(this, GetName(), Sym.getValue(), Sym.getGeneric(), - C); + if (sym.isCommon()) { + auto *c = make(sym); + chunks.push_back(c); + return symtab->addCommon(this, getName(), sym.getValue(), sym.getGeneric(), + c); } - if (Sym.isAbsolute()) { - StringRef Name = GetName(); + if (sym.isAbsolute()) { + StringRef name = getName(); // Skip special symbols. - if (Name == "@comp.id") + if (name == "@comp.id") return nullptr; - if (Name == "@feat.00") { - Feat00Flags = Sym.getValue(); + if (name == "@feat.00") { + feat00Flags = sym.getValue(); return nullptr; } - if (Sym.isExternal()) - return Symtab->addAbsolute(Name, Sym); - return make(Name, Sym); + if (sym.isExternal()) + return symtab->addAbsolute(name, sym); + return make(name, sym); } - int32_t SectionNumber = Sym.getSectionNumber(); - if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG) + int32_t sectionNumber = sym.getSectionNumber(); + if (sectionNumber == llvm::COFF::IMAGE_SYM_DEBUG) return nullptr; - if (llvm::COFF::isReservedSectionNumber(SectionNumber)) - fatal(toString(this) + ": " + GetName() + - " should not refer to special section " + Twine(SectionNumber)); + if (llvm::COFF::isReservedSectionNumber(sectionNumber)) + fatal(toString(this) + ": " + getName() + + " should not refer to special section " + Twine(sectionNumber)); - if ((uint32_t)SectionNumber >= SparseChunks.size()) - fatal(toString(this) + ": " + GetName() + - " should not refer to non-existent section " + Twine(SectionNumber)); + if ((uint32_t)sectionNumber >= sparseChunks.size()) + fatal(toString(this) + ": " + getName() + + " should not refer to non-existent section " + Twine(sectionNumber)); - // Handle comdat leader symbols. - if (const coff_aux_section_definition *Def = ComdatDefs[SectionNumber]) { - ComdatDefs[SectionNumber] = nullptr; - Symbol *Leader; - if (Sym.isExternal()) { - std::tie(Leader, Prevailing) = - Symtab->addComdat(this, GetName(), Sym.getGeneric()); + // Comdat handling. + // A comdat symbol consists of two symbol table entries. + // The first symbol entry has the name of the section (e.g. .text), fixed + // values for the other fields, and one auxilliary record. + // The second symbol entry has the name of the comdat symbol, called the + // "comdat leader". + // When this function is called for the first symbol entry of a comdat, + // it sets comdatDefs and returns None, and when it's called for the second + // symbol entry it reads comdatDefs and then sets it back to nullptr. + + // Handle comdat leader. + if (const coff_aux_section_definition *def = comdatDefs[sectionNumber]) { + comdatDefs[sectionNumber] = nullptr; + DefinedRegular *leader; + + if (sym.isExternal()) { + std::tie(leader, prevailing) = + symtab->addComdat(this, getName(), sym.getGeneric()); } else { - Leader = make(this, /*Name*/ "", /*IsCOMDAT*/ false, - /*IsExternal*/ false, Sym.getGeneric()); - Prevailing = true; + leader = make(this, /*Name*/ "", /*IsCOMDAT*/ false, + /*IsExternal*/ false, sym.getGeneric()); + prevailing = true; } - if (Prevailing) { - SectionChunk *C = readSection(SectionNumber, Def, GetName()); - SparseChunks[SectionNumber] = C; - C->Sym = cast(Leader); - cast(Leader)->Data = &C->Repl; + if (def->Selection < (int)IMAGE_COMDAT_SELECT_NODUPLICATES || + // Intentionally ends at IMAGE_COMDAT_SELECT_LARGEST: link.exe + // doesn't understand IMAGE_COMDAT_SELECT_NEWEST either. + def->Selection > (int)IMAGE_COMDAT_SELECT_LARGEST) { + fatal("unknown comdat type " + std::to_string((int)def->Selection) + + " for " + getName() + " in " + toString(this)); + } + COMDATType selection = (COMDATType)def->Selection; + + if (leader->isCOMDAT) + handleComdatSelection(sym, selection, prevailing, leader); + + if (prevailing) { + SectionChunk *c = readSection(sectionNumber, def, getName()); + sparseChunks[sectionNumber] = c; + c->sym = cast(leader); + c->selection = selection; + cast(leader)->data = &c->repl; } else { - SparseChunks[SectionNumber] = nullptr; + sparseChunks[sectionNumber] = nullptr; } - return Leader; + return leader; } - // 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 (const coff_aux_section_definition *Def = Sym.getSectionDefinition()) { - if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) - readAssociativeDefinition(Sym, Def); - else - ComdatDefs[SectionNumber] = Def; + // 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 (const coff_aux_section_definition *def = sym.getSectionDefinition()) { + if (def->Selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE) + comdatDefs[sectionNumber] = def; } + return None; } - // readAssociativeDefinition() writes to SparseChunks, so need to check again. - if (SparseChunks[SectionNumber] == PendingComdat) - return None; - - return createRegular(Sym); + return createRegular(sym); } MachineTypes ObjFile::getMachineType() { - if (COFFObj) - return static_cast(COFFObj->getMachine()); + if (coffObj) + return static_cast(coffObj->getMachine()); return IMAGE_FILE_MACHINE_UNKNOWN; } -StringRef ltrim1(StringRef S, const char *Chars) { - if (!S.empty() && strchr(Chars, S[0])) - return S.substr(1); - return S; +ArrayRef ObjFile::getDebugSection(StringRef secName) { + if (SectionChunk *sec = SectionChunk::findByName(debugChunks, secName)) + return sec->consumeDebugMagic(); + return {}; } +// OBJ files systematically store critical informations in a .debug$S stream, +// even if the TU was compiled with no debug info. At least two records are +// always there. S_OBJNAME stores a 32-bit signature, which is loaded into the +// PCHSignature member. S_COMPILE3 stores compile-time cmd-line flags. This is +// currently used to initialize the hotPatchable member. +void ObjFile::initializeFlags() { + ArrayRef data = getDebugSection(".debug$S"); + if (data.empty()) + return; + + DebugSubsectionArray subsections; + + BinaryStreamReader reader(data, support::little); + ExitOnError exitOnErr; + exitOnErr(reader.readArray(subsections, data.size())); + + for (const DebugSubsectionRecord &ss : subsections) { + if (ss.kind() != DebugSubsectionKind::Symbols) + continue; + + unsigned offset = 0; + + // Only parse the first two records. We are only looking for S_OBJNAME + // and S_COMPILE3, and they usually appear at the beginning of the + // stream. + for (unsigned i = 0; i < 2; ++i) { + Expected sym = readSymbolFromStream(ss.getRecordData(), offset); + if (!sym) { + consumeError(sym.takeError()); + return; + } + if (sym->kind() == SymbolKind::S_COMPILE3) { + auto cs = + cantFail(SymbolDeserializer::deserializeAs(sym.get())); + hotPatchable = + (cs.Flags & CompileSym3Flags::HotPatch) != CompileSym3Flags::None; + } + if (sym->kind() == SymbolKind::S_OBJNAME) { + auto objName = cantFail(SymbolDeserializer::deserializeAs( + sym.get())); + pchSignature = objName.Signature; + } + offset += sym->length(); + } + } +} + +// Depending on the compilation flags, OBJs can refer to external files, +// necessary to merge this OBJ into the final PDB. We currently support two +// types of external files: Precomp/PCH OBJs, when compiling with /Yc and /Yu. +// And PDB type servers, when compiling with /Zi. This function extracts these +// dependencies and makes them available as a TpiSource interface (see +// DebugTypes.h). Both cases only happen with cl.exe: clang-cl produces regular +// output even with /Yc and /Yu and with /Zi. +void ObjFile::initializeDependencies() { + if (!config->debug) + return; + + bool isPCH = false; + + ArrayRef data = getDebugSection(".debug$P"); + if (!data.empty()) + isPCH = true; + else + data = getDebugSection(".debug$T"); + + if (data.empty()) + return; + + CVTypeArray types; + BinaryStreamReader reader(data, support::little); + cantFail(reader.readArray(types, reader.getLength())); + + CVTypeArray::Iterator firstType = types.begin(); + if (firstType == types.end()) + return; + + debugTypes.emplace(types); + + if (isPCH) { + debugTypesObj = makePrecompSource(this); + return; + } + + if (firstType->kind() == LF_TYPESERVER2) { + TypeServer2Record ts = cantFail( + TypeDeserializer::deserializeAs(firstType->data())); + debugTypesObj = makeUseTypeServerSource(this, &ts); + return; + } + + if (firstType->kind() == LF_PRECOMP) { + PrecompRecord precomp = cantFail( + TypeDeserializer::deserializeAs(firstType->data())); + debugTypesObj = makeUsePrecompSource(this, &precomp); + return; + } + + debugTypesObj = makeTpiSource(this); +} + +StringRef ltrim1(StringRef s, const char *chars) { + if (!s.empty() && strchr(chars, s[0])) + return s.substr(1); + return s; +} + void ImportFile::parse() { - const char *Buf = MB.getBufferStart(); - const char *End = MB.getBufferEnd(); - const auto *Hdr = reinterpret_cast(Buf); + const char *buf = mb.getBufferStart(); + const auto *hdr = reinterpret_cast(buf); // Check if the total size is valid. - if ((size_t)(End - Buf) != (sizeof(*Hdr) + Hdr->SizeOfData)) + if (mb.getBufferSize() != sizeof(*hdr) + hdr->SizeOfData) fatal("broken import library"); // Read names and create an __imp_ symbol. - StringRef Name = Saver.save(StringRef(Buf + sizeof(*Hdr))); - StringRef ImpName = Saver.save("__imp_" + Name); - const char *NameStart = Buf + sizeof(coff_import_header) + Name.size() + 1; - DLLName = StringRef(NameStart); - StringRef ExtName; - switch (Hdr->getNameType()) { + StringRef name = saver.save(StringRef(buf + sizeof(*hdr))); + StringRef impName = saver.save("__imp_" + name); + const char *nameStart = buf + sizeof(coff_import_header) + name.size() + 1; + dllName = StringRef(nameStart); + StringRef extName; + switch (hdr->getNameType()) { case IMPORT_ORDINAL: - ExtName = ""; + extName = ""; break; case IMPORT_NAME: - ExtName = Name; + extName = name; break; case IMPORT_NAME_NOPREFIX: - ExtName = ltrim1(Name, "?@_"); + extName = ltrim1(name, "?@_"); break; case IMPORT_NAME_UNDECORATE: - ExtName = ltrim1(Name, "?@_"); - ExtName = ExtName.substr(0, ExtName.find('@')); + extName = ltrim1(name, "?@_"); + extName = extName.substr(0, extName.find('@')); break; } - this->Hdr = Hdr; - ExternalName = ExtName; + this->hdr = hdr; + externalName = extName; - ImpSym = Symtab->addImportData(ImpName, this); + impSym = symtab->addImportData(impName, this); // If this was a duplicate, we logged an error but may continue; - // in this case, ImpSym is nullptr. - if (!ImpSym) + // in this case, impSym is nullptr. + if (!impSym) return; - if (Hdr->getType() == llvm::COFF::IMPORT_CONST) - static_cast(Symtab->addImportData(Name, this)); + if (hdr->getType() == llvm::COFF::IMPORT_CONST) + static_cast(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) - ThunkSym = Symtab->addImportThunk( - Name, cast_or_null(ImpSym), Hdr->Machine); + if (hdr->getType() == llvm::COFF::IMPORT_CODE) + thunkSym = symtab->addImportThunk( + name, cast_or_null(impSym), hdr->Machine); } +BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive) + : InputFile(BitcodeKind, mb) { + std::string path = mb.getBufferIdentifier().str(); + if (config->thinLTOIndexOnly) + path = replaceThinLTOSuffix(mb.getBufferIdentifier()); + + // ThinLTO assumes that all MemoryBufferRefs given to it have a unique + // name. If two archives define two members with the same name, this + // causes a collision which result in only one of the objects being taken + // into consideration at LTO time (which very likely causes undefined + // symbols later in the link stage). So we append file offset to make + // filename unique. + MemoryBufferRef mbref( + mb.getBuffer(), + saver.save(archiveName + path + + (archiveName.empty() ? "" : utostr(offsetInArchive)))); + + obj = check(lto::InputFile::create(mbref)); +} + void BitcodeFile::parse() { - Obj = check(lto::InputFile::create(MemoryBufferRef( - MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier())))); - std::vector> 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); - } else if (ObjSym.isCommon()) { - Sym = Symtab->addCommon(this, SymName, ObjSym.getCommonSize()); - } else if (ObjSym.isWeak() && ObjSym.isIndirect()) { + std::vector> comdat(obj->getComdatTable().size()); + for (size_t i = 0; i != obj->getComdatTable().size(); ++i) + // FIXME: lto::InputFile doesn't keep enough data to do correct comdat + // selection handling. + 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); + } else if (objSym.isCommon()) { + sym = symtab->addCommon(this, symName, objSym.getCommonSize()); + } else if (objSym.isWeak() && objSym.isIndirect()) { // Weak external. - Sym = Symtab->addUndefined(SymName, this, true); - std::string Fallback = ObjSym.getCOFFWeakExternalFallback(); - 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); + sym = symtab->addUndefined(symName, this, true); + std::string fallback = objSym.getCOFFWeakExternalFallback(); + 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); + sym = symtab->addUndefined(symName, this, false); } else { - Sym = Symtab->addRegular(this, SymName); + sym = symtab->addRegular(this, symName); } - Symbols.push_back(Sym); + symbols.push_back(sym); + if (objSym.isUsed()) + config->gcroot.push_back(sym); } - Directives = Obj->getCOFFLinkerOpts(); + directives = obj->getCOFFLinkerOpts(); } MachineTypes BitcodeFile::getMachineType() { - switch (Triple(Obj->getTargetTriple()).getArch()) { + switch (Triple(obj->getTargetTriple()).getArch()) { case Triple::x86_64: return AMD64; case Triple::x86: return I386; case Triple::arm: return ARMNT; case Triple::aarch64: return ARM64; default: return IMAGE_FILE_MACHINE_UNKNOWN; } } + +std::string replaceThinLTOSuffix(StringRef path) { + StringRef suffix = config->thinLTOObjectSuffixReplace.first; + StringRef repl = config->thinLTOObjectSuffixReplace.second; + + if (path.consume_back(suffix)) + return (path + repl).str(); + return path; +} } // namespace coff } // namespace lld // Returns the last element of a path, which is supposed to be a filename. -static StringRef getBasename(StringRef Path) { - return sys::path::filename(Path, sys::path::Style::windows); +static StringRef getBasename(StringRef path) { + return sys::path::filename(path, sys::path::Style::windows); } // Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)". -std::string lld::toString(const coff::InputFile *File) { - if (!File) +std::string lld::toString(const coff::InputFile *file) { + if (!file) return ""; - if (File->ParentName.empty()) - return File->getName(); + if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind) + return file->getName(); - return (getBasename(File->ParentName) + "(" + getBasename(File->getName()) + + return (getBasename(file->parentName) + "(" + getBasename(file->getName()) + ")") .str(); } Index: vendor/lld/dist/COFF/InputFiles.h =================================================================== --- vendor/lld/dist/COFF/InputFiles.h (revision 351287) +++ vendor/lld/dist/COFF/InputFiles.h (revision 351288) @@ -1,280 +1,321 @@ //===- InputFiles.h ---------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #ifndef LLD_COFF_INPUT_FILES_H #define LLD_COFF_INPUT_FILES_H #include "Config.h" #include "lld/Common/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Support/StringSaver.h" #include #include #include namespace llvm { namespace pdb { class DbiModuleDescriptorBuilder; } } namespace lld { namespace coff { -std::vector getArchiveMembers(llvm::object::Archive *File); +std::vector getArchiveMembers(llvm::object::Archive *file); using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; using llvm::COFF::MachineTypes; using llvm::object::Archive; using llvm::object::COFFObjectFile; using llvm::object::COFFSymbolRef; using llvm::object::coff_import_header; using llvm::object::coff_section; class Chunk; class Defined; class DefinedImportData; class DefinedImportThunk; +class DefinedRegular; class Lazy; class SectionChunk; class Symbol; class Undefined; +class TpiSource; // The root class of input files. class InputFile { public: enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind }; - Kind kind() const { return FileKind; } + Kind kind() const { return fileKind; } virtual ~InputFile() {} // Returns the filename. - StringRef getName() const { return MB.getBufferIdentifier(); } + StringRef getName() const { return mb.getBufferIdentifier(); } // Reads a file (the constructor doesn't do that). virtual void parse() = 0; // Returns the CPU type this file was compiled to. virtual MachineTypes getMachineType() { return IMAGE_FILE_MACHINE_UNKNOWN; } - MemoryBufferRef MB; + MemoryBufferRef mb; // An archive file name if this file is created from an archive. - StringRef ParentName; + StringRef parentName; // Returns .drectve section contents if exist. - StringRef getDirectives() { return StringRef(Directives).trim(); } + StringRef getDirectives() { return directives; } protected: - InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {} + InputFile(Kind k, MemoryBufferRef m) : mb(m), fileKind(k) {} - std::string Directives; + StringRef directives; private: - const Kind FileKind; + const Kind fileKind; }; // .lib or .a file. class ArchiveFile : public InputFile { public: - explicit ArchiveFile(MemoryBufferRef M); - static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; } + explicit ArchiveFile(MemoryBufferRef m); + static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; } void parse() override; // Enqueues an archive member load for the given symbol. If we've already // enqueued a load for the same archive member, this function does nothing, // which ensures that we don't load the same member more than once. - void addMember(const Archive::Symbol *Sym); + void addMember(const Archive::Symbol *sym); private: - std::unique_ptr File; - std::string Filename; - llvm::DenseSet Seen; + std::unique_ptr file; + llvm::DenseSet seen; }; // .obj or .o file. This may be a member of an archive file. class ObjFile : public InputFile { public: - explicit ObjFile(MemoryBufferRef M) : InputFile(ObjectKind, M) {} - static bool classof(const InputFile *F) { return F->kind() == ObjectKind; } + explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {} + static bool classof(const InputFile *f) { return f->kind() == ObjectKind; } void parse() override; MachineTypes getMachineType() override; - ArrayRef getChunks() { return Chunks; } - ArrayRef getDebugChunks() { return DebugChunks; } - ArrayRef getSXDataChunks() { return SXDataChunks; } - ArrayRef getGuardFidChunks() { return GuardFidChunks; } - ArrayRef getGuardLJmpChunks() { return GuardLJmpChunks; } - ArrayRef getSymbols() { return Symbols; } + ArrayRef getChunks() { return chunks; } + ArrayRef getDebugChunks() { return debugChunks; } + ArrayRef getSXDataChunks() { return sXDataChunks; } + ArrayRef getGuardFidChunks() { return guardFidChunks; } + ArrayRef getGuardLJmpChunks() { return guardLJmpChunks; } + ArrayRef getSymbols() { return symbols; } - // Returns a Symbol object for the SymbolIndex'th symbol in the + ArrayRef getDebugSection(StringRef secName); + + // Returns a Symbol object for the symbolIndex'th symbol in the // underlying object file. - Symbol *getSymbol(uint32_t SymbolIndex) { - return Symbols[SymbolIndex]; + Symbol *getSymbol(uint32_t symbolIndex) { + return symbols[symbolIndex]; } // Returns the underlying COFF file. - COFFObjectFile *getCOFFObj() { return COFFObj.get(); } + COFFObjectFile *getCOFFObj() { return coffObj.get(); } - // Whether the object was already merged into the final PDB or not - bool wasProcessedForPDB() const { return !!ModuleDBI; } + // Add a symbol for a range extension thunk. Return the new symbol table + // index. This index can be used to modify a relocation. + uint32_t addRangeThunkSymbol(Symbol *thunk) { + symbols.push_back(thunk); + return symbols.size() - 1; + } - static std::vector Instances; + static std::vector instances; // Flags in the absolute @feat.00 symbol if it is present. These usually // indicate if an object was compiled with certain security features enabled // like stack guard, safeseh, /guard:cf, or other things. - uint32_t Feat00Flags = 0; + uint32_t feat00Flags = 0; // True if this object file is compatible with SEH. COFF-specific and // x86-only. COFF spec 5.10.1. The .sxdata section. - bool hasSafeSEH() { return Feat00Flags & 0x1; } + bool hasSafeSEH() { return feat00Flags & 0x1; } // True if this file was compiled with /guard:cf. - bool hasGuardCF() { return Feat00Flags & 0x800; } + bool hasGuardCF() { return feat00Flags & 0x800; } // Pointer to the PDB module descriptor builder. Various debug info records // will reference object files by "module index", which is here. Things like // source files and section contributions are also recorded here. Will be null // if we are not producing a PDB. - llvm::pdb::DbiModuleDescriptorBuilder *ModuleDBI = nullptr; + llvm::pdb::DbiModuleDescriptorBuilder *moduleDBI = nullptr; - const coff_section *AddrsigSec = nullptr; + const coff_section *addrsigSec = nullptr; // When using Microsoft precompiled headers, this is the PCH's key. // The same key is used by both the precompiled object, and objects using the // precompiled object. Any difference indicates out-of-date objects. - llvm::Optional PCHSignature; + llvm::Optional pchSignature; + // Whether this is an object file created from .res files. + bool isResourceObjFile = false; + + // Whether this file was compiled with /hotpatch. + bool hotPatchable = false; + + // Whether the object was already merged into the final PDB. + bool mergedIntoPDB = false; + + // If the OBJ has a .debug$T stream, this tells how it will be handled. + TpiSource *debugTypesObj = nullptr; + + // The .debug$T stream if there's one. + llvm::Optional debugTypes; + private: + const coff_section* getSection(uint32_t i); + const coff_section *getSection(COFFSymbolRef sym) { + return getSection(sym.getSectionNumber()); + } + void initializeChunks(); void initializeSymbols(); + void initializeFlags(); + void initializeDependencies(); SectionChunk * - readSection(uint32_t SectionNumber, - const llvm::object::coff_aux_section_definition *Def, - StringRef LeaderName); + readSection(uint32_t sectionNumber, + const llvm::object::coff_aux_section_definition *def, + StringRef leaderName); void readAssociativeDefinition( - COFFSymbolRef COFFSym, - const llvm::object::coff_aux_section_definition *Def); + COFFSymbolRef coffSym, + const llvm::object::coff_aux_section_definition *def); void readAssociativeDefinition( - COFFSymbolRef COFFSym, - const llvm::object::coff_aux_section_definition *Def, - uint32_t ParentSection); + COFFSymbolRef coffSym, + const llvm::object::coff_aux_section_definition *def, + uint32_t parentSection); void recordPrevailingSymbolForMingw( - COFFSymbolRef COFFSym, - llvm::DenseMap &PrevailingSectionMap); + COFFSymbolRef coffSym, + llvm::DenseMap &prevailingSectionMap); void maybeAssociateSEHForMingw( - COFFSymbolRef Sym, const llvm::object::coff_aux_section_definition *Def, - const llvm::DenseMap &PrevailingSectionMap); + COFFSymbolRef sym, const llvm::object::coff_aux_section_definition *def, + const llvm::DenseMap &prevailingSectionMap); + // Given a new symbol Sym with comdat selection Selection, if the new + // symbol is not (yet) Prevailing and the existing comdat leader set to + // Leader, emits a diagnostic if the new symbol and its selection doesn't + // match the existing symbol and its selection. If either old or new + // symbol have selection IMAGE_COMDAT_SELECT_LARGEST, Sym might replace + // the existing leader. In that case, Prevailing is set to true. + void handleComdatSelection(COFFSymbolRef sym, + llvm::COFF::COMDATType &selection, + bool &prevailing, DefinedRegular *leader); + llvm::Optional - createDefined(COFFSymbolRef Sym, + createDefined(COFFSymbolRef sym, std::vector - &ComdatDefs, - bool &PrevailingComdat); - Symbol *createRegular(COFFSymbolRef Sym); - Symbol *createUndefined(COFFSymbolRef Sym); + &comdatDefs, + bool &prevailingComdat); + Symbol *createRegular(COFFSymbolRef sym); + Symbol *createUndefined(COFFSymbolRef sym); - std::unique_ptr COFFObj; + std::unique_ptr coffObj; // List of all chunks defined by this file. This includes both section // chunks and non-section chunks for common symbols. - std::vector Chunks; + std::vector chunks; // CodeView debug info sections. - std::vector DebugChunks; + std::vector debugChunks; // Chunks containing symbol table indices of exception handlers. Only used for // 32-bit x86. - std::vector SXDataChunks; + std::vector sXDataChunks; // Chunks containing symbol table indices of address taken symbols and longjmp // targets. These are not linked into the final binary when /guard:cf is set. - std::vector GuardFidChunks; - std::vector GuardLJmpChunks; + std::vector guardFidChunks; + std::vector guardLJmpChunks; // This vector contains the same chunks as Chunks, but they are // indexed such that you can get a SectionChunk by section index. // Nonexistent section indices are filled with null pointers. // (Because section number is 1-based, the first slot is always a // null pointer.) - std::vector SparseChunks; + std::vector sparseChunks; // 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 Symbols; + std::vector symbols; }; // This type represents import library members that contain DLL names // and symbols exported from the DLLs. See Microsoft PE/COFF spec. 7 // for details about the format. class ImportFile : public InputFile { public: - explicit ImportFile(MemoryBufferRef M) : InputFile(ImportKind, M) {} + explicit ImportFile(MemoryBufferRef m) : InputFile(ImportKind, m) {} - static bool classof(const InputFile *F) { return F->kind() == ImportKind; } + static bool classof(const InputFile *f) { return f->kind() == ImportKind; } - static std::vector Instances; + static std::vector instances; - Symbol *ImpSym = nullptr; - Symbol *ThunkSym = nullptr; - std::string DLLName; + Symbol *impSym = nullptr; + Symbol *thunkSym = nullptr; + std::string dllName; private: void parse() override; public: - StringRef ExternalName; - const coff_import_header *Hdr; - Chunk *Location = nullptr; + StringRef externalName; + const coff_import_header *hdr; + Chunk *location = nullptr; // We want to eliminate dllimported symbols if no one actually refers them. // These "Live" bits are used to keep track of which import library members // are actually in use. // // If the Live bit is turned off by MarkLive, Writer will ignore dllimported // symbols provided by this import library member. We also track whether the // imported symbol is used separately from whether the thunk is used in order // to avoid creating unnecessary thunks. - bool Live = !Config->DoGC; - bool ThunkLive = !Config->DoGC; + bool live = !config->doGC; + bool thunkLive = !config->doGC; }; // Used for LTO. class BitcodeFile : public InputFile { public: - explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {} - static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; } - ArrayRef getSymbols() { return Symbols; } + BitcodeFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive); + static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } + ArrayRef getSymbols() { return symbols; } MachineTypes getMachineType() override; - static std::vector Instances; - std::unique_ptr Obj; + static std::vector instances; + std::unique_ptr obj; private: void parse() override; - std::vector Symbols; + std::vector symbols; }; + +std::string replaceThinLTOSuffix(StringRef path); } // namespace coff -std::string toString(const coff::InputFile *File); +std::string toString(const coff::InputFile *file); } // namespace lld #endif Index: vendor/lld/dist/COFF/LTO.cpp =================================================================== --- vendor/lld/dist/COFF/LTO.cpp (revision 351287) +++ vendor/lld/dist/COFF/LTO.cpp (revision 351288) @@ -1,152 +1,206 @@ //===- LTO.cpp ------------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "LTO.h" #include "Config.h" #include "InputFiles.h" #include "Symbols.h" +#include "lld/Common/Args.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Strings.h" #include "lld/Common/TargetOptionsCommandFlags.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/Bitcode/BitcodeWriter.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" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include using namespace llvm; using namespace llvm::object; using namespace lld; using namespace lld::coff; -static std::unique_ptr createLTO() { - lto::Config C; - C.Options = InitTargetOptionsFromCodeGenFlags(); +// Creates an empty file to and returns a raw_fd_ostream to write to it. +static std::unique_ptr openFile(StringRef file) { + std::error_code ec; + auto ret = + llvm::make_unique(file, ec, sys::fs::OpenFlags::F_None); + if (ec) { + error("cannot open " + file + ": " + ec.message()); + return nullptr; + } + return ret; +} +static std::string getThinLTOOutputFile(StringRef path) { + return lto::getThinLTOOutputFile(path, + config->thinLTOPrefixReplace.first, + config->thinLTOPrefixReplace.second); +} + +static lto::Config createConfig() { + lto::Config c; + c.Options = initTargetOptionsFromCodeGenFlags(); + // Always emit a section per function/datum with LTO. LLVM LTO should get most // of the benefit of linker GC, but there are still opportunities for ICF. - C.Options.FunctionSections = true; - C.Options.DataSections = true; + c.Options.FunctionSections = true; + c.Options.DataSections = true; // Use static reloc model on 32-bit x86 because it usually results in more // compact code, and because there are also known code generation bugs when // using the PIC model (see PR34306). - if (Config->Machine == COFF::IMAGE_FILE_MACHINE_I386) - C.RelocModel = Reloc::Static; + if (config->machine == COFF::IMAGE_FILE_MACHINE_I386) + c.RelocModel = Reloc::Static; else - C.RelocModel = Reloc::PIC_; - C.DisableVerify = true; - C.DiagHandler = diagnosticHandler; - C.OptLevel = Config->LTOO; - C.CPU = GetCPUStr(); - C.MAttrs = GetMAttrs(); + c.RelocModel = Reloc::PIC_; + c.DisableVerify = true; + c.DiagHandler = diagnosticHandler; + c.OptLevel = config->ltoo; + c.CPU = getCPUStr(); + c.MAttrs = getMAttrs(); + c.CGOptLevel = args::getCGOptLevel(config->ltoo); - if (Config->SaveTemps) - checkError(C.addSaveTemps(std::string(Config->OutputFile) + ".", + if (config->saveTemps) + checkError(c.addSaveTemps(std::string(config->outputFile) + ".", /*UseInputModulePath*/ true)); - lto::ThinBackend Backend; - if (Config->ThinLTOJobs != 0) - Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs); - return llvm::make_unique(std::move(C), Backend, - Config->LTOPartitions); + return c; } -BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {} +BitcodeCompiler::BitcodeCompiler() { + // Initialize indexFile. + if (!config->thinLTOIndexOnlyArg.empty()) + indexFile = openFile(config->thinLTOIndexOnlyArg); + // Initialize ltoObj. + lto::ThinBackend backend; + if (config->thinLTOIndexOnly) { + auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); }; + backend = lto::createWriteIndexesThinBackend( + config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second, + config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite); + } else if (config->thinLTOJobs != 0) { + backend = lto::createInProcessThinBackend(config->thinLTOJobs); + } + + ltoObj = llvm::make_unique(createConfig(), backend, + config->ltoPartitions); +} + BitcodeCompiler::~BitcodeCompiler() = default; -static void undefine(Symbol *S) { replaceSymbol(S, S->getName()); } +static void undefine(Symbol *s) { replaceSymbol(s, s->getName()); } -void BitcodeCompiler::add(BitcodeFile &F) { - lto::InputFile &Obj = *F.Obj; - unsigned SymNum = 0; - std::vector SymBodies = F.getSymbols(); - std::vector Resols(SymBodies.size()); +void BitcodeCompiler::add(BitcodeFile &f) { + lto::InputFile &obj = *f.obj; + unsigned symNum = 0; + std::vector symBodies = f.getSymbols(); + std::vector resols(symBodies.size()); + if (config->thinLTOIndexOnly) + thinIndices.insert(obj.getName()); + // Provide a resolution to the LTO API for each symbol. - for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) { - Symbol *Sym = SymBodies[SymNum]; - lto::SymbolResolution &R = Resols[SymNum]; - ++SymNum; + for (const lto::InputFile::Symbol &objSym : obj.symbols()) { + Symbol *sym = symBodies[symNum]; + lto::SymbolResolution &r = resols[symNum]; + ++symNum; // 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() && Sym->getFile() == &F; - R.VisibleToRegularObj = Sym->IsUsedInRegularObj; - if (R.Prevailing) - undefine(Sym); + r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f; + r.VisibleToRegularObj = sym->isUsedInRegularObj; + if (r.Prevailing) + undefine(sym); } - checkError(LTOObj->add(std::move(F.Obj), Resols)); + checkError(ltoObj->add(std::move(f.obj), resols)); } // Merge all the bitcode files we have seen, codegen the result // and return the resulting objects. std::vector BitcodeCompiler::compile() { - unsigned MaxTasks = LTOObj->getMaxTasks(); - Buf.resize(MaxTasks); - Files.resize(MaxTasks); + unsigned maxTasks = ltoObj->getMaxTasks(); + buf.resize(maxTasks); + files.resize(maxTasks); // 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 MB) { - Files[Task] = std::move(MB); + lto::NativeObjectCache cache; + if (!config->ltoCache.empty()) + cache = check(lto::localCache( + config->ltoCache, [&](size_t task, std::unique_ptr mb) { + files[task] = std::move(mb); })); - checkError(LTOObj->run( - [&](size_t Task) { + checkError(ltoObj->run( + [&](size_t task) { return llvm::make_unique( - llvm::make_unique(Buf[Task])); + llvm::make_unique(buf[task])); }, - Cache)); + cache)); - if (!Config->LTOCache.empty()) - pruneCache(Config->LTOCache, Config->LTOCachePolicy); + // Emit empty index files for non-indexed files + for (StringRef s : thinIndices) { + std::string path = getThinLTOOutputFile(s); + openFile(path + ".thinlto.bc"); + if (config->thinLTOEmitImportsFiles) + openFile(path + ".imports"); + } - std::vector Ret; - for (unsigned I = 0; I != MaxTasks; ++I) { - if (Buf[I].empty()) + // ThinLTO with index only option is required to generate only the index + // files. After that, we exit from linker and ThinLTO backend runs in a + // distributed environment. + if (config->thinLTOIndexOnly) { + if (indexFile) + indexFile->close(); + return {}; + } + + if (!config->ltoCache.empty()) + pruneCache(config->ltoCache, config->ltoCachePolicy); + + std::vector ret; + for (unsigned i = 0; i != maxTasks; ++i) { + if (buf[i].empty()) continue; - if (Config->SaveTemps) { - if (I == 0) - saveBuffer(Buf[I], Config->OutputFile + ".lto.obj"); + if (config->saveTemps) { + if (i == 0) + saveBuffer(buf[i], config->outputFile + ".lto.obj"); else - saveBuffer(Buf[I], Config->OutputFile + Twine(I) + ".lto.obj"); + saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.obj"); } - Ret.emplace_back(Buf[I].data(), Buf[I].size()); + ret.emplace_back(buf[i].data(), buf[i].size()); } - for (std::unique_ptr &File : Files) - if (File) - Ret.push_back(File->getBuffer()); + for (std::unique_ptr &file : files) + if (file) + ret.push_back(file->getBuffer()); - return Ret; + return ret; } Index: vendor/lld/dist/COFF/LTO.h =================================================================== --- vendor/lld/dist/COFF/LTO.h (revision 351287) +++ vendor/lld/dist/COFF/LTO.h (revision 351288) @@ -1,57 +1,60 @@ //===- LTO.h ----------------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// // // This file provides a way to combine bitcode files into one COFF // file by compiling them using LLVM. // // If LTO is in use, your input files are not in regular COFF files // but instead LLVM bitcode files. In that case, the linker has to // convert bitcode files into the native format so that we can create // a COFF file that contains native code. This file provides that // functionality. // //===----------------------------------------------------------------------===// #ifndef LLD_COFF_LTO_H #define LLD_COFF_LTO_H #include "lld/Common/LLVM.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" #include #include namespace llvm { namespace lto { class LTO; } } namespace lld { namespace coff { class BitcodeFile; class InputFile; class BitcodeCompiler { public: BitcodeCompiler(); ~BitcodeCompiler(); - void add(BitcodeFile &F); + void add(BitcodeFile &f); std::vector compile(); private: - std::unique_ptr LTOObj; - std::vector> Buf; - std::vector> Files; + std::unique_ptr ltoObj; + std::vector> buf; + std::vector> files; + std::unique_ptr indexFile; + llvm::DenseSet thinIndices; }; } } #endif Index: vendor/lld/dist/COFF/MapFile.cpp =================================================================== --- vendor/lld/dist/COFF/MapFile.cpp (revision 351287) +++ vendor/lld/dist/COFF/MapFile.cpp (revision 351288) @@ -1,125 +1,124 @@ //===- MapFile.cpp --------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// // // This file implements the /lldmap option. It shows lists in order and // hierarchically the output sections, input sections, input files and // symbol: // // Address Size Align Out File Symbol // 00201000 00000015 4 .text // 00201000 0000000e 4 test.o:(.text) // 0020100e 00000000 0 local // 00201005 00000000 0 f(int) // //===----------------------------------------------------------------------===// #include "MapFile.h" #include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" #include "lld/Common/ErrorHandler.h" -#include "llvm/Support/Parallel.h" +#include "lld/Common/Threads.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; using namespace llvm::object; using namespace lld; using namespace lld::coff; -typedef DenseMap> - SymbolMapTy; +using SymbolMapTy = + DenseMap>; -static const std::string Indent8 = " "; // 8 spaces -static const std::string Indent16 = " "; // 16 spaces +static const std::string indent8 = " "; // 8 spaces +static const std::string indent16 = " "; // 16 spaces // Print out the first three columns of a line. -static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size, - uint64_t Align) { - OS << format("%08llx %08llx %5lld ", Addr, Size, Align); +static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size, + uint64_t align) { + os << format("%08llx %08llx %5lld ", addr, size, align); } // Returns a list of all symbols that we want to print out. static std::vector getSymbols() { - std::vector V; - for (ObjFile *File : ObjFile::Instances) - for (Symbol *B : File->getSymbols()) - if (auto *Sym = dyn_cast_or_null(B)) - if (Sym && !Sym->getCOFFSymbol().isSectionDefinition()) - V.push_back(Sym); - return V; + std::vector v; + for (ObjFile *file : ObjFile::instances) + for (Symbol *b : file->getSymbols()) + if (auto *sym = dyn_cast_or_null(b)) + if (sym && !sym->getCOFFSymbol().isSectionDefinition()) + v.push_back(sym); + return v; } // Returns a map from sections to their symbols. -static SymbolMapTy getSectionSyms(ArrayRef Syms) { - SymbolMapTy Ret; - for (DefinedRegular *S : Syms) - Ret[S->getChunk()].push_back(S); +static SymbolMapTy getSectionSyms(ArrayRef syms) { + SymbolMapTy ret; + for (DefinedRegular *s : syms) + ret[s->getChunk()].push_back(s); // Sort symbols by address. - for (auto &It : Ret) { - SmallVectorImpl &V = It.second; - std::sort(V.begin(), V.end(), [](DefinedRegular *A, DefinedRegular *B) { - return A->getRVA() < B->getRVA(); + for (auto &it : ret) { + SmallVectorImpl &v = it.second; + std::sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) { + return a->getRVA() < b->getRVA(); }); } - return Ret; + return ret; } // Construct a map from symbols to their stringified representations. static DenseMap -getSymbolStrings(ArrayRef Syms) { - std::vector Str(Syms.size()); - for_each_n(parallel::par, (size_t)0, Syms.size(), [&](size_t I) { - raw_string_ostream OS(Str[I]); - writeHeader(OS, Syms[I]->getRVA(), 0, 0); - OS << Indent16 << toString(*Syms[I]); +getSymbolStrings(ArrayRef syms) { + std::vector str(syms.size()); + parallelForEachN((size_t)0, syms.size(), [&](size_t i) { + raw_string_ostream os(str[i]); + writeHeader(os, syms[i]->getRVA(), 0, 0); + os << indent16 << toString(*syms[i]); }); - DenseMap Ret; - for (size_t I = 0, E = Syms.size(); I < E; ++I) - Ret[Syms[I]] = std::move(Str[I]); - return Ret; + DenseMap ret; + for (size_t i = 0, e = syms.size(); i < e; ++i) + ret[syms[i]] = std::move(str[i]); + return ret; } -void coff::writeMapFile(ArrayRef OutputSections) { - if (Config->MapFile.empty()) +void coff::writeMapFile(ArrayRef outputSections) { + if (config->mapFile.empty()) return; - std::error_code EC; - raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None); - if (EC) - fatal("cannot open " + Config->MapFile + ": " + EC.message()); + std::error_code ec; + raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None); + if (ec) + fatal("cannot open " + config->mapFile + ": " + ec.message()); // Collect symbol info that we want to print out. - std::vector Syms = getSymbols(); - SymbolMapTy SectionSyms = getSectionSyms(Syms); - DenseMap SymStr = getSymbolStrings(Syms); + std::vector syms = getSymbols(); + SymbolMapTy sectionSyms = getSectionSyms(syms); + DenseMap symStr = getSymbolStrings(syms); // Print out the header line. - OS << "Address Size Align Out In Symbol\n"; + os << "Address Size Align Out In Symbol\n"; // Print out file contents. - for (OutputSection *Sec : OutputSections) { - writeHeader(OS, Sec->getRVA(), Sec->getVirtualSize(), /*Align=*/PageSize); - OS << Sec->Name << '\n'; + for (OutputSection *sec : outputSections) { + writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize); + os << sec->name << '\n'; - for (Chunk *C : Sec->Chunks) { - auto *SC = dyn_cast(C); - if (!SC) + for (Chunk *c : sec->chunks) { + auto *sc = dyn_cast(c); + if (!sc) continue; - writeHeader(OS, SC->getRVA(), SC->getSize(), SC->Alignment); - OS << Indent8 << SC->File->getName() << ":(" << SC->getSectionName() + writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment()); + os << indent8 << sc->file->getName() << ":(" << sc->getSectionName() << ")\n"; - for (DefinedRegular *Sym : SectionSyms[SC]) - OS << SymStr[Sym] << '\n'; + for (DefinedRegular *sym : sectionSyms[sc]) + os << symStr[sym] << '\n'; } } } Index: vendor/lld/dist/COFF/MapFile.h =================================================================== --- vendor/lld/dist/COFF/MapFile.h (revision 351287) +++ vendor/lld/dist/COFF/MapFile.h (revision 351288) @@ -1,22 +1,21 @@ //===- MapFile.h ------------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #ifndef LLD_COFF_MAPFILE_H #define LLD_COFF_MAPFILE_H #include "llvm/ADT/ArrayRef.h" namespace lld { namespace coff { class OutputSection; -void writeMapFile(llvm::ArrayRef OutputSections); +void writeMapFile(llvm::ArrayRef outputSections); } } #endif Index: vendor/lld/dist/COFF/MarkLive.cpp =================================================================== --- vendor/lld/dist/COFF/MarkLive.cpp (revision 351287) +++ vendor/lld/dist/COFF/MarkLive.cpp (revision 351288) @@ -1,74 +1,73 @@ //===- MarkLive.cpp -------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "Chunks.h" #include "Symbols.h" #include "lld/Common/Timer.h" #include "llvm/ADT/STLExtras.h" #include namespace lld { namespace coff { -static Timer GCTimer("GC", Timer::root()); +static Timer gctimer("GC", Timer::root()); // Set live bit on for each reachable chunk. Unmarked (unreachable) // COMDAT chunks will be ignored by Writer, so they will be excluded // from the final output. -void markLive(ArrayRef Chunks) { - ScopedTimer T(GCTimer); +void markLive(ArrayRef chunks) { + ScopedTimer t(gctimer); // We build up a worklist of sections which have been marked as live. We only // push into the worklist when we discover an unmarked section, and we mark // as we push, so sections never appear twice in the list. - SmallVector Worklist; + SmallVector worklist; // COMDAT section chunks are dead by default. Add non-COMDAT chunks. - for (Chunk *C : Chunks) - if (auto *SC = dyn_cast(C)) - if (SC->Live) - Worklist.push_back(SC); + for (Chunk *c : chunks) + if (auto *sc = dyn_cast(c)) + if (sc->live) + worklist.push_back(sc); - auto Enqueue = [&](SectionChunk *C) { - if (C->Live) + auto enqueue = [&](SectionChunk *c) { + if (c->live) return; - C->Live = true; - Worklist.push_back(C); + c->live = true; + worklist.push_back(c); }; - auto AddSym = [&](Symbol *B) { - if (auto *Sym = dyn_cast(B)) - Enqueue(Sym->getChunk()); - else if (auto *Sym = dyn_cast(B)) - Sym->File->Live = true; - else if (auto *Sym = dyn_cast(B)) - Sym->WrappedSym->File->Live = Sym->WrappedSym->File->ThunkLive = true; + auto addSym = [&](Symbol *b) { + if (auto *sym = dyn_cast(b)) + enqueue(sym->getChunk()); + else if (auto *sym = dyn_cast(b)) + sym->file->live = true; + else if (auto *sym = dyn_cast(b)) + sym->wrappedSym->file->live = sym->wrappedSym->file->thunkLive = true; }; // Add GC root chunks. - for (Symbol *B : Config->GCRoot) - AddSym(B); + for (Symbol *b : config->gcroot) + addSym(b); - while (!Worklist.empty()) { - SectionChunk *SC = Worklist.pop_back_val(); - assert(SC->Live && "We mark as live when pushing onto the worklist!"); + while (!worklist.empty()) { + SectionChunk *sc = worklist.pop_back_val(); + assert(sc->live && "We mark as live when pushing onto the worklist!"); // Mark all symbols listed in the relocation table for this section. - for (Symbol *B : SC->symbols()) - if (B) - AddSym(B); + for (Symbol *b : sc->symbols()) + if (b) + addSym(b); // Mark associative sections if any. - for (SectionChunk *C : SC->children()) - Enqueue(C); + for (SectionChunk &c : sc->children()) + enqueue(&c); } } } } Index: vendor/lld/dist/COFF/MarkLive.h =================================================================== --- vendor/lld/dist/COFF/MarkLive.h (revision 351287) +++ vendor/lld/dist/COFF/MarkLive.h (revision 351288) @@ -1,24 +1,25 @@ //===- MarkLive.h -----------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #ifndef LLD_COFF_MARKLIVE_H #define LLD_COFF_MARKLIVE_H #include "lld/Common/LLVM.h" #include "llvm/ADT/ArrayRef.h" namespace lld { namespace coff { -void markLive(ArrayRef Chunks); +class Chunk; + +void markLive(ArrayRef chunks); } // namespace coff } // namespace lld #endif // LLD_COFF_MARKLIVE_H Index: vendor/lld/dist/COFF/MinGW.cpp =================================================================== --- vendor/lld/dist/COFF/MinGW.cpp (revision 351287) +++ vendor/lld/dist/COFF/MinGW.cpp (revision 351288) @@ -1,176 +1,166 @@ //===- MinGW.cpp ----------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// 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; -void AutoExporter::initSymbolExcludes() { - ExcludeSymbolPrefixes = { +AutoExporter::AutoExporter() { + excludeLibs = { + "libgcc", + "libgcc_s", + "libstdc++", + "libmingw32", + "libmingwex", + "libg2c", + "libsupc++", + "libobjc", + "libgcj", + "libclang_rt.builtins", + "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", + }; + + excludeSymbolPrefixes = { // Import symbols "__imp_", "__IMPORT_DESCRIPTOR_", // Extra import symbols from GNU import libraries "__nm_", // C++ symbols "__rtti_", "__builtin_", // Artifical symbols such as .refptr ".", }; - ExcludeSymbolSuffixes = { + + excludeSymbolSuffixes = { "_iname", "_NULL_THUNK_DATA", }; - if (Config->Machine == I386) { - ExcludeSymbols = { + + 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", }; - ExcludeSymbolPrefixes.insert("__head_"); + excludeSymbolPrefixes.insert("__head_"); } else { - ExcludeSymbols = { + 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", }; - ExcludeSymbolPrefixes.insert("_head_"); + excludeSymbolPrefixes.insert("_head_"); } } -AutoExporter::AutoExporter() { - ExcludeLibs = { - "libgcc", - "libgcc_s", - "libstdc++", - "libmingw32", - "libmingwex", - "libg2c", - "libsupc++", - "libobjc", - "libgcj", - "libclang_rt.builtins", - "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", - }; -} - -void AutoExporter::addWholeArchive(StringRef Path) { - StringRef LibName = sys::path::filename(Path); +void AutoExporter::addWholeArchive(StringRef path) { + StringRef libName = sys::path::filename(path); // Drop the file extension, to match the processing below. - LibName = LibName.substr(0, LibName.rfind('.')); - ExcludeLibs.erase(LibName); + libName = libName.substr(0, libName.rfind('.')); + excludeLibs.erase(libName); } -bool AutoExporter::shouldExport(Defined *Sym) const { - if (!Sym || !Sym->isLive() || !Sym->getChunk()) +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(Sym) && !isa(Sym)) + if (!isa(sym) && !isa(sym)) return false; - if (ExcludeSymbols.count(Sym->getName())) + if (excludeSymbols.count(sym->getName())) return false; - for (StringRef Prefix : ExcludeSymbolPrefixes.keys()) - if (Sym->getName().startswith(Prefix)) + for (StringRef prefix : excludeSymbolPrefixes.keys()) + if (sym->getName().startswith(prefix)) return false; - for (StringRef Suffix : ExcludeSymbolSuffixes.keys()) - if (Sym->getName().endswith(Suffix)) + for (StringRef suffix : excludeSymbolSuffixes.keys()) + if (sym->getName().endswith(suffix)) return false; // If a corresponding __imp_ symbol exists and is defined, don't export it. - if (Symtab->find(("__imp_" + Sym->getName()).str())) + 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()) + if (!sym->getFile()) return false; - StringRef LibName = sys::path::filename(Sym->getFile()->ParentName); + 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); + 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); + 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()); +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(E.Sym)) { - if (Def && Def->getChunk() && - !(Def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) - OS << " DATA"; + os << "EXPORTS\n"; + for (Export &e : config->exports) { + os << " " << e.exportName << " " + << "@" << e.ordinal; + if (auto *def = dyn_cast_or_null(e.sym)) { + if (def && def->getChunk() && + !(def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) + os << " DATA"; } - OS << "\n"; + os << "\n"; } } Index: vendor/lld/dist/COFF/MinGW.h =================================================================== --- vendor/lld/dist/COFF/MinGW.h (revision 351287) +++ vendor/lld/dist/COFF/MinGW.h (revision 351288) @@ -1,44 +1,41 @@ //===- MinGW.h --------------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// 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(); - void initSymbolExcludes(); + void addWholeArchive(StringRef path); - void addWholeArchive(StringRef Path); + llvm::StringSet<> excludeSymbols; + llvm::StringSet<> excludeSymbolPrefixes; + llvm::StringSet<> excludeSymbolSuffixes; + llvm::StringSet<> excludeLibs; + llvm::StringSet<> excludeObjects; - llvm::StringSet<> ExcludeSymbols; - llvm::StringSet<> ExcludeSymbolPrefixes; - llvm::StringSet<> ExcludeSymbolSuffixes; - llvm::StringSet<> ExcludeLibs; - llvm::StringSet<> ExcludeObjects; - - bool shouldExport(Defined *Sym) const; + bool shouldExport(Defined *sym) const; }; -void writeDefFile(StringRef Name); +void writeDefFile(StringRef name); } // namespace coff } // namespace lld #endif Index: vendor/lld/dist/COFF/Options.td =================================================================== --- vendor/lld/dist/COFF/Options.td (revision 351287) +++ vendor/lld/dist/COFF/Options.td (revision 351288) @@ -1,194 +1,225 @@ include "llvm/Option/OptParser.td" // link.exe accepts options starting with either a dash or a slash. // Flag that takes no arguments. -class F : Flag<["/", "-", "-?"], name>; +class F : Flag<["/", "-", "/?", "-?"], name>; // Flag that takes one argument after ":". class P : - Joined<["/", "-", "-?"], name#":">, HelpText; + Joined<["/", "-", "/?", "-?"], name#":">, HelpText; // 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 { def "" : F, HelpText; def _no : F, HelpText; } def align : P<"align", "Section alignment">; def aligncomm : P<"aligncomm", "Set common symbol alignment">; def alternatename : P<"alternatename", "Define weak alias">; def base : P<"base", "Base address of the program">; def color_diagnostics: Flag<["--"], "color-diagnostics">, HelpText<"Use colors in diagnostics">; def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">, HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">; def defaultlib : P<"defaultlib", "Add the library to the list of input files">; def delayload : P<"delayload", "Delay loaded DLL name">; def entry : P<"entry", "Name of entry point symbol">; def errorlimit : P<"errorlimit", "Maximum number of errors to emit before stopping (0 = no limit)">; def export : P<"export", "Export a function">; // No help text because /failifmismatch is not intended to be used by the user. def failifmismatch : P<"failifmismatch", "">; +def filealign : P<"filealign", "Section alignment in the output file">; +def functionpadmin : F<"functionpadmin">; +def functionpadmin_opt : P<"functionpadmin", "Prepares an image for hotpatching">; def guard : P<"guard", "Control flow guard">; def heap : P<"heap", "Size of the heap">; def ignore : P<"ignore", "Specify warning codes to ignore">; def implib : P<"implib", "Import library name">; def lib : F<"lib">, HelpText<"Act like lib.exe; must be first argument if present">; def libpath : P<"libpath", "Additional library search path">; def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">; def lldltocache : P<"lldltocache", "Path to ThinLTO cached object file directory">; 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">; def merge : P<"merge", "Combine sections">; def mllvm : P<"mllvm", "Options to pass to LLVM">; def nodefaultlib : P<"nodefaultlib", "Remove a default library">; def opt : P<"opt", "Control optimizations">; def order : P<"order", "Put functions in order">; def out : P<"out", "Path to file to write output">; def natvis : P<"natvis", "Path to natvis file to embed in the PDB">; def no_color_diagnostics: F<"no-color-diagnostics">, HelpText<"Do not use colors in diagnostics">; def pdb : P<"pdb", "PDB file path">; def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">; def section : P<"section", "Specify section attributes">; def stack : P<"stack", "Size of the stack">; def stub : P<"stub", "Specify DOS stub file">; def subsystem : P<"subsystem", "Specify subsystem">; def timestamp : P<"timestamp", "Specify the PE header timestamp">; def version : P<"version", "Specify a version number in the PE header">; def wholearchive_file : P<"wholearchive", "Include all object files from this archive">; -def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias; +def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">, + Alias; def manifest : F<"manifest">, HelpText<"Create .manifest file">; def manifest_colon : P< "manifest", "NO disables manifest output; EMBED[,ID=#] embeds manifest as resource in the image">; def manifestuac : P<"manifestuac", "User access control">; def manifestfile : P<"manifestfile", "Manifest output path, with /manifest">; def manifestdependency : P< "manifestdependency", "Attributes for element in manifest file; implies /manifest">; def manifestinput : P< "manifestinput", "Additional manifest inputs; only valid with /manifest:embed">; // We cannot use multiclass P because class name "incl" is different // from its command line option name. We do this because "include" is // a reserved keyword in tablegen. -def incl : Joined<["/", "-"], "include:">, +def incl : Joined<["/", "-", "/?", "-?"], "include:">, HelpText<"Force symbol to be added to symbol table as undefined one">; // "def" is also a keyword. -def deffile : Joined<["/", "-"], "def:">, +def deffile : Joined<["/", "-", "/?", "-?"], "def:">, HelpText<"Use module-definition file">; def debug : F<"debug">, HelpText<"Embed a symbol table in the image">; def debug_opt : P<"debug", "Embed a symbol table in the image with option">; def debugtype : P<"debugtype", "Debug Info Options">; def dll : F<"dll">, HelpText<"Create a DLL">; def driver : P<"driver", "Generate a Windows NT Kernel Mode Driver">; def nodefaultlib_all : F<"nodefaultlib">, HelpText<"Remove all default libraries">; def noentry : F<"noentry">, HelpText<"Don't add reference to DllMainCRTStartup; only valid with /dll">; def profile : F<"profile">; def repro : F<"Brepro">, HelpText<"Use a hash of the executable as the PE header timestamp">; -def swaprun_cd : F<"swaprun:cd">; -def swaprun_net : F<"swaprun:net">; +def swaprun : P<"swaprun", + "Comma-separated list of 'cd' or 'net'">; +def swaprun_cd : F<"swaprun:cd">, Alias, AliasArgs<["cd"]>, + HelpText<"Make loader run output binary from swap instead of from CD">; +def swaprun_net : F<"swaprun:net">, Alias, AliasArgs<["net"]>, + HelpText<"Make loader run output binary from swap instead of from network">; def verbose : F<"verbose">; def wholearchive_flag : F<"wholearchive">; def force : F<"force">, HelpText<"Allow undefined and multiply defined symbols when creating executables">; def force_unresolved : F<"force:unresolved">, HelpText<"Allow undefined symbols when creating executables">; def force_multiple : F<"force:multiple">, HelpText<"Allow multiply defined symbols when creating executables">; +def force_multipleres : F<"force:multipleres">, + HelpText<"Allow multiply defined resources when creating executables">; defm WX : B<"WX", "Treat warnings as errors", "Don't treat warnings as errors">; defm allowbind : B<"allowbind", "Enable DLL binding (default)", "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", "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 incremental : B<"incremental", "Keep original import library if contents are unchanged", "Overwrite import library even if contents are unchanged">; defm integritycheck : B<"integritycheck", "Set FORCE_INTEGRITY bit in PE header", "No effect (default)">; defm largeaddressaware : B<"largeaddressaware", "Enable large addresses (default on 64-bit)", "Disable large addresses (default on 32-bit)">; 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; +// /?? and -?? must be before /? and -? to not confuse lib/Options. +def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias; + // LLD extensions +def exclude_all_symbols : F<"exclude-all-symbols">; def export_all_symbols : F<"export-all-symbols">; +defm demangle : B<"demangle", + "Demangle symbols in output (default)", + "Do not demangle symbols in output">; +def include_optional : Joined<["/", "-", "/?", "-?"], "includeoptional:">, + HelpText<"Add symbol as undefined, but allow it to remain undefined">; def kill_at : F<"kill-at">; def lldmingw : F<"lldmingw">; -def output_def : Joined<["/", "-"], "output-def:">; +def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">; def pdb_source_path : P<"pdbsourcepath", "Base path used to make relative source file path absolute in PDB">; def rsp_quoting : Joined<["--"], "rsp-quoting=">, HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">; +def thinlto_emit_imports_files : + F<"thinlto-emit-imports-files">, + HelpText<"Emit .imports files with -thinlto-index-only">; +def thinlto_index_only : + F<"thinlto-index-only">, + HelpText<"Instead of linking, emit ThinLTO index files">; +def thinlto_index_only_arg : P< + "thinlto-index-only", + "-thinlto-index-only and also write native module names to file">; +def thinlto_object_suffix_replace : P< + "thinlto-object-suffix-replace", + "'old;new' replace old suffix with new suffix in ThinLTO index">; +def thinlto_prefix_replace: P< + "thinlto-prefix-replace", + "'old;new' replace old prefix with new prefix in ThinLTO outputs">; def dash_dash_version : Flag<["--"], "version">, HelpText<"Print version information">; +defm threads: B<"threads", + "Run the linker multi-threaded (default)", + "Do not run the linker multi-threaded">; // Flags for debugging def lldmap : F<"lldmap">; -def lldmap_file : Joined<["/", "-"], "lldmap:">; +def lldmap_file : Joined<["/", "-", "/?", "-?"], "lldmap:">; def show_timing : F<"time">; +def summary : F<"summary">; //============================================================================== // The flags below do nothing. They are defined only for link.exe compatibility. //============================================================================== -class QF : Joined<["/", "-", "-?"], name#":">; +class QF : Joined<["/", "-", "/?", "-?"], name#":">; -multiclass QB { - def "" : F; - def _no : F; -} - -def functionpadmin : F<"functionpadmin">; def ignoreidl : F<"ignoreidl">; def nologo : F<"nologo">; def throwingnew : F<"throwingnew">; def editandcontinue : F<"editandcontinue">; def fastfail : F<"fastfail">; def delay : QF<"delay">; def errorreport : QF<"errorreport">; def idlout : QF<"idlout">; def maxilksize : QF<"maxilksize">; def tlbid : QF<"tlbid">; def tlbout : QF<"tlbout">; def verbose_all : QF<"verbose">; def guardsym : QF<"guardsym">; Index: vendor/lld/dist/COFF/PDB.cpp =================================================================== --- vendor/lld/dist/COFF/PDB.cpp (revision 351287) +++ vendor/lld/dist/COFF/PDB.cpp (revision 351288) @@ -1,1765 +1,1836 @@ //===- PDB.cpp ------------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "PDB.h" #include "Chunks.h" #include "Config.h" +#include "DebugTypes.h" #include "Driver.h" #include "SymbolTable.h" #include "Symbols.h" +#include "TypeMerger.h" #include "Writer.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Timer.h" +#include "lld/Common/Threads.h" #include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.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/SymbolRecordHelpers.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/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/Object/CVDebugRecord.h" #include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/JamCRC.h" -#include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include using namespace lld; using namespace lld::coff; using namespace llvm; using namespace llvm::codeview; using llvm::object::coff_section; -static ExitOnError ExitOnErr; +static ExitOnError exitOnErr; -static Timer TotalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root()); +static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root()); -static Timer AddObjectsTimer("Add Objects", TotalPdbLinkTimer); -static Timer TypeMergingTimer("Type Merging", AddObjectsTimer); -static Timer SymbolMergingTimer("Symbol Merging", AddObjectsTimer); -static Timer GlobalsLayoutTimer("Globals Stream Layout", TotalPdbLinkTimer); -static Timer TpiStreamLayoutTimer("TPI Stream Layout", TotalPdbLinkTimer); -static Timer DiskCommitTimer("Commit to Disk", TotalPdbLinkTimer); +static Timer addObjectsTimer("Add Objects", totalPdbLinkTimer); +static Timer typeMergingTimer("Type Merging", addObjectsTimer); +static Timer symbolMergingTimer("Symbol Merging", addObjectsTimer); +static Timer globalsLayoutTimer("Globals Stream Layout", totalPdbLinkTimer); +static Timer tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer); +static Timer diskCommitTimer("Commit to Disk", totalPdbLinkTimer); namespace { -/// Map from type index and item index in a type server PDB to the -/// corresponding index in the destination PDB. -struct CVIndexMap { - SmallVector TPIMap; - SmallVector IPIMap; - bool IsTypeServerMap = false; - bool IsPrecompiledTypeMap = false; -}; - class DebugSHandler; class PDBLinker { friend DebugSHandler; public: - PDBLinker(SymbolTable *Symtab) - : Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc), - IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc) { + PDBLinker(SymbolTable *symtab) + : alloc(), symtab(symtab), builder(alloc), tMerger(alloc) { // This isn't strictly necessary, but link.exe usually puts an empty string // as the first "valid" string in the string table, so we do the same in // order to maintain as much byte-for-byte compatibility as possible. - PDBStrTab.insert(""); + pdbStrTab.insert(""); } /// Emit the basic PDB structure: initial streams, headers, etc. - void initialize(llvm::codeview::DebugInfo *BuildId); + void initialize(llvm::codeview::DebugInfo *buildId); /// Add natvis files specified on the command line. void addNatvisFiles(); /// Link CodeView from each object file in the symbol table into the PDB. void addObjectsToPDB(); + /// Link info for each import file in the symbol table into the PDB. + void addImportFilesToPDB(ArrayRef outputSections); + /// Link CodeView from a single object file into the target (output) PDB. /// When a precompiled headers object is linked, its TPI map might be provided /// externally. - void addObjFile(ObjFile *File, CVIndexMap *ExternIndexMap = nullptr); + void addObjFile(ObjFile *file, CVIndexMap *externIndexMap = nullptr); /// Produce a mapping from the type and item indices used in the object /// file to those in the destination PDB. /// /// If the object file uses a type server PDB (compiled with /Zi), merge TPI /// and IPI from the type server PDB and return a map for it. Each unique type /// server PDB is merged at most once, so this may return an existing index /// mapping. /// /// 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. - Expected mergeDebugT(ObjFile *File, - CVIndexMap *ObjectIndexMap); + /// caller-provided objectIndexMap. + Expected mergeDebugT(ObjFile *file, + CVIndexMap *objectIndexMap); /// Reads and makes available a PDB. - Expected maybeMergeTypeServerPDB(ObjFile *File, - const CVType &FirstType); + Expected maybeMergeTypeServerPDB(ObjFile *file); /// Merges a precompiled headers TPI map into the current TPI map. The /// precompiled headers object will also be loaded and remapped in the /// process. - Expected - mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType, - CVIndexMap *ObjectIndexMap); + Error mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *objectIndexMap); /// Reads and makes available a precompiled headers object. /// /// This is a requirement for objects compiled with cl.exe /Yu. In that /// case, the referenced object (which was compiled with /Yc) has to be loaded /// first. This is mainly because the current object's TPI stream has external /// references to the precompiled headers object. /// /// If the precompiled headers object was already loaded, this function will /// simply return its (remapped) TPI map. - Expected aquirePrecompObj(ObjFile *File, - PrecompRecord Precomp); + Expected aquirePrecompObj(ObjFile *file); /// Adds a precompiled headers object signature -> TPI mapping. std::pair - registerPrecompiledHeaders(uint32_t Signature); + registerPrecompiledHeaders(uint32_t signature); - void mergeSymbolRecords(ObjFile *File, const CVIndexMap &IndexMap, - std::vector &StringTableRefs, - BinaryStreamRef SymData); + void mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, + std::vector &stringTableRefs, + BinaryStreamRef symData); /// Add the section map and section contributions to the PDB. - void addSections(ArrayRef OutputSections, - ArrayRef SectionTable); + void addSections(ArrayRef outputSections, + ArrayRef sectionTable); - /// Get the type table or the global type table if /DEBUG:GHASH is enabled. - TypeCollection &getTypeTable() { - if (Config->DebugGHashes) - return GlobalTypeTable; - return TypeTable; - } - - /// Get the ID table or the global ID table if /DEBUG:GHASH is enabled. - TypeCollection &getIDTable() { - if (Config->DebugGHashes) - return GlobalIDTable; - return IDTable; - } - /// Write the PDB to disk and store the Guid generated for it in *Guid. - void commit(codeview::GUID *Guid); + void commit(codeview::GUID *guid); + // Print statistics regarding the final PDB + void printStats(); + private: - BumpPtrAllocator Alloc; + BumpPtrAllocator alloc; - SymbolTable *Symtab; + SymbolTable *symtab; - pdb::PDBFileBuilder Builder; + pdb::PDBFileBuilder builder; - /// Type records that will go into the PDB TPI stream. - MergingTypeTableBuilder TypeTable; + TypeMerger tMerger; - /// Item records that will go into the PDB IPI stream. - 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. - DebugStringTableSubsection PDBStrTab; + DebugStringTableSubsection pdbStrTab; - llvm::SmallString<128> NativePath; + llvm::SmallString<128> nativePath; - /// A list of other PDBs which are loaded during the linking process and which - /// we need to keep around since the linking operation may reference pointers - /// inside of these PDBs. - llvm::SmallVector, 2> LoadedPDBs; + std::vector sectionMap; - std::vector SectionMap; - /// Type index mappings of type server PDBs that we've loaded so far. - std::map TypeServerIndexMappings; + std::map typeServerIndexMappings; /// Type index mappings of precompiled objects type map that we've loaded so /// far. - std::map PrecompTypeIndexMappings; + std::map precompTypeIndexMappings; - /// List of TypeServer PDBs which cannot be loaded. - /// Cached to prevent repeated load attempts. - std::map MissingTypeServerPDBs; + // For statistics + uint64_t globalSymbols = 0; + uint64_t moduleSymbols = 0; + uint64_t publicSymbols = 0; }; class DebugSHandler { - PDBLinker &Linker; + PDBLinker &linker; /// The object file whose .debug$S sections we're processing. - ObjFile &File; + ObjFile &file; /// The result of merging type indices. - const CVIndexMap &IndexMap; + const CVIndexMap &indexMap; /// The DEBUG_S_STRINGTABLE subsection. These strings are referred to by /// index from other records in the .debug$S section. All of these strings /// need to be added to the global PDB string table, and all references to /// these strings need to have their indices re-written to refer to the /// global PDB string table. - DebugStringTableSubsectionRef CVStrTab; + DebugStringTableSubsectionRef cVStrTab; /// The DEBUG_S_FILECHKSMS subsection. As above, these are referred to /// by other records in the .debug$S section and need to be merged into the /// PDB. - DebugChecksumsSubsectionRef Checksums; + DebugChecksumsSubsectionRef checksums; + /// The DEBUG_S_INLINEELINES subsection. There can be only one of these per + /// object file. + DebugInlineeLinesSubsectionRef inlineeLines; + /// The DEBUG_S_FRAMEDATA subsection(s). There can be more than one of /// these and they need not appear in any specific order. However, they /// contain string table references which need to be re-written, so we /// collect them all here and re-write them after all subsections have been /// discovered and processed. - std::vector NewFpoFrames; + std::vector newFpoFrames; /// Pointers to raw memory that we determine have string table references /// that need to be re-written. We first process all .debug$S subsections /// to ensure that we can handle subsections written in any order, building /// up this list as we go. At the end, we use the string table (which must /// have been discovered by now else it is an error) to re-write these /// references. - std::vector StringTableReferences; + std::vector stringTableReferences; public: - DebugSHandler(PDBLinker &Linker, ObjFile &File, const CVIndexMap &IndexMap) - : Linker(Linker), File(File), IndexMap(IndexMap) {} + DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap &indexMap) + : linker(linker), file(file), indexMap(indexMap) {} - void handleDebugS(lld::coff::SectionChunk &DebugS); + void handleDebugS(lld::coff::SectionChunk &debugS); + + std::shared_ptr + mergeInlineeLines(DebugChecksumsSubsection *newChecksums); + void finish(); }; } // Visual Studio's debugger requires absolute paths in various places in the // PDB to work without additional configuration: // https://docs.microsoft.com/en-us/visualstudio/debugger/debug-source-files-common-properties-solution-property-pages-dialog-box -static void pdbMakeAbsolute(SmallVectorImpl &FileName) { +static void pdbMakeAbsolute(SmallVectorImpl &fileName) { // The default behavior is to produce paths that are valid within the context // of the machine that you perform the link on. If the linker is running on // a POSIX system, we will output absolute POSIX paths. If the linker is // running on a Windows system, we will output absolute Windows paths. If the // user desires any other kind of behavior, they should explicitly pass // /pdbsourcepath, in which case we will treat the exact string the user // passed in as the gospel and not normalize, canonicalize it. - if (sys::path::is_absolute(FileName, sys::path::Style::windows) || - sys::path::is_absolute(FileName, sys::path::Style::posix)) + if (sys::path::is_absolute(fileName, sys::path::Style::windows) || + sys::path::is_absolute(fileName, sys::path::Style::posix)) return; // It's not absolute in any path syntax. Relative paths necessarily refer to // the local file system, so we can make it native without ending up with a // nonsensical path. - sys::path::native(FileName); - if (Config->PDBSourcePath.empty()) { - sys::fs::make_absolute(FileName); + if (config->pdbSourcePath.empty()) { + sys::path::native(fileName); + sys::fs::make_absolute(fileName); return; } - // Only apply native and dot removal to the relative file path. We want to - // leave the path the user specified untouched since we assume they specified - // it for a reason. - sys::path::remove_dots(FileName, /*remove_dot_dots=*/true); - SmallString<128> AbsoluteFileName = Config->PDBSourcePath; - sys::path::append(AbsoluteFileName, FileName); - FileName = std::move(AbsoluteFileName); -} + // Try to guess whether /PDBSOURCEPATH is a unix path or a windows path. + // Since PDB's are more of a Windows thing, we make this conservative and only + // decide that it's a unix path if we're fairly certain. Specifically, if + // it starts with a forward slash. + SmallString<128> absoluteFileName = config->pdbSourcePath; + sys::path::Style guessedStyle = absoluteFileName.startswith("/") + ? sys::path::Style::posix + : sys::path::Style::windows; + sys::path::append(absoluteFileName, guessedStyle, fileName); + sys::path::native(absoluteFileName, guessedStyle); + sys::path::remove_dots(absoluteFileName, true, guessedStyle); -static SectionChunk *findByName(ArrayRef Sections, - StringRef Name) { - for (SectionChunk *C : Sections) - if (C->getSectionName() == Name) - return C; - return nullptr; + fileName = std::move(absoluteFileName); } -static ArrayRef consumeDebugMagic(ArrayRef Data, - StringRef SecName) { - // First 4 bytes are section magic. - if (Data.size() < 4) - fatal(SecName + " too short"); - if (support::endian::read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC) - fatal(SecName + " has an invalid magic"); - return Data.slice(4); -} - -static ArrayRef 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 DebugH) { - if (DebugH.size() < sizeof(object::debug_h_header)) +static bool canUseDebugH(ArrayRef debugH) { + if (debugH.size() < sizeof(object::debug_h_header)) return false; - auto *Header = - reinterpret_cast(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_8) && - (DebugH.size() % 8 == 0); + auto *header = + reinterpret_cast(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_8) && + (debugH.size() % 8 == 0); } -static Optional> getDebugH(ObjFile *File) { - SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$H"); - if (!Sec) +static Optional> getDebugH(ObjFile *file) { + SectionChunk *sec = + SectionChunk::findByName(file->getDebugChunks(), ".debug$H"); + if (!sec) return llvm::None; - ArrayRef Contents = Sec->getContents(); - if (!canUseDebugH(Contents)) + ArrayRef contents = sec->getContents(); + if (!canUseDebugH(contents)) return None; - return Contents; + return contents; } static ArrayRef -getHashesFromDebugH(ArrayRef DebugH) { - assert(canUseDebugH(DebugH)); +getHashesFromDebugH(ArrayRef debugH) { + assert(canUseDebugH(debugH)); - DebugH = DebugH.drop_front(sizeof(object::debug_h_header)); - uint32_t Count = DebugH.size() / sizeof(GloballyHashedType); - return {reinterpret_cast(DebugH.data()), Count}; + debugH = debugH.drop_front(sizeof(object::debug_h_header)); + uint32_t count = debugH.size() / sizeof(GloballyHashedType); + return {reinterpret_cast(debugH.data()), count}; } -static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder, - TypeCollection &TypeTable) { +static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder, + TypeCollection &typeTable) { // Start the TPI or IPI stream header. - TpiBuilder.setVersionHeader(pdb::PdbTpiV80); + tpiBuilder.setVersionHeader(pdb::PdbTpiV80); // 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()) + 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); + tpiBuilder.addTypeRecord(type.RecordData, *hash); }); } -// OBJs usually start their symbol stream with a S_OBJNAME record. This record -// also contains the signature/key of the current PCH session. The signature -// must be same for all objects which depend on the precompiled object. -// Recompiling the precompiled headers will generate a new PCH key and thus -// invalidate all the dependent objects. -static uint32_t extractPCHSignature(ObjFile *File) { - auto DbgIt = find_if(File->getDebugChunks(), [](SectionChunk *C) { - return C->getSectionName() == ".debug$S"; - }); - if (!DbgIt) - return 0; - - ArrayRef Contents = - consumeDebugMagic((*DbgIt)->getContents(), ".debug$S"); - DebugSubsectionArray Subsections; - BinaryStreamReader Reader(Contents, support::little); - ExitOnErr(Reader.readArray(Subsections, Contents.size())); - - for (const DebugSubsectionRecord &SS : Subsections) { - if (SS.kind() != DebugSubsectionKind::Symbols) - continue; - - // If it's there, the S_OBJNAME record shall come first in the stream. - Expected Sym = readSymbolFromStream(SS.getRecordData(), 0); - if (!Sym) { - consumeError(Sym.takeError()); - continue; - } - if (auto ObjName = SymbolDeserializer::deserializeAs(Sym.get())) - return ObjName->Signature; - } - return 0; -} - Expected -PDBLinker::mergeDebugT(ObjFile *File, CVIndexMap *ObjectIndexMap) { - ScopedTimer T(TypeMergingTimer); +PDBLinker::mergeDebugT(ObjFile *file, CVIndexMap *objectIndexMap) { + ScopedTimer t(typeMergingTimer); - bool IsPrecompiledHeader = false; + if (!file->debugTypesObj) + return *objectIndexMap; // no Types stream - ArrayRef Data = getDebugSection(File, ".debug$T"); - if (Data.empty()) { - // Try again, Microsoft precompiled headers use .debug$P instead of - // .debug$T - Data = getDebugSection(File, ".debug$P"); - IsPrecompiledHeader = true; - } - if (Data.empty()) - return *ObjectIndexMap; // no debug info - // Precompiled headers objects need to save the index map for further // reference by other objects which use the precompiled headers. - if (IsPrecompiledHeader) { - uint32_t PCHSignature = extractPCHSignature(File); - if (PCHSignature == 0) + if (file->debugTypesObj->kind == TpiSource::PCH) { + uint32_t pchSignature = file->pchSignature.getValueOr(0); + if (pchSignature == 0) fatal("No signature found for the precompiled headers OBJ (" + - File->getName() + ")"); + file->getName() + ")"); // When a precompiled headers object comes first on the command-line, we // update the mapping here. Otherwise, if an object referencing the // precompiled headers object comes first, the mapping is created in // aquirePrecompObj(), thus we would skip this block. - if (!ObjectIndexMap->IsPrecompiledTypeMap) { - auto R = registerPrecompiledHeaders(PCHSignature); - if (R.second) + if (!objectIndexMap->isPrecompiledTypeMap) { + auto r = registerPrecompiledHeaders(pchSignature); + if (r.second) fatal( "A precompiled headers OBJ with the same signature was already " "provided! (" + - File->getName() + ")"); + file->getName() + ")"); - ObjectIndexMap = &R.first; + objectIndexMap = &r.first; } } - BinaryByteStream Stream(Data, support::little); - CVTypeArray Types; - BinaryStreamReader Reader(Stream); - if (auto EC = Reader.readArray(Types, Reader.getLength())) - fatal("Reader::readArray failed: " + toString(std::move(EC))); - - auto FirstType = Types.begin(); - if (FirstType == Types.end()) - return *ObjectIndexMap; - - if (FirstType->kind() == LF_TYPESERVER2) { + if (file->debugTypesObj->kind == TpiSource::UsingPDB) { // Look through type servers. If we've already seen this type server, // don't merge any type information. - return maybeMergeTypeServerPDB(File, *FirstType); - } else if (FirstType->kind() == LF_PRECOMP) { + return maybeMergeTypeServerPDB(file); + } + + CVTypeArray &types = *file->debugTypes; + + if (file->debugTypesObj->kind == TpiSource::UsingPCH) { // This object was compiled with /Yu, so process the corresponding // precompiled headers object (/Yc) first. Some type indices in the current // object are referencing data in the precompiled headers object, so we need // both to be loaded. - auto E = mergeInPrecompHeaderObj(File, *FirstType, ObjectIndexMap); - if (!E) - return E.takeError(); + Error e = mergeInPrecompHeaderObj(file, objectIndexMap); + if (e) + return std::move(e); - // Drop LF_PRECOMP record from the input stream, as it needs to be replaced - // with the precompiled headers object type stream. - // Note that we can't just call Types.drop_front(), as we explicitly want to - // rebase the stream. - Types.setUnderlyingStream( - Types.getUnderlyingStream().drop_front(FirstType->RecordData.size())); + // Drop LF_PRECOMP record from the input stream, as it has been replaced + // with the precompiled headers Type stream in the mergeInPrecompHeaderObj() + // call above. Note that we can't just call Types.drop_front(), as we + // explicitly want to rebase the stream. + CVTypeArray::Iterator firstType = types.begin(); + types.setUnderlyingStream( + types.getUnderlyingStream().drop_front(firstType->RecordData.size())); } // Fill in the temporary, caller-provided ObjectIndexMap. - if (Config->DebugGHashes) { - ArrayRef Hashes; - std::vector OwnedHashes; - if (Optional> DebugH = getDebugH(File)) - Hashes = getHashesFromDebugH(*DebugH); + if (config->debugGHashes) { + ArrayRef hashes; + std::vector ownedHashes; + if (Optional> debugH = getDebugH(file)) + hashes = getHashesFromDebugH(*debugH); else { - OwnedHashes = GloballyHashedType::hashTypes(Types); - Hashes = OwnedHashes; + ownedHashes = GloballyHashedType::hashTypes(types); + hashes = ownedHashes; } - if (auto Err = mergeTypeAndIdRecords(GlobalIDTable, GlobalTypeTable, - ObjectIndexMap->TPIMap, Types, Hashes, - File->PCHSignature)) + if (auto err = mergeTypeAndIdRecords( + tMerger.globalIDTable, tMerger.globalTypeTable, + objectIndexMap->tpiMap, types, hashes, file->pchSignature)) fatal("codeview::mergeTypeAndIdRecords failed: " + - toString(std::move(Err))); + toString(std::move(err))); } else { - if (auto Err = - mergeTypeAndIdRecords(IDTable, TypeTable, ObjectIndexMap->TPIMap, - Types, File->PCHSignature)) + if (auto err = mergeTypeAndIdRecords(tMerger.iDTable, tMerger.typeTable, + objectIndexMap->tpiMap, types, + file->pchSignature)) fatal("codeview::mergeTypeAndIdRecords failed: " + - toString(std::move(Err))); + toString(std::move(err))); } - return *ObjectIndexMap; + return *objectIndexMap; } -static Expected> -tryToLoadPDB(const codeview::GUID &GuidFromObj, StringRef TSPath) { - // Ensure the file exists before anything else. We want to return ENOENT, - // "file not found", even if the path points to a removable device (in which - // case the return message would be EAGAIN, "resource unavailable try again") - if (!llvm::sys::fs::exists(TSPath)) - return errorCodeToError(std::error_code(ENOENT, std::generic_category())); +Expected PDBLinker::maybeMergeTypeServerPDB(ObjFile *file) { + Expected pdbSession = findTypeServerSource(file); + if (!pdbSession) + return pdbSession.takeError(); - ErrorOr> MBOrErr = MemoryBuffer::getFile( - TSPath, /*FileSize=*/-1, /*RequiresNullTerminator=*/false); - if (!MBOrErr) - return errorCodeToError(MBOrErr.getError()); + pdb::PDBFile &pdbFile = pdbSession.get()->getPDBFile(); + pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream()); - std::unique_ptr ThisSession; - if (auto EC = pdb::NativeSession::createFromPdb( - MemoryBuffer::getMemBuffer(Driver->takeBuffer(std::move(*MBOrErr)), - /*RequiresNullTerminator=*/false), - ThisSession)) - return std::move(EC); + auto it = typeServerIndexMappings.emplace(info.getGuid(), CVIndexMap()); + CVIndexMap &indexMap = it.first->second; + if (!it.second) + return indexMap; // already merged - std::unique_ptr NS( - static_cast(ThisSession.release())); - pdb::PDBFile &File = NS->getPDBFile(); - auto ExpectedInfo = File.getPDBInfoStream(); - // All PDB Files should have an Info stream. - if (!ExpectedInfo) - return ExpectedInfo.takeError(); - - // Just because a file with a matching name was found and it was an actual - // PDB file doesn't mean it matches. For it to match the InfoStream's GUID - // must match the GUID specified in the TypeServer2 record. - if (ExpectedInfo->getGuid() != GuidFromObj) - return make_error(pdb::pdb_error_code::signature_out_of_date); - - return std::move(NS); -} - -Expected -PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, const CVType &FirstType) { - TypeServer2Record TS; - if (auto EC = - TypeDeserializer::deserializeAs(const_cast(FirstType), TS)) - fatal("error reading record: " + toString(std::move(EC))); - - const codeview::GUID &TSId = TS.getGuid(); - StringRef TSPath = TS.getName(); - - // First, check if the PDB has previously failed to load. - auto PrevErr = MissingTypeServerPDBs.find(TSId); - if (PrevErr != MissingTypeServerPDBs.end()) - return createFileError( - TSPath, - make_error(PrevErr->second, inconvertibleErrorCode())); - - // Second, check if we already loaded a PDB with this GUID. Return the type - // index mapping if we have it. - auto Insertion = TypeServerIndexMappings.insert({TSId, CVIndexMap()}); - CVIndexMap &IndexMap = Insertion.first->second; - if (!Insertion.second) - return IndexMap; - // Mark this map as a type server map. - IndexMap.IsTypeServerMap = true; + indexMap.isTypeServerMap = true; - // Check for a PDB at: - // 1. The given file path - // 2. Next to the object file or archive file - auto ExpectedSession = handleExpected( - tryToLoadPDB(TSId, TSPath), - [&]() { - StringRef LocalPath = - !File->ParentName.empty() ? File->ParentName : File->getName(); - SmallString<128> Path = sys::path::parent_path(LocalPath); - // Currently, type server PDBs are only created by cl, which only runs - // on Windows, so we can assume type server paths are Windows style. - sys::path::append( - Path, sys::path::filename(TSPath, sys::path::Style::windows)); - return tryToLoadPDB(TSId, Path); - }, - [&](std::unique_ptr EC) -> Error { - auto SysErr = EC->convertToErrorCode(); - // Only re-try loading if the previous error was "No such file or - // directory" - if (SysErr.category() == std::generic_category() && - SysErr.value() == ENOENT) - return Error::success(); - return Error(std::move(EC)); - }); - - if (auto E = ExpectedSession.takeError()) { - TypeServerIndexMappings.erase(TSId); - - // Flatten the error to a string, for later display, if the error occurs - // again on the same PDB. - std::string ErrMsg; - raw_string_ostream S(ErrMsg); - S << E; - MissingTypeServerPDBs.emplace(TSId, S.str()); - - return createFileError(TSPath, std::move(E)); + Expected expectedTpi = pdbFile.getPDBTpiStream(); + if (auto e = expectedTpi.takeError()) + fatal("Type server does not have TPI stream: " + toString(std::move(e))); + pdb::TpiStream *maybeIpi = nullptr; + if (pdbFile.hasPDBIpiStream()) { + Expected expectedIpi = pdbFile.getPDBIpiStream(); + if (auto e = expectedIpi.takeError()) + fatal("Error getting type server IPI stream: " + toString(std::move(e))); + maybeIpi = &*expectedIpi; } - pdb::NativeSession *Session = ExpectedSession->get(); - - // Keep a strong reference to this PDB, so that it's safe to hold pointers - // into the file. - LoadedPDBs.push_back(std::move(*ExpectedSession)); - - auto ExpectedTpi = Session->getPDBFile().getPDBTpiStream(); - if (auto E = ExpectedTpi.takeError()) - fatal("Type server does not have TPI stream: " + toString(std::move(E))); - auto ExpectedIpi = Session->getPDBFile().getPDBIpiStream(); - if (auto E = ExpectedIpi.takeError()) - fatal("Type server does not have TPI stream: " + toString(std::move(E))); - - if (Config->DebugGHashes) { + 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); - - Optional EndPrecomp; + auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray()); + Optional endPrecomp; // Merge TPI first, because the IPI stream will reference type indices. - if (auto Err = mergeTypeRecords(GlobalTypeTable, IndexMap.TPIMap, - ExpectedTpi->typeArray(), TpiHashes, EndPrecomp)) - fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err))); + if (auto err = + mergeTypeRecords(tMerger.globalTypeTable, indexMap.tpiMap, + expectedTpi->typeArray(), tpiHashes, endPrecomp)) + 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))); + if (maybeIpi) { + auto ipiHashes = + GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes); + if (auto err = + mergeIdRecords(tMerger.globalIDTable, indexMap.tpiMap, + indexMap.ipiMap, maybeIpi->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))); + if (auto err = mergeTypeRecords(tMerger.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))); + if (maybeIpi) { + if (auto err = mergeIdRecords(tMerger.iDTable, indexMap.tpiMap, + indexMap.ipiMap, maybeIpi->typeArray())) + fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); + } } - return IndexMap; + return indexMap; } -Expected -PDBLinker::mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType, - CVIndexMap *ObjectIndexMap) { - PrecompRecord Precomp; - if (auto EC = TypeDeserializer::deserializeAs(const_cast(FirstType), - Precomp)) - fatal("error reading record: " + toString(std::move(EC))); +Error PDBLinker::mergeInPrecompHeaderObj(ObjFile *file, + CVIndexMap *objectIndexMap) { + const PrecompRecord &precomp = + retrieveDependencyInfo(file->debugTypesObj); - auto E = aquirePrecompObj(File, Precomp); - if (!E) - return E.takeError(); + Expected e = aquirePrecompObj(file); + if (!e) + return e.takeError(); - const CVIndexMap &PrecompIndexMap = *E; - assert(PrecompIndexMap.IsPrecompiledTypeMap); + const CVIndexMap &precompIndexMap = *e; + assert(precompIndexMap.isPrecompiledTypeMap); - if (PrecompIndexMap.TPIMap.empty()) - return PrecompIndexMap; + if (precompIndexMap.tpiMap.empty()) + return Error::success(); - assert(Precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex); - assert(Precomp.getTypesCount() <= PrecompIndexMap.TPIMap.size()); + assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex); + assert(precomp.getTypesCount() <= precompIndexMap.tpiMap.size()); // Use the previously remapped index map from the precompiled headers. - ObjectIndexMap->TPIMap.append(PrecompIndexMap.TPIMap.begin(), - PrecompIndexMap.TPIMap.begin() + - Precomp.getTypesCount()); - return *ObjectIndexMap; + objectIndexMap->tpiMap.append(precompIndexMap.tpiMap.begin(), + precompIndexMap.tpiMap.begin() + + precomp.getTypesCount()); + return Error::success(); } static bool equals_path(StringRef path1, StringRef path2) { #if defined(_WIN32) return path1.equals_lower(path2); #else return path1.equals(path2); #endif } // Find by name an OBJ provided on the command line -static ObjFile *findObjByName(StringRef FileNameOnly) { - SmallString<128> CurrentPath; +static ObjFile *findObjByName(StringRef fileNameOnly) { + SmallString<128> currentPath; - for (ObjFile *F : ObjFile::Instances) { - StringRef CurrentFileName = sys::path::filename(F->getName()); + for (ObjFile *f : ObjFile::instances) { + StringRef currentFileName = sys::path::filename(f->getName()); // Compare based solely on the file name (link.exe behavior) - if (equals_path(CurrentFileName, FileNameOnly)) - return F; + if (equals_path(currentFileName, fileNameOnly)) + return f; } return nullptr; } std::pair -PDBLinker::registerPrecompiledHeaders(uint32_t Signature) { - auto Insertion = PrecompTypeIndexMappings.insert({Signature, CVIndexMap()}); - CVIndexMap &IndexMap = Insertion.first->second; - if (!Insertion.second) - return {IndexMap, true}; +PDBLinker::registerPrecompiledHeaders(uint32_t signature) { + auto insertion = precompTypeIndexMappings.insert({signature, CVIndexMap()}); + CVIndexMap &indexMap = insertion.first->second; + if (!insertion.second) + return {indexMap, true}; // Mark this map as a precompiled types map. - IndexMap.IsPrecompiledTypeMap = true; - return {IndexMap, false}; + indexMap.isPrecompiledTypeMap = true; + return {indexMap, false}; } -Expected -PDBLinker::aquirePrecompObj(ObjFile *File, PrecompRecord Precomp) { +Expected PDBLinker::aquirePrecompObj(ObjFile *file) { + const PrecompRecord &precomp = + retrieveDependencyInfo(file->debugTypesObj); + // First, check if we already loaded the precompiled headers object with this // signature. Return the type index mapping if we've already seen it. - auto R = registerPrecompiledHeaders(Precomp.getSignature()); - if (R.second) - return R.first; + auto r = registerPrecompiledHeaders(precomp.getSignature()); + if (r.second) + return r.first; - CVIndexMap &IndexMap = R.first; + CVIndexMap &indexMap = r.first; // Cross-compile warning: given that Clang doesn't generate LF_PRECOMP // records, we assume the OBJ comes from a Windows build of cl.exe. Thusly, // the paths embedded in the OBJs are in the Windows format. - SmallString<128> PrecompFileName = sys::path::filename( - Precomp.getPrecompFilePath(), sys::path::Style::windows); + SmallString<128> precompFileName = sys::path::filename( + precomp.getPrecompFilePath(), sys::path::Style::windows); // link.exe requires that a precompiled headers object must always be provided // on the command-line, even if that's not necessary. - auto PrecompFile = findObjByName(PrecompFileName); - if (!PrecompFile) + auto precompFile = findObjByName(precompFileName); + if (!precompFile) return createFileError( - PrecompFileName.str(), + precompFileName.str(), make_error(pdb::pdb_error_code::external_cmdline_ref)); - addObjFile(PrecompFile, &IndexMap); + addObjFile(precompFile, &indexMap); - if (!PrecompFile->PCHSignature) - fatal(PrecompFile->getName() + " is not a precompiled headers object"); + if (!precompFile->pchSignature) + fatal(precompFile->getName() + " is not a precompiled headers object"); - if (Precomp.getSignature() != PrecompFile->PCHSignature.getValueOr(0)) + if (precomp.getSignature() != precompFile->pchSignature.getValueOr(0)) return createFileError( - Precomp.getPrecompFilePath().str(), + precomp.getPrecompFilePath().str(), make_error(pdb::pdb_error_code::signature_out_of_date)); - return IndexMap; + return indexMap; } -static bool remapTypeIndex(TypeIndex &TI, ArrayRef TypeIndexMap) { - if (TI.isSimple()) +static bool remapTypeIndex(TypeIndex &ti, ArrayRef typeIndexMap) { + if (ti.isSimple()) return true; - if (TI.toArrayIndex() >= TypeIndexMap.size()) + if (ti.toArrayIndex() >= typeIndexMap.size()) return false; - TI = TypeIndexMap[TI.toArrayIndex()]; + ti = typeIndexMap[ti.toArrayIndex()]; return true; } -static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind, - MutableArrayRef RecordBytes, - const CVIndexMap &IndexMap, - ArrayRef TypeRefs) { - MutableArrayRef Contents = - RecordBytes.drop_front(sizeof(RecordPrefix)); - for (const TiReference &Ref : TypeRefs) { - unsigned ByteSize = Ref.Count * sizeof(TypeIndex); - if (Contents.size() < Ref.Offset + ByteSize) +static void remapTypesInSymbolRecord(ObjFile *file, SymbolKind symKind, + MutableArrayRef recordBytes, + const CVIndexMap &indexMap, + ArrayRef typeRefs) { + MutableArrayRef contents = + recordBytes.drop_front(sizeof(RecordPrefix)); + for (const TiReference &ref : typeRefs) { + unsigned byteSize = ref.Count * sizeof(TypeIndex); + if (contents.size() < ref.Offset + byteSize) fatal("symbol record too short"); // This can be an item index or a type index. Choose the appropriate map. - ArrayRef TypeOrItemMap = IndexMap.TPIMap; - bool IsItemIndex = Ref.Kind == TiRefKind::IndexRef; - if (IsItemIndex && IndexMap.IsTypeServerMap) - TypeOrItemMap = IndexMap.IPIMap; + ArrayRef typeOrItemMap = indexMap.tpiMap; + bool isItemIndex = ref.Kind == TiRefKind::IndexRef; + if (isItemIndex && indexMap.isTypeServerMap) + typeOrItemMap = indexMap.ipiMap; - MutableArrayRef TIs( - reinterpret_cast(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); + MutableArrayRef tIs( + reinterpret_cast(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); continue; } } } } static void -recordStringTableReferenceAtOffset(MutableArrayRef Contents, - uint32_t Offset, - std::vector &StrTableRefs) { - Contents = - Contents.drop_front(Offset).take_front(sizeof(support::ulittle32_t)); - ulittle32_t *Index = reinterpret_cast(Contents.data()); - StrTableRefs.push_back(Index); +recordStringTableReferenceAtOffset(MutableArrayRef contents, + uint32_t offset, + std::vector &strTableRefs) { + contents = + contents.drop_front(offset).take_front(sizeof(support::ulittle32_t)); + ulittle32_t *index = reinterpret_cast(contents.data()); + strTableRefs.push_back(index); } static void -recordStringTableReferences(SymbolKind Kind, MutableArrayRef Contents, - std::vector &StrTableRefs) { +recordStringTableReferences(SymbolKind kind, MutableArrayRef contents, + std::vector &strTableRefs) { // For now we only handle S_FILESTATIC, but we may need the same logic for // S_DEFRANGE and S_DEFRANGE_SUBFIELD. However, I cannot seem to generate any // PDBs that contain these types of records, so because of the uncertainty // they are omitted here until we can prove that it's necessary. - switch (Kind) { + switch (kind) { case SymbolKind::S_FILESTATIC: // FileStaticSym::ModFileOffset - recordStringTableReferenceAtOffset(Contents, 8, StrTableRefs); + recordStringTableReferenceAtOffset(contents, 8, strTableRefs); break; case SymbolKind::S_DEFRANGE: case SymbolKind::S_DEFRANGE_SUBFIELD: log("Not fixing up string table reference in S_DEFRANGE / " "S_DEFRANGE_SUBFIELD record"); break; default: break; } } -static SymbolKind symbolKind(ArrayRef RecordData) { - const RecordPrefix *Prefix = - reinterpret_cast(RecordData.data()); - return static_cast(uint16_t(Prefix->RecordKind)); +static SymbolKind symbolKind(ArrayRef recordData) { + const RecordPrefix *prefix = + reinterpret_cast(recordData.data()); + return static_cast(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 &RecordData, - TypeCollection &IDTable) { - RecordPrefix *Prefix = reinterpret_cast(RecordData.data()); +static void translateIdSymbols(MutableArrayRef &recordData, + TypeCollection &iDTable) { + RecordPrefix *prefix = reinterpret_cast(recordData.data()); - SymbolKind Kind = symbolKind(RecordData); + SymbolKind kind = symbolKind(recordData); - if (Kind == SymbolKind::S_PROC_ID_END) { - Prefix->RecordKind = SymbolKind::S_END; + 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 Refs; - auto Content = RecordData.drop_front(sizeof(RecordPrefix)); - CVSymbol Sym(Kind, RecordData); - discoverTypeIndicesInSymbol(Sym, Refs); - assert(Refs.size() == 1); - assert(Refs.front().Count == 1); + if (kind == SymbolKind::S_GPROC32_ID || kind == SymbolKind::S_LPROC32_ID) { + SmallVector refs; + auto content = recordData.drop_front(sizeof(RecordPrefix)); + CVSymbol sym(recordData); + discoverTypeIndicesInSymbol(sym, refs); + assert(refs.size() == 1); + assert(refs.front().Count == 1); - TypeIndex *TI = - reinterpret_cast(Content.data() + Refs[0].Offset); - // `TI` is the index of a FuncIdRecord or MemberFuncIdRecord which lives in + TypeIndex *ti = + reinterpret_cast(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 Indices; - discoverTypeIndices(FuncIdData, Indices); - assert(Indices.size() == 2); - *TI = Indices[1]; + if (!ti->isSimple() && !ti->isNoneType()) { + CVType funcIdData = iDTable.getType(*ti); + SmallVector indices; + discoverTypeIndices(funcIdData, indices); + assert(indices.size() == 2); + *ti = indices[1]; } - Kind = (Kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32 + kind = (kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32 : SymbolKind::S_LPROC32; - Prefix->RecordKind = uint16_t(Kind); + prefix->RecordKind = uint16_t(kind); } } /// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned. /// The object file may not be aligned. static MutableArrayRef -copyAndAlignSymbol(const CVSymbol &Sym, MutableArrayRef &AlignedMem) { - size_t Size = alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb)); - assert(Size >= 4 && "record too short"); - assert(Size <= MaxRecordLength && "record too long"); - assert(AlignedMem.size() >= Size && "didn't preallocate enough"); +copyAndAlignSymbol(const CVSymbol &sym, MutableArrayRef &alignedMem) { + size_t size = alignTo(sym.length(), alignOf(CodeViewContainer::Pdb)); + assert(size >= 4 && "record too short"); + assert(size <= MaxRecordLength && "record too long"); + assert(alignedMem.size() >= size && "didn't preallocate enough"); // Copy the symbol record and zero out any padding bytes. - MutableArrayRef NewData = AlignedMem.take_front(Size); - AlignedMem = AlignedMem.drop_front(Size); - memcpy(NewData.data(), Sym.data().data(), Sym.length()); - memset(NewData.data() + Sym.length(), 0, Size - Sym.length()); + MutableArrayRef newData = alignedMem.take_front(size); + alignedMem = alignedMem.drop_front(size); + memcpy(newData.data(), sym.data().data(), sym.length()); + memset(newData.data() + sym.length(), 0, size - sym.length()); // Update the record prefix length. It should point to the beginning of the // next record. - auto *Prefix = reinterpret_cast(NewData.data()); - Prefix->RecordLen = Size - 2; - return NewData; + auto *prefix = reinterpret_cast(newData.data()); + prefix->RecordLen = size - 2; + return newData; } struct ScopeRecord { - ulittle32_t PtrParent; - ulittle32_t PtrEnd; + ulittle32_t ptrParent; + ulittle32_t ptrEnd; }; struct SymbolScope { - ScopeRecord *OpeningRecord; - uint32_t ScopeOffset; + ScopeRecord *openingRecord; + uint32_t scopeOffset; }; -static void scopeStackOpen(SmallVectorImpl &Stack, - uint32_t CurOffset, CVSymbol &Sym) { - assert(symbolOpensScope(Sym.kind())); - SymbolScope S; - S.ScopeOffset = CurOffset; - S.OpeningRecord = const_cast( - reinterpret_cast(Sym.content().data())); - S.OpeningRecord->PtrParent = Stack.empty() ? 0 : Stack.back().ScopeOffset; - Stack.push_back(S); +static void scopeStackOpen(SmallVectorImpl &stack, + uint32_t curOffset, CVSymbol &sym) { + assert(symbolOpensScope(sym.kind())); + SymbolScope s; + s.scopeOffset = curOffset; + s.openingRecord = const_cast( + reinterpret_cast(sym.content().data())); + s.openingRecord->ptrParent = stack.empty() ? 0 : stack.back().scopeOffset; + stack.push_back(s); } -static void scopeStackClose(SmallVectorImpl &Stack, - uint32_t CurOffset, ObjFile *File) { - if (Stack.empty()) { - warn("symbol scopes are not balanced in " + File->getName()); +static void scopeStackClose(SmallVectorImpl &stack, + uint32_t curOffset, InputFile *file) { + if (stack.empty()) { + warn("symbol scopes are not balanced in " + file->getName()); return; } - SymbolScope S = Stack.pop_back_val(); - S.OpeningRecord->PtrEnd = CurOffset; + SymbolScope s = stack.pop_back_val(); + s.openingRecord->ptrEnd = curOffset; } -static bool symbolGoesInModuleStream(const CVSymbol &Sym, bool IsGlobalScope) { - switch (Sym.kind()) { +static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) { + switch (sym.kind()) { case SymbolKind::S_GDATA32: case SymbolKind::S_CONSTANT: // 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_UDT records go in the module stream if it is not a global S_UDT. case SymbolKind::S_UDT: - return !IsGlobalScope; + return !isGlobalScope; // 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, bool IsGlobalScope) { - switch (Sym.kind()) { +static bool symbolGoesInGlobalsStream(const CVSymbol &sym, bool isGlobalScope) { + 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; // S_UDT records go in the globals stream if it is a global S_UDT. case SymbolKind::S_UDT: - return IsGlobalScope; + return isGlobalScope; default: return false; } } -static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, uint16_t ModIndex, - unsigned SymOffset, const CVSymbol &Sym) { - switch (Sym.kind()) { +static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex, + unsigned symOffset, 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); + 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 = ModIndex; + SymbolRecordKind k = SymbolRecordKind::ProcRefSym; + if (sym.kind() == SymbolKind::S_LPROC32) + k = SymbolRecordKind::LocalProcRef; + ProcRefSym ps(k); + ps.Module = modIndex; // For some reason, MSVC seems to add one to this value. - ++PS.Module; - PS.Name = getSymbolName(Sym); - PS.SumName = 0; - PS.SymOffset = SymOffset; - Builder.addGlobalSymbol(PS); + ++ps.Module; + ps.Name = getSymbolName(sym); + ps.SumName = 0; + ps.SymOffset = symOffset; + builder.addGlobalSymbol(ps); break; } default: llvm_unreachable("Invalid symbol kind!"); } } -void PDBLinker::mergeSymbolRecords(ObjFile *File, const CVIndexMap &IndexMap, - std::vector &StringTableRefs, - BinaryStreamRef SymData) { - ArrayRef SymsBuffer; - cantFail(SymData.readBytes(0, SymData.getLength(), SymsBuffer)); - SmallVector Scopes; +void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, + std::vector &stringTableRefs, + BinaryStreamRef symData) { + ArrayRef symsBuffer; + cantFail(symData.readBytes(0, symData.getLength(), symsBuffer)); + SmallVector scopes; // Iterate every symbol to check if any need to be realigned, and if so, how // much space we need to allocate for them. - bool NeedsRealignment = false; - unsigned TotalRealignedSize = 0; - auto EC = forEachCodeViewRecord( - SymsBuffer, [&](CVSymbol Sym) -> llvm::Error { - unsigned RealignedSize = - alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb)); - NeedsRealignment |= RealignedSize != Sym.length(); - TotalRealignedSize += RealignedSize; + bool needsRealignment = false; + unsigned totalRealignedSize = 0; + auto ec = forEachCodeViewRecord( + symsBuffer, [&](CVSymbol sym) -> llvm::Error { + unsigned realignedSize = + alignTo(sym.length(), alignOf(CodeViewContainer::Pdb)); + needsRealignment |= realignedSize != sym.length(); + totalRealignedSize += realignedSize; return Error::success(); }); // If any of the symbol record lengths was corrupt, ignore them all, warn // about it, and move on. - if (EC) { - warn("corrupt symbol records in " + File->getName()); - consumeError(std::move(EC)); + if (ec) { + warn("corrupt symbol records in " + file->getName()); + consumeError(std::move(ec)); return; } // If any symbol needed realignment, allocate enough contiguous memory for // them all. Typically symbol subsections are small enough that this will not // cause fragmentation. - MutableArrayRef AlignedSymbolMem; - if (NeedsRealignment) { - void *AlignedData = - Alloc.Allocate(TotalRealignedSize, alignOf(CodeViewContainer::Pdb)); - AlignedSymbolMem = makeMutableArrayRef( - reinterpret_cast(AlignedData), TotalRealignedSize); + MutableArrayRef alignedSymbolMem; + if (needsRealignment) { + void *alignedData = + alloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb)); + alignedSymbolMem = makeMutableArrayRef( + reinterpret_cast(alignedData), totalRealignedSize); } // Iterate again, this time doing the real work. - unsigned CurSymOffset = File->ModuleDBI->getNextSymbolOffset(); - ArrayRef BulkSymbols; + unsigned curSymOffset = file->moduleDBI->getNextSymbolOffset(); + ArrayRef bulkSymbols; cantFail(forEachCodeViewRecord( - SymsBuffer, [&](CVSymbol Sym) -> llvm::Error { + symsBuffer, [&](CVSymbol sym) -> llvm::Error { // Align the record if required. - MutableArrayRef RecordBytes; - if (NeedsRealignment) { - RecordBytes = copyAndAlignSymbol(Sym, AlignedSymbolMem); - Sym = CVSymbol(Sym.kind(), RecordBytes); + MutableArrayRef recordBytes; + if (needsRealignment) { + recordBytes = copyAndAlignSymbol(sym, alignedSymbolMem); + sym = CVSymbol(recordBytes); } else { // Otherwise, we can actually mutate the symbol directly, since we // copied it to apply relocations. - RecordBytes = makeMutableArrayRef( - const_cast(Sym.data().data()), Sym.length()); + recordBytes = makeMutableArrayRef( + const_cast(sym.data().data()), sym.length()); } // Discover type index references in the record. Skip it if we don't // know where they are. - SmallVector TypeRefs; - if (!discoverTypeIndicesInSymbol(Sym, TypeRefs)) { + SmallVector typeRefs; + if (!discoverTypeIndicesInSymbol(sym, typeRefs)) { log("ignoring unknown symbol record with kind 0x" + - utohexstr(Sym.kind())); + utohexstr(sym.kind())); return Error::success(); } // Re-map all the type index references. - remapTypesInSymbolRecord(File, Sym.kind(), RecordBytes, IndexMap, - TypeRefs); + remapTypesInSymbolRecord(file, sym.kind(), recordBytes, indexMap, + typeRefs); // An object file may have S_xxx_ID symbols, but these get converted to // "real" symbols in a PDB. - translateIdSymbols(RecordBytes, getIDTable()); - Sym = CVSymbol(symbolKind(RecordBytes), RecordBytes); + translateIdSymbols(recordBytes, tMerger.getIDTable()); + sym = CVSymbol(recordBytes); // If this record refers to an offset in the object file's string table, // add that item to the global PDB string table and re-write the index. - recordStringTableReferences(Sym.kind(), RecordBytes, StringTableRefs); + recordStringTableReferences(sym.kind(), recordBytes, stringTableRefs); // Fill in "Parent" and "End" fields by maintaining a stack of scopes. - if (symbolOpensScope(Sym.kind())) - scopeStackOpen(Scopes, CurSymOffset, Sym); - else if (symbolEndsScope(Sym.kind())) - scopeStackClose(Scopes, CurSymOffset, File); + if (symbolOpensScope(sym.kind())) + scopeStackOpen(scopes, curSymOffset, sym); + else if (symbolEndsScope(sym.kind())) + scopeStackClose(scopes, curSymOffset, 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(Sym, Scopes.empty())) - addGlobalSymbol(Builder.getGsiBuilder(), - File->ModuleDBI->getModuleIndex(), CurSymOffset, Sym); + if (symbolGoesInGlobalsStream(sym, scopes.empty())) { + addGlobalSymbol(builder.getGsiBuilder(), + file->moduleDBI->getModuleIndex(), curSymOffset, sym); + ++globalSymbols; + } - if (symbolGoesInModuleStream(Sym, Scopes.empty())) { + if (symbolGoesInModuleStream(sym, scopes.empty())) { // Add symbols to the module in bulk. If this symbol is contiguous // with the previous run of symbols to add, combine the ranges. If // not, close the previous range of symbols and start a new one. - if (Sym.data().data() == BulkSymbols.end()) { - BulkSymbols = makeArrayRef(BulkSymbols.data(), - BulkSymbols.size() + Sym.length()); + if (sym.data().data() == bulkSymbols.end()) { + bulkSymbols = makeArrayRef(bulkSymbols.data(), + bulkSymbols.size() + sym.length()); } else { - File->ModuleDBI->addSymbolsInBulk(BulkSymbols); - BulkSymbols = RecordBytes; + file->moduleDBI->addSymbolsInBulk(bulkSymbols); + bulkSymbols = recordBytes; } - CurSymOffset += Sym.length(); + curSymOffset += sym.length(); + ++moduleSymbols; } return Error::success(); })); // Add any remaining symbols we've accumulated. - File->ModuleDBI->addSymbolsInBulk(BulkSymbols); + file->moduleDBI->addSymbolsInBulk(bulkSymbols); } // Allocate memory for a .debug$S / .debug$F section and relocate it. -static ArrayRef relocateDebugChunk(BumpPtrAllocator &Alloc, - SectionChunk &DebugChunk) { - uint8_t *Buffer = Alloc.Allocate(DebugChunk.getSize()); - assert(DebugChunk.OutputSectionOff == 0 && +static ArrayRef relocateDebugChunk(BumpPtrAllocator &alloc, + SectionChunk &debugChunk) { + uint8_t *buffer = alloc.Allocate(debugChunk.getSize()); + assert(debugChunk.getOutputSectionIdx() == 0 && "debug sections should not be in output sections"); - DebugChunk.readRelocTargets(); - DebugChunk.writeTo(Buffer); - return makeArrayRef(Buffer, DebugChunk.getSize()); + debugChunk.writeTo(buffer); + return makeArrayRef(buffer, debugChunk.getSize()); } -static pdb::SectionContrib createSectionContrib(const Chunk *C, uint32_t Modi) { - OutputSection *OS = C->getOutputSection(); - pdb::SectionContrib SC; - memset(&SC, 0, sizeof(SC)); - SC.ISect = OS->SectionIndex; - SC.Off = C->getRVA() - OS->getRVA(); - SC.Size = C->getSize(); - if (auto *SecChunk = dyn_cast(C)) { - SC.Characteristics = SecChunk->Header->Characteristics; - SC.Imod = SecChunk->File->ModuleDBI->getModuleIndex(); - ArrayRef Contents = SecChunk->getContents(); - JamCRC CRC(0); - ArrayRef CharContents = makeArrayRef( - reinterpret_cast(Contents.data()), Contents.size()); - CRC.update(CharContents); - SC.DataCrc = CRC.getCRC(); +static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) { + OutputSection *os = c ? c->getOutputSection() : nullptr; + pdb::SectionContrib sc; + memset(&sc, 0, sizeof(sc)); + sc.ISect = os ? os->sectionIndex : llvm::pdb::kInvalidStreamIndex; + sc.Off = c && os ? c->getRVA() - os->getRVA() : 0; + sc.Size = c ? c->getSize() : -1; + if (auto *secChunk = dyn_cast_or_null(c)) { + sc.Characteristics = secChunk->header->Characteristics; + sc.Imod = secChunk->file->moduleDBI->getModuleIndex(); + ArrayRef contents = secChunk->getContents(); + JamCRC crc(0); + ArrayRef charContents = makeArrayRef( + reinterpret_cast(contents.data()), contents.size()); + crc.update(charContents); + sc.DataCrc = crc.getCRC(); } else { - SC.Characteristics = OS->Header.Characteristics; - // FIXME: When we start creating DBI for import libraries, use those here. - SC.Imod = Modi; + sc.Characteristics = os ? os->header.Characteristics : 0; + sc.Imod = modi; } - SC.RelocCrc = 0; // FIXME + sc.RelocCrc = 0; // FIXME - return SC; + return sc; } static uint32_t -translateStringTableIndex(uint32_t ObjIndex, - const DebugStringTableSubsectionRef &ObjStrTable, - DebugStringTableSubsection &PdbStrTable) { - auto ExpectedString = ObjStrTable.getString(ObjIndex); - if (!ExpectedString) { +translateStringTableIndex(uint32_t objIndex, + const DebugStringTableSubsectionRef &objStrTable, + DebugStringTableSubsection &pdbStrTable) { + auto expectedString = objStrTable.getString(objIndex); + if (!expectedString) { warn("Invalid string table reference"); - consumeError(ExpectedString.takeError()); + consumeError(expectedString.takeError()); return 0; } - return PdbStrTable.insert(*ExpectedString); + return pdbStrTable.insert(*expectedString); } -void DebugSHandler::handleDebugS(lld::coff::SectionChunk &DebugS) { - DebugSubsectionArray Subsections; +void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) { + DebugSubsectionArray subsections; - ArrayRef RelocatedDebugContents = consumeDebugMagic( - relocateDebugChunk(Linker.Alloc, DebugS), DebugS.getSectionName()); + ArrayRef relocatedDebugContents = SectionChunk::consumeDebugMagic( + relocateDebugChunk(linker.alloc, debugS), debugS.getSectionName()); - BinaryStreamReader Reader(RelocatedDebugContents, support::little); - ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size())); + BinaryStreamReader reader(relocatedDebugContents, support::little); + exitOnErr(reader.readArray(subsections, relocatedDebugContents.size())); - for (const DebugSubsectionRecord &SS : Subsections) { - switch (SS.kind()) { + for (const DebugSubsectionRecord &ss : subsections) { + // Ignore subsections with the 'ignore' bit. Some versions of the Visual C++ + // runtime have subsections with this bit set. + if (uint32_t(ss.kind()) & codeview::SubsectionIgnoreFlag) + continue; + + switch (ss.kind()) { case DebugSubsectionKind::StringTable: { - assert(!CVStrTab.valid() && + assert(!cVStrTab.valid() && "Encountered multiple string table subsections!"); - ExitOnErr(CVStrTab.initialize(SS.getRecordData())); + exitOnErr(cVStrTab.initialize(ss.getRecordData())); break; } case DebugSubsectionKind::FileChecksums: - assert(!Checksums.valid() && + assert(!checksums.valid() && "Encountered multiple checksum subsections!"); - ExitOnErr(Checksums.initialize(SS.getRecordData())); + exitOnErr(checksums.initialize(ss.getRecordData())); break; case DebugSubsectionKind::Lines: // We can add the relocated line table directly to the PDB without // modification because the file checksum offsets will stay the same. - File.ModuleDBI->addDebugSubsection(SS); + file.moduleDBI->addDebugSubsection(ss); break; + case DebugSubsectionKind::InlineeLines: + assert(!inlineeLines.valid() && + "Encountered multiple inlinee lines subsections!"); + exitOnErr(inlineeLines.initialize(ss.getRecordData())); + break; case DebugSubsectionKind::FrameData: { // We need to re-write string table indices here, so save off all // frame data subsections until we've processed the entire list of // subsections so that we can be sure we have the string table. - DebugFrameDataSubsectionRef FDS; - ExitOnErr(FDS.initialize(SS.getRecordData())); - NewFpoFrames.push_back(std::move(FDS)); + DebugFrameDataSubsectionRef fds; + exitOnErr(fds.initialize(ss.getRecordData())); + newFpoFrames.push_back(std::move(fds)); break; } case DebugSubsectionKind::Symbols: { - Linker.mergeSymbolRecords(&File, IndexMap, StringTableReferences, - SS.getRecordData()); + linker.mergeSymbolRecords(&file, indexMap, stringTableReferences, + ss.getRecordData()); break; } + + case DebugSubsectionKind::CrossScopeImports: + case DebugSubsectionKind::CrossScopeExports: + // These appear to relate to cross-module optimization, so we might use + // these for ThinLTO. + break; + + case DebugSubsectionKind::ILLines: + case DebugSubsectionKind::FuncMDTokenMap: + case DebugSubsectionKind::TypeMDTokenMap: + case DebugSubsectionKind::MergedAssemblyInput: + // These appear to relate to .Net assembly info. + break; + + case DebugSubsectionKind::CoffSymbolRVA: + // Unclear what this is for. + break; + default: - // FIXME: Process the rest of the subsections. + warn("ignoring unknown debug$S subsection kind 0x" + + utohexstr(uint32_t(ss.kind())) + " in file " + toString(&file)); break; } } } +static Expected +getFileName(const DebugStringTableSubsectionRef &strings, + const DebugChecksumsSubsectionRef &checksums, uint32_t fileID) { + auto iter = checksums.getArray().at(fileID); + if (iter == checksums.getArray().end()) + return make_error(cv_error_code::no_records); + uint32_t offset = iter->FileNameOffset; + return strings.getString(offset); +} + +std::shared_ptr +DebugSHandler::mergeInlineeLines(DebugChecksumsSubsection *newChecksums) { + auto newInlineeLines = std::make_shared( + *newChecksums, inlineeLines.hasExtraFiles()); + + for (const InlineeSourceLine &line : inlineeLines) { + TypeIndex inlinee = line.Header->Inlinee; + uint32_t fileID = line.Header->FileID; + uint32_t sourceLine = line.Header->SourceLineNum; + + ArrayRef typeOrItemMap = + indexMap.isTypeServerMap ? indexMap.ipiMap : indexMap.tpiMap; + if (!remapTypeIndex(inlinee, typeOrItemMap)) { + log("ignoring inlinee line record in " + file.getName() + + " with bad inlinee index 0x" + utohexstr(inlinee.getIndex())); + continue; + } + + SmallString<128> filename = + exitOnErr(getFileName(cVStrTab, checksums, fileID)); + pdbMakeAbsolute(filename); + newInlineeLines->addInlineSite(inlinee, filename, sourceLine); + + if (inlineeLines.hasExtraFiles()) { + for (uint32_t extraFileId : line.ExtraFiles) { + filename = exitOnErr(getFileName(cVStrTab, checksums, extraFileId)); + pdbMakeAbsolute(filename); + newInlineeLines->addExtraFile(filename); + } + } + } + + return newInlineeLines; +} + void DebugSHandler::finish() { - pdb::DbiStreamBuilder &DbiBuilder = Linker.Builder.getDbiBuilder(); + pdb::DbiStreamBuilder &dbiBuilder = linker.builder.getDbiBuilder(); // We should have seen all debug subsections across the entire object file now // which means that if a StringTable subsection and Checksums subsection were // present, now is the time to handle them. - if (!CVStrTab.valid()) { - if (Checksums.valid()) + if (!cVStrTab.valid()) { + if (checksums.valid()) fatal(".debug$S sections with a checksums subsection must also contain a " "string table subsection"); - if (!StringTableReferences.empty()) + if (!stringTableReferences.empty()) warn("No StringTable subsection was encountered, but there are string " "table references"); return; } // Rewrite string table indices in the Fpo Data and symbol records to refer to // the global PDB string table instead of the object file string table. - for (DebugFrameDataSubsectionRef &FDS : NewFpoFrames) { - const ulittle32_t *Reloc = FDS.getRelocPtr(); - for (codeview::FrameData FD : FDS) { - FD.RvaStart += *Reloc; - FD.FrameFunc = - translateStringTableIndex(FD.FrameFunc, CVStrTab, Linker.PDBStrTab); - DbiBuilder.addNewFpoData(FD); + for (DebugFrameDataSubsectionRef &fds : newFpoFrames) { + const ulittle32_t *reloc = fds.getRelocPtr(); + for (codeview::FrameData fd : fds) { + fd.RvaStart += *reloc; + fd.FrameFunc = + translateStringTableIndex(fd.FrameFunc, cVStrTab, linker.pdbStrTab); + dbiBuilder.addNewFpoData(fd); } } - for (ulittle32_t *Ref : StringTableReferences) - *Ref = translateStringTableIndex(*Ref, CVStrTab, Linker.PDBStrTab); + for (ulittle32_t *ref : stringTableReferences) + *ref = translateStringTableIndex(*ref, cVStrTab, linker.pdbStrTab); // Make a new file checksum table that refers to offsets in the PDB-wide // string table. Generally the string table subsection appears after the // checksum table, so we have to do this after looping over all the // subsections. - auto NewChecksums = make_unique(Linker.PDBStrTab); - for (FileChecksumEntry &FC : Checksums) { - SmallString<128> FileName = - ExitOnErr(CVStrTab.getString(FC.FileNameOffset)); - pdbMakeAbsolute(FileName); - ExitOnErr(Linker.Builder.getDbiBuilder().addModuleSourceFile( - *File.ModuleDBI, FileName)); - NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum); + auto newChecksums = make_unique(linker.pdbStrTab); + for (FileChecksumEntry &fc : checksums) { + SmallString<128> filename = + exitOnErr(cVStrTab.getString(fc.FileNameOffset)); + pdbMakeAbsolute(filename); + exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename)); + newChecksums->addChecksum(filename, fc.Kind, fc.Checksum); } - File.ModuleDBI->addDebugSubsection(std::move(NewChecksums)); + + // Rewrite inlinee item indices if present. + if (inlineeLines.valid()) + file.moduleDBI->addDebugSubsection(mergeInlineeLines(newChecksums.get())); + + file.moduleDBI->addDebugSubsection(std::move(newChecksums)); } -void PDBLinker::addObjFile(ObjFile *File, CVIndexMap *ExternIndexMap) { - if (File->wasProcessedForPDB()) +void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) { + if (file->mergedIntoPDB) return; - // 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 - // absolute. - bool InArchive = !File->ParentName.empty(); - SmallString<128> Path = InArchive ? File->ParentName : File->getName(); - pdbMakeAbsolute(Path); - StringRef Name = InArchive ? File->getName() : StringRef(Path); + file->mergedIntoPDB = true; - pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder(); - File->ModuleDBI = &ExitOnErr(DbiBuilder.addModuleInfo(Name)); - File->ModuleDBI->setObjFileName(Path); - - auto Chunks = File->getChunks(); - uint32_t Modi = File->ModuleDBI->getModuleIndex(); - for (Chunk *C : Chunks) { - auto *SecChunk = dyn_cast(C); - if (!SecChunk || !SecChunk->Live) - continue; - pdb::SectionContrib SC = createSectionContrib(SecChunk, Modi); - File->ModuleDBI->setFirstSectionContrib(SC); - break; - } - // Before we can process symbol substreams from .debug$S, we need to process // type information, file checksums, and the string table. Add type info to // the PDB first, so that we can get the map from object file type and item // indices to PDB type and item indices. - CVIndexMap ObjectIndexMap; - auto IndexMapResult = - mergeDebugT(File, ExternIndexMap ? ExternIndexMap : &ObjectIndexMap); + CVIndexMap objectIndexMap; + auto indexMapResult = + mergeDebugT(file, externIndexMap ? externIndexMap : &objectIndexMap); // If the .debug$T sections fail to merge, assume there is no debug info. - if (!IndexMapResult) { - if (!Config->WarnDebugInfoUnusable) { - consumeError(IndexMapResult.takeError()); + if (!indexMapResult) { + if (!config->warnDebugInfoUnusable) { + consumeError(indexMapResult.takeError()); return; } - StringRef FileName = sys::path::filename(Path); - warn("Cannot use debug info for '" + FileName + "' [LNK4099]\n" + + warn("Cannot use debug info for '" + toString(file) + "' [LNK4099]\n" + ">>> failed to load reference " + - StringRef(toString(IndexMapResult.takeError()))); + StringRef(toString(indexMapResult.takeError()))); return; } - ScopedTimer T(SymbolMergingTimer); + ScopedTimer t(symbolMergingTimer); - DebugSHandler DSH(*this, *File, *IndexMapResult); + pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); + DebugSHandler dsh(*this, *file, *indexMapResult); // Now do all live .debug$S and .debug$F sections. - for (SectionChunk *DebugChunk : File->getDebugChunks()) { - if (!DebugChunk->Live || DebugChunk->getSize() == 0) + for (SectionChunk *debugChunk : file->getDebugChunks()) { + if (!debugChunk->live || debugChunk->getSize() == 0) continue; - if (DebugChunk->getSectionName() == ".debug$S") { - DSH.handleDebugS(*DebugChunk); + if (debugChunk->getSectionName() == ".debug$S") { + dsh.handleDebugS(*debugChunk); continue; } - if (DebugChunk->getSectionName() == ".debug$F") { - ArrayRef RelocatedDebugContents = - relocateDebugChunk(Alloc, *DebugChunk); + if (debugChunk->getSectionName() == ".debug$F") { + ArrayRef relocatedDebugContents = + relocateDebugChunk(alloc, *debugChunk); - FixedStreamArray FpoRecords; - BinaryStreamReader Reader(RelocatedDebugContents, support::little); - uint32_t Count = RelocatedDebugContents.size() / sizeof(object::FpoData); - ExitOnErr(Reader.readArray(FpoRecords, Count)); + FixedStreamArray fpoRecords; + BinaryStreamReader reader(relocatedDebugContents, support::little); + uint32_t count = relocatedDebugContents.size() / sizeof(object::FpoData); + exitOnErr(reader.readArray(fpoRecords, count)); // These are already relocated and don't refer to the string table, so we // can just copy it. - for (const object::FpoData &FD : FpoRecords) - DbiBuilder.addOldFpoData(FD); + for (const object::FpoData &fd : fpoRecords) + dbiBuilder.addOldFpoData(fd); continue; } } // Do any post-processing now that all .debug$S sections have been processed. - DSH.finish(); + dsh.finish(); } -static PublicSym32 createPublic(Defined *Def) { - PublicSym32 Pub(SymbolKind::S_PUB32); - Pub.Name = Def->getName(); - if (auto *D = dyn_cast(Def)) { - if (D->getCOFFSymbol().isFunctionDefinition()) - Pub.Flags = PublicSymFlags::Function; - } else if (isa(Def)) { - Pub.Flags = PublicSymFlags::Function; +// 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 +// absolute. +static void createModuleDBI(pdb::PDBFileBuilder &builder) { + pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); + SmallString<128> objName; + + for (ObjFile *file : ObjFile::instances) { + + bool inArchive = !file->parentName.empty(); + objName = inArchive ? file->parentName : file->getName(); + pdbMakeAbsolute(objName); + StringRef modName = inArchive ? file->getName() : StringRef(objName); + + file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName)); + file->moduleDBI->setObjFileName(objName); + + ArrayRef chunks = file->getChunks(); + uint32_t modi = file->moduleDBI->getModuleIndex(); + + for (Chunk *c : chunks) { + auto *secChunk = dyn_cast(c); + if (!secChunk || !secChunk->live) + continue; + pdb::SectionContrib sc = createSectionContrib(secChunk, modi); + file->moduleDBI->setFirstSectionContrib(sc); + break; + } } +} - 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; +static PublicSym32 createPublic(Defined *def) { + PublicSym32 pub(SymbolKind::S_PUB32); + pub.Name = def->getName(); + if (auto *d = dyn_cast(def)) { + if (d->getCOFFSymbol().isFunctionDefinition()) + pub.Flags = PublicSymFlags::Function; + } else if (isa(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() { - ScopedTimer T1(AddObjectsTimer); - for (ObjFile *File : ObjFile::Instances) - addObjFile(File); + ScopedTimer t1(addObjectsTimer); - Builder.getStringTableBuilder().setStrings(PDBStrTab); - T1.stop(); + createModuleDBI(builder); + for (ObjFile *file : ObjFile::instances) + addObjFile(file); + + builder.getStringTableBuilder().setStrings(pdbStrTab); + t1.stop(); + // Construct TPI and IPI stream contents. - ScopedTimer T2(TpiStreamLayoutTimer); - addTypeInfo(Builder.getTpiBuilder(), getTypeTable()); - addTypeInfo(Builder.getIpiBuilder(), getIDTable()); - T2.stop(); + ScopedTimer t2(tpiStreamLayoutTimer); + addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable()); + addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable()); + t2.stop(); - ScopedTimer T3(GlobalsLayoutTimer); + ScopedTimer t3(globalsLayoutTimer); // Compute the public and global symbols. - auto &GsiBuilder = Builder.getGsiBuilder(); - std::vector Publics; - Symtab->forEachSymbol([&Publics](Symbol *S) { + auto &gsiBuilder = builder.getGsiBuilder(); + std::vector publics; + symtab->forEachSymbol([&publics](Symbol *s) { // Only emit defined, live symbols that have a chunk. - auto *Def = dyn_cast(S); - if (Def && Def->isLive() && Def->getChunk()) - Publics.push_back(createPublic(Def)); + auto *def = dyn_cast(s); + if (def && def->isLive() && def->getChunk()) + publics.push_back(createPublic(def)); }); - if (!Publics.empty()) { + if (!publics.empty()) { + publicSymbols = publics.size(); // Sort the public symbols and add them to the stream. - sort(parallel::par, Publics.begin(), Publics.end(), - [](const PublicSym32 &L, const PublicSym32 &R) { - return L.Name < R.Name; - }); - for (const PublicSym32 &Pub : Publics) - GsiBuilder.addPublicSymbol(Pub); + parallelSort(publics, [](const PublicSym32 &l, const PublicSym32 &r) { + return l.Name < r.Name; + }); + for (const PublicSym32 &pub : publics) + gsiBuilder.addPublicSymbol(pub); } } +void PDBLinker::printStats() { + if (!config->showSummary) + return; + + SmallString<256> buffer; + raw_svector_ostream stream(buffer); + + stream << center_justify("Summary", 80) << '\n' + << std::string(80, '-') << '\n'; + + auto print = [&](uint64_t v, StringRef s) { + stream << format_decimal(v, 15) << " " << s << '\n'; + }; + + print(ObjFile::instances.size(), + "Input OBJ files (expanded from all cmd-line inputs)"); + print(typeServerIndexMappings.size(), "PDB type server dependencies"); + print(precompTypeIndexMappings.size(), "Precomp OBJ dependencies"); + print(tMerger.getTypeTable().size() + tMerger.getIDTable().size(), + "Merged TPI records"); + print(pdbStrTab.size(), "Output PDB strings"); + print(globalSymbols, "Global symbol records"); + print(moduleSymbols, "Module symbol records"); + print(publicSymbols, "Public symbol records"); + + message(buffer); +} + void PDBLinker::addNatvisFiles() { - for (StringRef File : Config->NatvisFiles) { - ErrorOr> DataOrErr = - MemoryBuffer::getFile(File); - if (!DataOrErr) { - warn("Cannot open input file: " + File); + for (StringRef file : config->natvisFiles) { + ErrorOr> dataOrErr = + MemoryBuffer::getFile(file); + if (!dataOrErr) { + warn("Cannot open input file: " + file); continue; } - Builder.addInjectedSource(File, std::move(*DataOrErr)); + builder.addInjectedSource(file, std::move(*dataOrErr)); } } -static codeview::CPUType toCodeViewMachine(COFF::MachineTypes Machine) { - switch (Machine) { +static codeview::CPUType toCodeViewMachine(COFF::MachineTypes machine) { + switch (machine) { case COFF::IMAGE_FILE_MACHINE_AMD64: return codeview::CPUType::X64; case COFF::IMAGE_FILE_MACHINE_ARM: return codeview::CPUType::ARM7; case COFF::IMAGE_FILE_MACHINE_ARM64: return codeview::CPUType::ARM64; case COFF::IMAGE_FILE_MACHINE_ARMNT: return codeview::CPUType::ARMNT; case COFF::IMAGE_FILE_MACHINE_I386: return codeview::CPUType::Intel80386; default: llvm_unreachable("Unsupported CPU Type"); } } // Mimic MSVC which surrounds arguments containing whitespace with quotes. // Double double-quotes are handled, so that the resulting string can be // executed again on the cmd-line. -static std::string quote(ArrayRef Args) { - std::string R; - R.reserve(256); - for (StringRef A : Args) { - if (!R.empty()) - R.push_back(' '); - bool HasWS = A.find(' ') != StringRef::npos; - bool HasQ = A.find('"') != StringRef::npos; - if (HasWS || HasQ) - R.push_back('"'); - if (HasQ) { - SmallVector S; - A.split(S, '"'); - R.append(join(S, "\"\"")); +static std::string quote(ArrayRef args) { + std::string r; + r.reserve(256); + for (StringRef a : args) { + if (!r.empty()) + r.push_back(' '); + bool hasWS = a.find(' ') != StringRef::npos; + bool hasQ = a.find('"') != StringRef::npos; + if (hasWS || hasQ) + r.push_back('"'); + if (hasQ) { + SmallVector s; + a.split(s, '"'); + r.append(join(s, "\"\"")); } else { - R.append(A); + r.append(a); } - if (HasWS || HasQ) - R.push_back('"'); + if (hasWS || hasQ) + r.push_back('"'); } - return R; + return r; } -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 = toCodeViewMachine(Config->Machine); +static void fillLinkerVerRecord(Compile3Sym &cs) { + cs.Machine = toCodeViewMachine(config->machine); // Interestingly, if we set the string to 0.0.0.0, then when trying to view // local variables WinDbg emits an error that private symbols are not present. // By setting this to a valid MSVC linker version string, local variables are // 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 = 25019; - CS.VersionBackendMajor = 14; - CS.VersionBackendMinor = 10; - CS.VersionBackendQFE = 0; + cs.Flags = CompileSym3Flags::None; + 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; - CS.VersionFrontendQFE = 0; - CS.Version = "LLVM Linker"; - CS.setLanguage(SourceLanguage::Link); + cs.VersionFrontendBuild = 0; + cs.VersionFrontendMajor = 0; + cs.VersionFrontendMinor = 0; + cs.VersionFrontendQFE = 0; + cs.Version = "LLVM Linker"; + cs.setLanguage(SourceLanguage::Link); +} - ArrayRef Args = makeArrayRef(Config->Argv).drop_front(); - std::string ArgStr = quote(Args); - EBS.Fields.push_back("cwd"); +static void addCommonLinkerModuleSymbols(StringRef path, + pdb::DbiModuleDescriptorBuilder &mod, + BumpPtrAllocator &allocator) { + ObjNameSym ons(SymbolRecordKind::ObjNameSym); + EnvBlockSym ebs(SymbolRecordKind::EnvBlockSym); + Compile3Sym cs(SymbolRecordKind::Compile3Sym); + fillLinkerVerRecord(cs); + + ons.Name = "* Linker *"; + ons.Signature = 0; + + ArrayRef args = makeArrayRef(config->argv).drop_front(); + std::string argStr = quote(args); + ebs.Fields.push_back("cwd"); SmallString<64> cwd; - if (Config->PDBSourcePath.empty()) + if (config->pdbSourcePath.empty()) sys::fs::current_path(cwd); else - cwd = Config->PDBSourcePath; - EBS.Fields.push_back(cwd); - EBS.Fields.push_back("exe"); - SmallString<64> exe = Config->Argv[0]; + cwd = config->pdbSourcePath; + ebs.Fields.push_back(cwd); + ebs.Fields.push_back("exe"); + SmallString<64> exe = config->argv[0]; pdbMakeAbsolute(exe); - EBS.Fields.push_back(exe); - EBS.Fields.push_back("pdb"); - EBS.Fields.push_back(Path); - EBS.Fields.push_back("cmd"); - EBS.Fields.push_back(ArgStr); - Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - ONS, Allocator, CodeViewContainer::Pdb)); - Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - CS, Allocator, CodeViewContainer::Pdb)); - Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - EBS, Allocator, CodeViewContainer::Pdb)); + ebs.Fields.push_back(exe); + ebs.Fields.push_back("pdb"); + ebs.Fields.push_back(path); + ebs.Fields.push_back("cmd"); + ebs.Fields.push_back(argStr); + mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( + ons, allocator, CodeViewContainer::Pdb)); + mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( + cs, allocator, CodeViewContainer::Pdb)); + mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( + 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.Header.Characteristics; - Sym.Length = OS.getVirtualSize(); - Sym.Name = OS.Name; - Sym.Rva = OS.getRVA(); - Sym.SectionNumber = OS.SectionIndex; - Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( - Sym, Allocator, CodeViewContainer::Pdb)); +static void addLinkerModuleCoffGroup(PartialSection *sec, + pdb::DbiModuleDescriptorBuilder &mod, + OutputSection &os, + BumpPtrAllocator &allocator) { + // If there's a section, there's at least one chunk + assert(!sec->chunks.empty()); + const Chunk *firstChunk = *sec->chunks.begin(); + const Chunk *lastChunk = *sec->chunks.rbegin(); + + // Emit COFF group + CoffGroupSym cgs(SymbolRecordKind::CoffGroupSym); + cgs.Name = sec->name; + cgs.Segment = os.sectionIndex; + cgs.Offset = firstChunk->getRVA() - os.getRVA(); + cgs.Size = lastChunk->getRVA() + lastChunk->getSize() - firstChunk->getRVA(); + cgs.Characteristics = sec->characteristics; + + // Somehow .idata sections & sections groups in the debug symbol stream have + // the "write" flag set. However the section header for the corresponding + // .idata section doesn't have it. + if (cgs.Name.startswith(".idata")) + cgs.Characteristics |= llvm::COFF::IMAGE_SCN_MEM_WRITE; + + mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( + cgs, 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.header.Characteristics; + sym.Length = os.getVirtualSize(); + sym.Name = os.name; + sym.Rva = os.getRVA(); + sym.SectionNumber = os.sectionIndex; + mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol( + sym, allocator, CodeViewContainer::Pdb)); + + // Skip COFF groups in MinGW because it adds a significant footprint to the + // PDB, due to each function being in its own section + if (config->mingw) + return; + + // Output COFF groups for individual chunks of this section. + for (PartialSection *sec : os.contribSections) { + addLinkerModuleCoffGroup(sec, mod, os, allocator); + } +} + +// Add all import files as modules to the PDB. +void PDBLinker::addImportFilesToPDB(ArrayRef outputSections) { + if (ImportFile::instances.empty()) + return; + + std::map dllToModuleDbi; + + for (ImportFile *file : ImportFile::instances) { + if (!file->live) + continue; + + if (!file->thunkSym) + continue; + + if (!file->thunkLive) + continue; + + std::string dll = StringRef(file->dllName).lower(); + llvm::pdb::DbiModuleDescriptorBuilder *&mod = dllToModuleDbi[dll]; + if (!mod) { + pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); + SmallString<128> libPath = file->parentName; + pdbMakeAbsolute(libPath); + sys::path::native(libPath); + + // Name modules similar to MSVC's link.exe. + // The first module is the simple dll filename + llvm::pdb::DbiModuleDescriptorBuilder &firstMod = + exitOnErr(dbiBuilder.addModuleInfo(file->dllName)); + firstMod.setObjFileName(libPath); + pdb::SectionContrib sc = + createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex); + firstMod.setFirstSectionContrib(sc); + + // The second module is where the import stream goes. + mod = &exitOnErr(dbiBuilder.addModuleInfo("Import:" + file->dllName)); + mod->setObjFileName(libPath); + } + + DefinedImportThunk *thunk = cast(file->thunkSym); + Chunk *thunkChunk = thunk->getChunk(); + OutputSection *thunkOS = thunkChunk->getOutputSection(); + + ObjNameSym ons(SymbolRecordKind::ObjNameSym); + Compile3Sym cs(SymbolRecordKind::Compile3Sym); + Thunk32Sym ts(SymbolRecordKind::Thunk32Sym); + ScopeEndSym es(SymbolRecordKind::ScopeEndSym); + + ons.Name = file->dllName; + ons.Signature = 0; + + fillLinkerVerRecord(cs); + + ts.Name = thunk->getName(); + ts.Parent = 0; + ts.End = 0; + ts.Next = 0; + ts.Thunk = ThunkOrdinal::Standard; + ts.Length = thunkChunk->getSize(); + ts.Segment = thunkOS->sectionIndex; + ts.Offset = thunkChunk->getRVA() - thunkOS->getRVA(); + + mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( + ons, alloc, CodeViewContainer::Pdb)); + mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( + cs, alloc, CodeViewContainer::Pdb)); + + SmallVector scopes; + CVSymbol newSym = codeview::SymbolSerializer::writeOneSymbol( + ts, alloc, CodeViewContainer::Pdb); + scopeStackOpen(scopes, mod->getNextSymbolOffset(), newSym); + + mod->addSymbol(newSym); + + newSym = codeview::SymbolSerializer::writeOneSymbol(es, alloc, + CodeViewContainer::Pdb); + scopeStackClose(scopes, mod->getNextSymbolOffset(), file); + + mod->addSymbol(newSym); + + pdb::SectionContrib sc = + createSectionContrib(thunk->getChunk(), mod->getModuleIndex()); + mod->setFirstSectionContrib(sc); + } +} + // Creates a PDB file. -void coff::createPDB(SymbolTable *Symtab, - ArrayRef OutputSections, - ArrayRef SectionTable, - llvm::codeview::DebugInfo *BuildId) { - ScopedTimer T1(TotalPdbLinkTimer); - PDBLinker PDB(Symtab); +void coff::createPDB(SymbolTable *symtab, + ArrayRef outputSections, + ArrayRef sectionTable, + llvm::codeview::DebugInfo *buildId) { + ScopedTimer t1(totalPdbLinkTimer); + PDBLinker pdb(symtab); - PDB.initialize(BuildId); - PDB.addObjectsToPDB(); - PDB.addSections(OutputSections, SectionTable); - PDB.addNatvisFiles(); + pdb.initialize(buildId); + pdb.addObjectsToPDB(); + pdb.addImportFilesToPDB(outputSections); + pdb.addSections(outputSections, sectionTable); + pdb.addNatvisFiles(); - ScopedTimer T2(DiskCommitTimer); - codeview::GUID Guid; - PDB.commit(&Guid); - memcpy(&BuildId->PDB70.Signature, &Guid, 16); + ScopedTimer t2(diskCommitTimer); + codeview::GUID guid; + pdb.commit(&guid); + memcpy(&buildId->PDB70.Signature, &guid, 16); + + t2.stop(); + t1.stop(); + pdb.printStats(); } -void PDBLinker::initialize(llvm::codeview::DebugInfo *BuildId) { - ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize +void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) { + exitOnErr(builder.initialize(4096)); // 4096 is blocksize - BuildId->Signature.CVSignature = OMF::Signature::PDB70; + buildId->Signature.CVSignature = OMF::Signature::PDB70; // Signature is set to a hash of the PDB contents when the PDB is done. - memset(BuildId->PDB70.Signature, 0, 16); - BuildId->PDB70.Age = 1; + memset(buildId->PDB70.Signature, 0, 16); + buildId->PDB70.Age = 1; // Create streams in MSF for predefined streams, namely // PDB, TPI, DBI and IPI. - for (int I = 0; I < (int)pdb::kSpecialStreamCount; ++I) - ExitOnErr(Builder.getMsfBuilder().addStream(0)); + for (int i = 0; i < (int)pdb::kSpecialStreamCount; ++i) + exitOnErr(builder.getMsfBuilder().addStream(0)); // Add an Info stream. - auto &InfoBuilder = Builder.getInfoBuilder(); - InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70); - InfoBuilder.setHashPDBContentsToGUID(true); + auto &infoBuilder = builder.getInfoBuilder(); + infoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70); + infoBuilder.setHashPDBContentsToGUID(true); // Add an empty DBI stream. - pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder(); - DbiBuilder.setAge(BuildId->PDB70.Age); - DbiBuilder.setVersionHeader(pdb::PdbDbiV70); - DbiBuilder.setMachineType(Config->Machine); + pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); + dbiBuilder.setAge(buildId->PDB70.Age); + dbiBuilder.setVersionHeader(pdb::PdbDbiV70); + dbiBuilder.setMachineType(config->machine); // Technically we are not link.exe 14.11, but there are known cases where // debugging tools on Windows expect Microsoft-specific version numbers or // they fail to work at all. Since we know we produce PDBs that are // compatible with LINK 14.11, we set that version number here. - DbiBuilder.setBuildNumber(14, 11); + dbiBuilder.setBuildNumber(14, 11); } -void PDBLinker::addSections(ArrayRef OutputSections, - ArrayRef SectionTable) { +void PDBLinker::addSections(ArrayRef outputSections, + ArrayRef sectionTable) { // It's not entirely clear what this is, but the * Linker * module uses it. - pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder(); - NativePath = Config->PDBPath; - pdbMakeAbsolute(NativePath); - uint32_t PdbFilePathNI = DbiBuilder.addECName(NativePath); - auto &LinkerModule = ExitOnErr(DbiBuilder.addModuleInfo("* Linker *")); - LinkerModule.setPdbFilePathNI(PdbFilePathNI); - addCommonLinkerModuleSymbols(NativePath, LinkerModule, Alloc); + pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); + nativePath = config->pdbPath; + pdbMakeAbsolute(nativePath); + 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->Chunks) { - pdb::SectionContrib SC = - createSectionContrib(C, LinkerModule.getModuleIndex()); - Builder.getDbiBuilder().addSectionContrib(SC); + for (OutputSection *os : outputSections) { + addLinkerModuleSectionSymbol(linkerModule, *os, alloc); + for (Chunk *c : os->chunks) { + pdb::SectionContrib sc = + createSectionContrib(c, linkerModule.getModuleIndex()); + builder.getDbiBuilder().addSectionContrib(sc); } } + // The * Linker * first section contrib is only used along with /INCREMENTAL, + // to provide trampolines thunks for incremental function patching. Set this + // as "unused" because LLD doesn't support /INCREMENTAL link. + pdb::SectionContrib sc = + createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex); + linkerModule.setFirstSectionContrib(sc); + // Add Section Map stream. - ArrayRef Sections = { - (const object::coff_section *)SectionTable.data(), - SectionTable.size() / sizeof(object::coff_section)}; - SectionMap = pdb::DbiStreamBuilder::createSectionMap(Sections); - DbiBuilder.setSectionMap(SectionMap); + ArrayRef sections = { + (const object::coff_section *)sectionTable.data(), + sectionTable.size() / sizeof(object::coff_section)}; + sectionMap = pdb::DbiStreamBuilder::createSectionMap(sections); + dbiBuilder.setSectionMap(sectionMap); // Add COFF section header stream. - ExitOnErr( - DbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, SectionTable)); + exitOnErr( + dbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, sectionTable)); } -void PDBLinker::commit(codeview::GUID *Guid) { +void PDBLinker::commit(codeview::GUID *guid) { // Write to a file. - ExitOnErr(Builder.commit(Config->PDBPath, Guid)); + exitOnErr(builder.commit(config->pdbPath, guid)); } -static Expected -getFileName(const DebugStringTableSubsectionRef &Strings, - const DebugChecksumsSubsectionRef &Checksums, uint32_t FileID) { - auto Iter = Checksums.getArray().at(FileID); - if (Iter == Checksums.getArray().end()) - return make_error(cv_error_code::no_records); - uint32_t Offset = Iter->FileNameOffset; - return Strings.getString(Offset); -} - static uint32_t getSecrelReloc() { - switch (Config->Machine) { + switch (config->machine) { case AMD64: return COFF::IMAGE_REL_AMD64_SECREL; case I386: return COFF::IMAGE_REL_I386_SECREL; case ARMNT: return COFF::IMAGE_REL_ARM_SECREL; case ARM64: return COFF::IMAGE_REL_ARM64_SECREL; default: llvm_unreachable("unknown machine type"); } } // Try to find a line table for the given offset Addr into the given chunk C. // If a line table was found, the line table, the string and checksum tables // that are used to interpret the line table, and the offset of Addr in the line // table are stored in the output arguments. Returns whether a line table was // found. -static bool findLineTable(const SectionChunk *C, uint32_t Addr, - DebugStringTableSubsectionRef &CVStrTab, - DebugChecksumsSubsectionRef &Checksums, - DebugLinesSubsectionRef &Lines, - uint32_t &OffsetInLinetable) { - ExitOnError ExitOnErr; - uint32_t SecrelReloc = getSecrelReloc(); +static bool findLineTable(const SectionChunk *c, uint32_t addr, + DebugStringTableSubsectionRef &cVStrTab, + DebugChecksumsSubsectionRef &checksums, + DebugLinesSubsectionRef &lines, + uint32_t &offsetInLinetable) { + ExitOnError exitOnErr; + uint32_t secrelReloc = getSecrelReloc(); - for (SectionChunk *DbgC : C->File->getDebugChunks()) { - if (DbgC->getSectionName() != ".debug$S") + for (SectionChunk *dbgC : c->file->getDebugChunks()) { + if (dbgC->getSectionName() != ".debug$S") continue; - // Build a mapping of SECREL relocations in DbgC that refer to C. - DenseMap Secrels; - for (const coff_relocation &R : DbgC->Relocs) { - if (R.Type != SecrelReloc) + // Build a mapping of SECREL relocations in dbgC that refer to `c`. + DenseMap secrels; + for (const coff_relocation &r : dbgC->getRelocs()) { + if (r.Type != secrelReloc) continue; - if (auto *S = dyn_cast_or_null( - C->File->getSymbols()[R.SymbolTableIndex])) - if (S->getChunk() == C) - Secrels[R.VirtualAddress] = S->getValue(); + if (auto *s = dyn_cast_or_null( + c->file->getSymbols()[r.SymbolTableIndex])) + if (s->getChunk() == c) + secrels[r.VirtualAddress] = s->getValue(); } - ArrayRef Contents = - consumeDebugMagic(DbgC->getContents(), ".debug$S"); - DebugSubsectionArray Subsections; - BinaryStreamReader Reader(Contents, support::little); - ExitOnErr(Reader.readArray(Subsections, Contents.size())); + ArrayRef contents = + SectionChunk::consumeDebugMagic(dbgC->getContents(), ".debug$S"); + DebugSubsectionArray subsections; + BinaryStreamReader reader(contents, support::little); + exitOnErr(reader.readArray(subsections, contents.size())); - for (const DebugSubsectionRecord &SS : Subsections) { - switch (SS.kind()) { + for (const DebugSubsectionRecord &ss : subsections) { + switch (ss.kind()) { case DebugSubsectionKind::StringTable: { - assert(!CVStrTab.valid() && + assert(!cVStrTab.valid() && "Encountered multiple string table subsections!"); - ExitOnErr(CVStrTab.initialize(SS.getRecordData())); + exitOnErr(cVStrTab.initialize(ss.getRecordData())); break; } case DebugSubsectionKind::FileChecksums: - assert(!Checksums.valid() && + assert(!checksums.valid() && "Encountered multiple checksum subsections!"); - ExitOnErr(Checksums.initialize(SS.getRecordData())); + exitOnErr(checksums.initialize(ss.getRecordData())); break; case DebugSubsectionKind::Lines: { - ArrayRef Bytes; - auto Ref = SS.getRecordData(); - ExitOnErr(Ref.readLongestContiguousChunk(0, Bytes)); - size_t OffsetInDbgC = Bytes.data() - DbgC->getContents().data(); + ArrayRef bytes; + auto ref = ss.getRecordData(); + exitOnErr(ref.readLongestContiguousChunk(0, bytes)); + size_t offsetInDbgC = bytes.data() - dbgC->getContents().data(); // Check whether this line table refers to C. - auto I = Secrels.find(OffsetInDbgC); - if (I == Secrels.end()) + auto i = secrels.find(offsetInDbgC); + if (i == secrels.end()) break; // Check whether this line table covers Addr in C. - DebugLinesSubsectionRef LinesTmp; - ExitOnErr(LinesTmp.initialize(BinaryStreamReader(Ref))); - uint32_t OffsetInC = I->second + LinesTmp.header()->RelocOffset; - if (Addr < OffsetInC || Addr >= OffsetInC + LinesTmp.header()->CodeSize) + DebugLinesSubsectionRef linesTmp; + exitOnErr(linesTmp.initialize(BinaryStreamReader(ref))); + uint32_t offsetInC = i->second + linesTmp.header()->RelocOffset; + if (addr < offsetInC || addr >= offsetInC + linesTmp.header()->CodeSize) break; - assert(!Lines.header() && + assert(!lines.header() && "Encountered multiple line tables for function!"); - ExitOnErr(Lines.initialize(BinaryStreamReader(Ref))); - OffsetInLinetable = Addr - OffsetInC; + exitOnErr(lines.initialize(BinaryStreamReader(ref))); + offsetInLinetable = addr - offsetInC; break; } default: break; } - if (CVStrTab.valid() && Checksums.valid() && Lines.header()) + if (cVStrTab.valid() && checksums.valid() && lines.header()) return true; } } return false; } // Use CodeView line tables to resolve a file and line number for the given // offset into the given chunk and return them, or {"", 0} if a line table was // not found. -std::pair coff::getFileLine(const SectionChunk *C, - uint32_t Addr) { - ExitOnError ExitOnErr; +std::pair coff::getFileLine(const SectionChunk *c, + uint32_t addr) { + ExitOnError exitOnErr; - DebugStringTableSubsectionRef CVStrTab; - DebugChecksumsSubsectionRef Checksums; - DebugLinesSubsectionRef Lines; - uint32_t OffsetInLinetable; + DebugStringTableSubsectionRef cVStrTab; + DebugChecksumsSubsectionRef checksums; + DebugLinesSubsectionRef lines; + uint32_t offsetInLinetable; - if (!findLineTable(C, Addr, CVStrTab, Checksums, Lines, OffsetInLinetable)) + if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable)) return {"", 0}; - Optional NameIndex; - Optional LineNumber; - for (LineColumnEntry &Entry : Lines) { - for (const LineNumberEntry &LN : Entry.LineNumbers) { - LineInfo LI(LN.Flags); - if (LN.Offset > OffsetInLinetable) { - if (!NameIndex) { - NameIndex = Entry.NameIndex; - LineNumber = LI.getStartLine(); + Optional nameIndex; + Optional lineNumber; + for (LineColumnEntry &entry : lines) { + for (const LineNumberEntry &ln : entry.LineNumbers) { + LineInfo li(ln.Flags); + if (ln.Offset > offsetInLinetable) { + if (!nameIndex) { + nameIndex = entry.NameIndex; + lineNumber = li.getStartLine(); } - StringRef Filename = - ExitOnErr(getFileName(CVStrTab, Checksums, *NameIndex)); - return {Filename, *LineNumber}; + StringRef filename = + exitOnErr(getFileName(cVStrTab, checksums, *nameIndex)); + return {filename, *lineNumber}; } - NameIndex = Entry.NameIndex; - LineNumber = LI.getStartLine(); + nameIndex = entry.NameIndex; + lineNumber = li.getStartLine(); } } - if (!NameIndex) + if (!nameIndex) return {"", 0}; - StringRef Filename = ExitOnErr(getFileName(CVStrTab, Checksums, *NameIndex)); - return {Filename, *LineNumber}; + StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex)); + return {filename, *lineNumber}; } Index: vendor/lld/dist/COFF/PDB.h =================================================================== --- vendor/lld/dist/COFF/PDB.h (revision 351287) +++ vendor/lld/dist/COFF/PDB.h (revision 351288) @@ -1,38 +1,37 @@ //===- PDB.h ----------------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #ifndef LLD_COFF_PDB_H #define LLD_COFF_PDB_H #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" namespace llvm { namespace codeview { union DebugInfo; } } namespace lld { namespace coff { class OutputSection; class SectionChunk; class SymbolTable; -void createPDB(SymbolTable *Symtab, - llvm::ArrayRef OutputSections, - llvm::ArrayRef SectionTable, - llvm::codeview::DebugInfo *BuildId); +void createPDB(SymbolTable *symtab, + llvm::ArrayRef outputSections, + llvm::ArrayRef sectionTable, + llvm::codeview::DebugInfo *buildId); -std::pair getFileLine(const SectionChunk *C, - uint32_t Addr); +std::pair getFileLine(const SectionChunk *c, + uint32_t addr); } } #endif Index: vendor/lld/dist/COFF/SymbolTable.cpp =================================================================== --- vendor/lld/dist/COFF/SymbolTable.cpp (revision 351287) +++ vendor/lld/dist/COFF/SymbolTable.cpp (revision 351288) @@ -1,548 +1,603 @@ //===- SymbolTable.cpp ----------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "SymbolTable.h" #include "Config.h" #include "Driver.h" #include "LTO.h" #include "PDB.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "lld/Common/Timer.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/Object/WindowsMachineFlag.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; namespace lld { namespace coff { -static Timer LTOTimer("LTO", Timer::root()); +static Timer ltoTimer("LTO", Timer::root()); -SymbolTable *Symtab; +SymbolTable *symtab; -void SymbolTable::addFile(InputFile *File) { - log("Reading " + toString(File)); - File->parse(); +void SymbolTable::addFile(InputFile *file) { + log("Reading " + toString(file)); + file->parse(); - MachineTypes MT = File->getMachineType(); - if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) { - Config->Machine = MT; - } else if (MT != IMAGE_FILE_MACHINE_UNKNOWN && Config->Machine != MT) { - error(toString(File) + ": machine type " + machineToStr(MT) + - " conflicts with " + machineToStr(Config->Machine)); + MachineTypes mt = file->getMachineType(); + if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) { + config->machine = mt; + } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && config->machine != mt) { + error(toString(file) + ": machine type " + machineToStr(mt) + + " conflicts with " + machineToStr(config->machine)); return; } - if (auto *F = dyn_cast(File)) { - ObjFile::Instances.push_back(F); - } else if (auto *F = dyn_cast(File)) { - BitcodeFile::Instances.push_back(F); - } else if (auto *F = dyn_cast(File)) { - ImportFile::Instances.push_back(F); + if (auto *f = dyn_cast(file)) { + ObjFile::instances.push_back(f); + } else if (auto *f = dyn_cast(file)) { + BitcodeFile::instances.push_back(f); + } else if (auto *f = dyn_cast(file)) { + ImportFile::instances.push_back(f); } - StringRef S = File->getDirectives(); - if (S.empty()) - return; - - log("Directives: " + toString(File) + ": " + S); - Driver->parseDirectives(S); + driver->parseDirectives(file); } -static void errorOrWarn(const Twine &S) { - if (Config->ForceUnresolved) - warn(S); +static void errorOrWarn(const Twine &s) { + if (config->forceUnresolved) + warn(s); else - error(S); + error(s); } // Returns the symbol in SC whose value is <= Addr that is closest to Addr. // This is generally the global variable or function whose definition contains // Addr. -static Symbol *getSymbol(SectionChunk *SC, uint32_t Addr) { - DefinedRegular *Candidate = nullptr; +static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) { + DefinedRegular *candidate = nullptr; - for (Symbol *S : SC->File->getSymbols()) { - auto *D = dyn_cast_or_null(S); - if (!D || D->getChunk() != SC || D->getValue() > Addr || - (Candidate && D->getValue() < Candidate->getValue())) + for (Symbol *s : sc->file->getSymbols()) { + auto *d = dyn_cast_or_null(s); + if (!d || !d->data || d->getChunk() != sc || d->getValue() > addr || + (candidate && d->getValue() < candidate->getValue())) continue; - Candidate = D; + candidate = d; } - return Candidate; + return candidate; } -std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) { +// Given a file and the index of a symbol in that file, returns a description +// of all references to that symbol from that file. If no debug information is +// available, returns just the name of the file, else one string per actual +// reference as described in the debug info. +std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex) { struct Location { - Symbol *Sym; - std::pair FileLine; + Symbol *sym; + std::pair fileLine; }; - std::vector Locations; + std::vector locations; - for (Chunk *C : File->getChunks()) { - auto *SC = dyn_cast(C); - if (!SC) + for (Chunk *c : file->getChunks()) { + auto *sc = dyn_cast(c); + if (!sc) continue; - for (const coff_relocation &R : SC->Relocs) { - if (R.SymbolTableIndex != SymIndex) + for (const coff_relocation &r : sc->getRelocs()) { + if (r.SymbolTableIndex != symIndex) continue; - std::pair FileLine = - getFileLine(SC, R.VirtualAddress); - Symbol *Sym = getSymbol(SC, R.VirtualAddress); - if (!FileLine.first.empty() || Sym) - Locations.push_back({Sym, FileLine}); + std::pair fileLine = + getFileLine(sc, r.VirtualAddress); + Symbol *sym = getSymbol(sc, r.VirtualAddress); + if (!fileLine.first.empty() || sym) + locations.push_back({sym, fileLine}); } } - if (Locations.empty()) - return "\n>>> referenced by " + toString(File); + if (locations.empty()) + return std::vector({"\n>>> referenced by " + toString(file)}); - std::string Out; - llvm::raw_string_ostream OS(Out); - for (Location Loc : Locations) { - OS << "\n>>> referenced by "; - if (!Loc.FileLine.first.empty()) - OS << Loc.FileLine.first << ":" << Loc.FileLine.second + std::vector symbolLocations(locations.size()); + size_t i = 0; + for (Location loc : locations) { + llvm::raw_string_ostream os(symbolLocations[i++]); + os << "\n>>> referenced by "; + if (!loc.fileLine.first.empty()) + os << loc.fileLine.first << ":" << loc.fileLine.second << "\n>>> "; - OS << toString(File); - if (Loc.Sym) - OS << ":(" << toString(*Loc.Sym) << ')'; + os << toString(file); + if (loc.sym) + os << ":(" << toString(*loc.sym) << ')'; } - return OS.str(); + return symbolLocations; } +// For an undefined symbol, stores all files referencing it and the index of +// the undefined symbol in each file. +struct UndefinedDiag { + Symbol *sym; + struct File { + ObjFile *oFile; + uint64_t symIndex; + }; + std::vector files; +}; + +static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) { + std::string out; + llvm::raw_string_ostream os(out); + os << "undefined symbol: " << toString(*undefDiag.sym); + + const size_t maxUndefReferences = 10; + size_t i = 0, numRefs = 0; + for (const UndefinedDiag::File &ref : undefDiag.files) { + std::vector symbolLocations = + getSymbolLocations(ref.oFile, ref.symIndex); + numRefs += symbolLocations.size(); + for (const std::string &s : symbolLocations) { + if (i >= maxUndefReferences) + break; + os << s; + i++; + } + } + if (i < numRefs) + os << "\n>>> referenced " << numRefs - i << " more times"; + errorOrWarn(os.str()); +} + void SymbolTable::loadMinGWAutomaticImports() { - for (auto &I : SymMap) { - Symbol *Sym = I.second; - auto *Undef = dyn_cast(Sym); - if (!Undef) + for (auto &i : symMap) { + Symbol *sym = i.second; + auto *undef = dyn_cast(sym); + if (!undef) continue; - if (!Sym->IsUsedInRegularObj) + if (!sym->isUsedInRegularObj) continue; - StringRef Name = Undef->getName(); + StringRef name = undef->getName(); - if (Name.startswith("__imp_")) + if (name.startswith("__imp_")) continue; // If we have an undefined symbol, but we have a Lazy representing a // symbol we could load from file, make sure to load that. - Lazy *L = dyn_cast_or_null(find(("__imp_" + Name).str())); - if (!L || L->PendingArchiveLoad) + Lazy *l = dyn_cast_or_null(find(("__imp_" + name).str())); + if (!l || l->pendingArchiveLoad) continue; - log("Loading lazy " + L->getName() + " from " + L->File->getName() + + log("Loading lazy " + l->getName() + " from " + l->file->getName() + " for automatic import"); - L->PendingArchiveLoad = true; - L->File->addMember(&L->Sym); + l->pendingArchiveLoad = true; + l->file->addMember(&l->sym); } } -bool SymbolTable::handleMinGWAutomaticImport(Symbol *Sym, StringRef Name) { - if (Name.startswith("__imp_")) +bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) { + if (name.startswith("__imp_")) return false; - Defined *Imp = dyn_cast_or_null(find(("__imp_" + Name).str())); - if (!Imp) + Defined *imp = dyn_cast_or_null(find(("__imp_" + name).str())); + if (!imp) return false; // Replace the reference directly to a variable with a reference // to the import address table instead. This obviously isn't right, - // but we mark the symbol as IsRuntimePseudoReloc, and a later pass + // but we mark the symbol as isRuntimePseudoReloc, and a later pass // will add runtime pseudo relocations for every relocation against // this Symbol. The runtime pseudo relocation framework expects the // reference itself to point at the IAT entry. - size_t ImpSize = 0; - if (isa(Imp)) { - log("Automatically importing " + Name + " from " + - cast(Imp)->getDLLName()); - ImpSize = sizeof(DefinedImportData); - } else if (isa(Imp)) { - log("Automatically importing " + Name + " from " + - toString(cast(Imp)->File)); - ImpSize = sizeof(DefinedRegular); + size_t impSize = 0; + if (isa(imp)) { + log("Automatically importing " + name + " from " + + cast(imp)->getDLLName()); + impSize = sizeof(DefinedImportData); + } else if (isa(imp)) { + log("Automatically importing " + name + " from " + + toString(cast(imp)->file)); + impSize = sizeof(DefinedRegular); } else { - warn("unable to automatically import " + Name + " from " + Imp->getName() + - " from " + toString(cast(Imp)->File) + + warn("unable to automatically import " + name + " from " + imp->getName() + + " from " + toString(cast(imp)->file) + "; unexpected symbol type"); return false; } - Sym->replaceKeepingName(Imp, ImpSize); - Sym->IsRuntimePseudoReloc = true; + sym->replaceKeepingName(imp, impSize); + sym->isRuntimePseudoReloc = true; // There may exist symbols named .refptr. which only consist // of a single pointer to . If it turns out is // automatically imported, we don't need to keep the .refptr. // pointer at all, but redirect all accesses to it to the IAT entry // for __imp_ instead, and drop the whole .refptr. chunk. - DefinedRegular *Refptr = - dyn_cast_or_null(find((".refptr." + Name).str())); - if (Refptr && Refptr->getChunk()->getSize() == Config->Wordsize) { - SectionChunk *SC = dyn_cast_or_null(Refptr->getChunk()); - if (SC && SC->Relocs.size() == 1 && *SC->symbols().begin() == Sym) { - log("Replacing .refptr." + Name + " with " + Imp->getName()); - Refptr->getChunk()->Live = false; - Refptr->replaceKeepingName(Imp, ImpSize); + DefinedRegular *refptr = + dyn_cast_or_null(find((".refptr." + name).str())); + if (refptr && refptr->getChunk()->getSize() == config->wordsize) { + SectionChunk *sc = dyn_cast_or_null(refptr->getChunk()); + if (sc && sc->getRelocs().size() == 1 && *sc->symbols().begin() == sym) { + log("Replacing .refptr." + name + " with " + imp->getName()); + refptr->getChunk()->live = false; + refptr->replaceKeepingName(imp, impSize); } } return true; } void SymbolTable::reportRemainingUndefines() { - SmallPtrSet Undefs; - DenseMap LocalImports; + SmallPtrSet undefs; + DenseMap localImports; - for (auto &I : SymMap) { - Symbol *Sym = I.second; - auto *Undef = dyn_cast(Sym); - if (!Undef) + for (auto &i : symMap) { + Symbol *sym = i.second; + auto *undef = dyn_cast(sym); + if (!undef) continue; - if (!Sym->IsUsedInRegularObj) + if (!sym->isUsedInRegularObj) continue; - StringRef Name = Undef->getName(); + StringRef name = undef->getName(); // A weak alias may have been resolved, so check for that. - if (Defined *D = Undef->getWeakAlias()) { + if (Defined *d = undef->getWeakAlias()) { // 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(D)) - memcpy(Sym, D, sizeof(DefinedRegular)); - else if (isa(D)) - memcpy(Sym, D, sizeof(DefinedAbsolute)); + if (isa(d)) + memcpy(sym, d, sizeof(DefinedRegular)); + else if (isa(d)) + memcpy(sym, d, sizeof(DefinedAbsolute)); else - memcpy(Sym, D, sizeof(SymbolUnion)); + 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(Imp)) { - auto *D = cast(Imp); - replaceSymbol(Sym, Name, D); - LocalImportChunks.push_back(cast(Sym)->getChunk()); - LocalImports[Sym] = D; + if (name.startswith("__imp_")) { + Symbol *imp = find(name.substr(strlen("__imp_"))); + if (imp && isa(imp)) { + auto *d = cast(imp); + replaceSymbol(sym, name, d); + localImportChunks.push_back(cast(sym)->getChunk()); + localImports[sym] = d; continue; } } // We don't want to report missing Microsoft precompiled headers symbols. // A proper message will be emitted instead in PDBLinker::aquirePrecompObj - if (Name.contains("_PchSym_")) + if (name.contains("_PchSym_")) continue; - if (Config->MinGW && handleMinGWAutomaticImport(Sym, Name)) + if (config->mingw && handleMinGWAutomaticImport(sym, name)) continue; // Remaining undefined symbols are not fatal if /force is specified. // They are replaced with dummy defined symbols. - if (Config->ForceUnresolved) - replaceSymbol(Sym, Name, 0); - Undefs.insert(Sym); + if (config->forceUnresolved) + replaceSymbol(sym, name, 0); + undefs.insert(sym); } - if (Undefs.empty() && LocalImports.empty()) + if (undefs.empty() && localImports.empty()) return; - for (Symbol *B : Config->GCRoot) { - if (Undefs.count(B)) - errorOrWarn(": undefined symbol: " + toString(*B)); - if (Config->WarnLocallyDefinedImported) - if (Symbol *Imp = LocalImports.lookup(B)) - warn(": locally defined symbol imported: " + toString(*Imp) + - " (defined in " + toString(Imp->getFile()) + ") [LNK4217]"); + for (Symbol *b : config->gcroot) { + if (undefs.count(b)) + errorOrWarn(": undefined symbol: " + toString(*b)); + if (config->warnLocallyDefinedImported) + if (Symbol *imp = localImports.lookup(b)) + warn(": locally defined symbol imported: " + toString(*imp) + + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); } - for (ObjFile *File : ObjFile::Instances) { - size_t SymIndex = (size_t)-1; - for (Symbol *Sym : File->getSymbols()) { - ++SymIndex; - if (!Sym) + std::vector undefDiags; + DenseMap firstDiag; + + for (ObjFile *file : ObjFile::instances) { + size_t symIndex = (size_t)-1; + for (Symbol *sym : file->getSymbols()) { + ++symIndex; + if (!sym) continue; - if (Undefs.count(Sym)) - errorOrWarn("undefined symbol: " + toString(*Sym) + - getSymbolLocations(File, SymIndex)); - if (Config->WarnLocallyDefinedImported) - if (Symbol *Imp = LocalImports.lookup(Sym)) - warn(toString(File) + - ": locally defined symbol imported: " + toString(*Imp) + - " (defined in " + toString(Imp->getFile()) + ") [LNK4217]"); + if (undefs.count(sym)) { + auto it = firstDiag.find(sym); + if (it == firstDiag.end()) { + firstDiag[sym] = undefDiags.size(); + undefDiags.push_back({sym, {{file, symIndex}}}); + } else { + undefDiags[it->second].files.push_back({file, symIndex}); + } + } + if (config->warnLocallyDefinedImported) + if (Symbol *imp = localImports.lookup(sym)) + warn(toString(file) + + ": locally defined symbol imported: " + toString(*imp) + + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); } } + + for (const UndefinedDiag& undefDiag : undefDiags) + reportUndefinedSymbol(undefDiag); } -std::pair SymbolTable::insert(StringRef Name) { - bool Inserted = false; - Symbol *&Sym = SymMap[CachedHashStringRef(Name)]; - if (!Sym) { - Sym = reinterpret_cast(make()); - Sym->IsUsedInRegularObj = false; - Sym->PendingArchiveLoad = false; - Inserted = true; +std::pair SymbolTable::insert(StringRef name) { + bool inserted = false; + Symbol *&sym = symMap[CachedHashStringRef(name)]; + if (!sym) { + sym = reinterpret_cast(make()); + sym->isUsedInRegularObj = false; + sym->pendingArchiveLoad = false; + inserted = true; } - return {Sym, Inserted}; + return {sym, inserted}; } -std::pair SymbolTable::insert(StringRef Name, InputFile *File) { - std::pair Result = insert(Name); - if (!File || !isa(File)) - Result.first->IsUsedInRegularObj = true; - return Result; +std::pair SymbolTable::insert(StringRef name, InputFile *file) { + std::pair result = insert(name); + if (!file || !isa(file)) + result.first->isUsedInRegularObj = true; + return result; } -Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F, - bool IsWeakAlias) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(Name, F); - if (WasInserted || (isa(S) && IsWeakAlias)) { - replaceSymbol(S, Name); - return S; +Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f, + bool isWeakAlias) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name, f); + if (wasInserted || (isa(s) && isWeakAlias)) { + replaceSymbol(s, name); + return s; } - if (auto *L = dyn_cast(S)) { - if (!S->PendingArchiveLoad) { - S->PendingArchiveLoad = true; - L->File->addMember(&L->Sym); + if (auto *l = dyn_cast(s)) { + if (!s->pendingArchiveLoad) { + s->pendingArchiveLoad = true; + l->file->addMember(&l->sym); } } - return S; + return s; } -void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) { - StringRef Name = Sym.getName(); - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(Name); - if (WasInserted) { - replaceSymbol(S, F, Sym); +void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol sym) { + StringRef name = sym.getName(); + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name); + if (wasInserted) { + replaceSymbol(s, f, sym); return; } - auto *U = dyn_cast(S); - if (!U || U->WeakAlias || S->PendingArchiveLoad) + auto *u = dyn_cast(s); + if (!u || u->weakAlias || s->pendingArchiveLoad) return; - S->PendingArchiveLoad = true; - F->addMember(&Sym); + s->pendingArchiveLoad = true; + f->addMember(&sym); } -void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) { - std::string Msg = "duplicate symbol: " + toString(*Existing) + " in " + - toString(Existing->getFile()) + " and in " + - toString(NewFile); +void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) { + std::string msg = "duplicate symbol: " + toString(*existing) + " in " + + toString(existing->getFile()) + " and in " + + toString(newFile); - if (Config->ForceMultiple) - warn(Msg); + if (config->forceMultiple) + warn(msg); else - error(Msg); + error(msg); } -Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, nullptr); - S->IsUsedInRegularObj = true; - if (WasInserted || isa(S) || isa(S)) - replaceSymbol(S, N, Sym); - else if (!isa(S)) - reportDuplicate(S, nullptr); - return S; +Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) + replaceSymbol(s, n, sym); + else if (!isa(s)) + reportDuplicate(s, nullptr); + return s; } -Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, nullptr); - S->IsUsedInRegularObj = true; - if (WasInserted || isa(S) || isa(S)) - replaceSymbol(S, N, VA); - else if (!isa(S)) - reportDuplicate(S, nullptr); - return S; +Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) + replaceSymbol(s, n, va); + else if (!isa(s)) + reportDuplicate(s, nullptr); + return s; } -Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, nullptr); - S->IsUsedInRegularObj = true; - if (WasInserted || isa(S) || isa(S)) - replaceSymbol(S, N, C); - else if (!isa(S)) - reportDuplicate(S, nullptr); - return S; +Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) + replaceSymbol(s, n, c); + else if (!isa(s)) + reportDuplicate(s, nullptr); + return s; } -Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, - const coff_symbol_generic *Sym, - SectionChunk *C) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, F); - if (WasInserted || !isa(S)) - replaceSymbol(S, F, N, /*IsCOMDAT*/ false, - /*IsExternal*/ true, Sym, C); +Symbol *SymbolTable::addRegular(InputFile *f, StringRef n, + const coff_symbol_generic *sym, + SectionChunk *c) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, f); + if (wasInserted || !isa(s)) + replaceSymbol(s, f, n, /*IsCOMDAT*/ false, + /*IsExternal*/ true, sym, c); else - reportDuplicate(S, F); - return S; + reportDuplicate(s, f); + return s; } -std::pair -SymbolTable::addComdat(InputFile *F, StringRef N, - const coff_symbol_generic *Sym) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, F); - if (WasInserted || !isa(S)) { - replaceSymbol(S, F, N, /*IsCOMDAT*/ true, - /*IsExternal*/ true, Sym, nullptr); - return {S, true}; +std::pair +SymbolTable::addComdat(InputFile *f, StringRef n, + const coff_symbol_generic *sym) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, f); + if (wasInserted || !isa(s)) { + replaceSymbol(s, f, n, /*IsCOMDAT*/ true, + /*IsExternal*/ true, sym, nullptr); + return {cast(s), true}; } - if (!cast(S)->isCOMDAT()) - reportDuplicate(S, F); - return {S, false}; + auto *existingSymbol = cast(s); + if (!existingSymbol->isCOMDAT) + reportDuplicate(s, f); + return {existingSymbol, false}; } -Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size, - const coff_symbol_generic *Sym, CommonChunk *C) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, F); - if (WasInserted || !isa(S)) - replaceSymbol(S, F, N, Size, Sym, C); - else if (auto *DC = dyn_cast(S)) - if (Size > DC->getSize()) - replaceSymbol(S, F, N, Size, Sym, C); - return S; +Symbol *SymbolTable::addCommon(InputFile *f, StringRef n, uint64_t size, + const coff_symbol_generic *sym, CommonChunk *c) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, f); + if (wasInserted || !isa(s)) + replaceSymbol(s, f, n, size, sym, c); + else if (auto *dc = dyn_cast(s)) + if (size > dc->getSize()) + replaceSymbol(s, f, n, size, sym, c); + return s; } -Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, nullptr); - S->IsUsedInRegularObj = true; - if (WasInserted || isa(S) || isa(S)) { - replaceSymbol(S, N, F); - return S; +Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) { + replaceSymbol(s, n, f); + return s; } - reportDuplicate(S, F); + reportDuplicate(s, f); return nullptr; } -Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID, - uint16_t Machine) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(Name, nullptr); - S->IsUsedInRegularObj = true; - if (WasInserted || isa(S) || isa(S)) { - replaceSymbol(S, Name, ID, Machine); - return S; +Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id, + uint16_t machine) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) { + replaceSymbol(s, name, id, machine); + return s; } - reportDuplicate(S, ID->File); + reportDuplicate(s, id->file); return nullptr; } std::vector SymbolTable::getChunks() { - std::vector Res; - for (ObjFile *File : ObjFile::Instances) { - ArrayRef V = File->getChunks(); - Res.insert(Res.end(), V.begin(), V.end()); + std::vector res; + for (ObjFile *file : ObjFile::instances) { + ArrayRef v = file->getChunks(); + res.insert(res.end(), v.begin(), v.end()); } - return Res; + return res; } -Symbol *SymbolTable::find(StringRef Name) { - return SymMap.lookup(CachedHashStringRef(Name)); +Symbol *SymbolTable::find(StringRef name) { + return symMap.lookup(CachedHashStringRef(name)); } -Symbol *SymbolTable::findUnderscore(StringRef Name) { - if (Config->Machine == I386) - return find(("_" + Name).str()); - return find(Name); +Symbol *SymbolTable::findUnderscore(StringRef name) { + if (config->machine == I386) + return find(("_" + name).str()); + return find(name); } -StringRef SymbolTable::findByPrefix(StringRef Prefix) { - for (auto Pair : SymMap) { - StringRef Name = Pair.first.val(); - if (Name.startswith(Prefix)) - return Name; +// Return all symbols that start with Prefix, possibly ignoring the first +// character of Prefix or the first character symbol. +std::vector SymbolTable::getSymsWithPrefix(StringRef prefix) { + std::vector syms; + for (auto pair : symMap) { + StringRef name = pair.first.val(); + if (name.startswith(prefix) || name.startswith(prefix.drop_front()) || + name.drop_front().startswith(prefix) || + name.drop_front().startswith(prefix.drop_front())) { + syms.push_back(pair.second); + } } - return ""; + return syms; } -StringRef SymbolTable::findMangle(StringRef Name) { - if (Symbol *Sym = find(Name)) - if (!isa(Sym)) - return Name; - if (Config->Machine != I386) - return findByPrefix(("?" + Name + "@@Y").str()); - if (!Name.startswith("_")) - return ""; +Symbol *SymbolTable::findMangle(StringRef name) { + if (Symbol *sym = find(name)) + if (!isa(sym)) + return sym; + + // Efficient fuzzy string lookup is impossible with a hash table, so iterate + // the symbol table once and collect all possibly matching symbols into this + // vector. Then compare each possibly matching symbol with each possible + // mangling. + std::vector syms = getSymsWithPrefix(name); + auto findByPrefix = [&syms](const Twine &t) -> Symbol * { + std::string prefix = t.str(); + for (auto *s : syms) + if (s->getName().startswith(prefix)) + return s; + return nullptr; + }; + + // For non-x86, just look for C++ functions. + if (config->machine != I386) + return findByPrefix("?" + name + "@@Y"); + + if (!name.startswith("_")) + return nullptr; // Search for x86 stdcall function. - StringRef S = findByPrefix((Name + "@").str()); - if (!S.empty()) - return S; + if (Symbol *s = findByPrefix(name + "@")) + return s; // Search for x86 fastcall function. - S = findByPrefix(("@" + Name.substr(1) + "@").str()); - if (!S.empty()) - return S; + if (Symbol *s = findByPrefix("@" + name.substr(1) + "@")) + return s; // Search for x86 vectorcall function. - S = findByPrefix((Name.substr(1) + "@@").str()); - if (!S.empty()) - return S; + if (Symbol *s = findByPrefix(name.substr(1) + "@@")) + return s; // Search for x86 C++ non-member function. - return findByPrefix(("?" + Name.substr(1) + "@@Y").str()); + return findByPrefix("?" + name.substr(1) + "@@Y"); } -void SymbolTable::mangleMaybe(Symbol *B) { - auto *U = dyn_cast(B); - if (!U || U->WeakAlias) - return; - StringRef Alias = findMangle(U->getName()); - if (!Alias.empty()) { - log(U->getName() + " aliased to " + Alias); - U->WeakAlias = addUndefined(Alias); - } +Symbol *SymbolTable::addUndefined(StringRef name) { + return addUndefined(name, nullptr, false); } -Symbol *SymbolTable::addUndefined(StringRef Name) { - return addUndefined(Name, nullptr, false); -} - std::vector SymbolTable::compileBitcodeFiles() { - LTO.reset(new BitcodeCompiler); - for (BitcodeFile *F : BitcodeFile::Instances) - LTO->add(*F); - return LTO->compile(); + lto.reset(new BitcodeCompiler); + for (BitcodeFile *f : BitcodeFile::instances) + lto->add(*f); + return lto->compile(); } void SymbolTable::addCombinedLTOObjects() { - if (BitcodeFile::Instances.empty()) + if (BitcodeFile::instances.empty()) return; - ScopedTimer T(LTOTimer); - for (StringRef Object : compileBitcodeFiles()) { - auto *Obj = make(MemoryBufferRef(Object, "lto.tmp")); - Obj->parse(); - ObjFile::Instances.push_back(Obj); + ScopedTimer t(ltoTimer); + for (StringRef object : compileBitcodeFiles()) { + auto *obj = make(MemoryBufferRef(object, "lto.tmp")); + obj->parse(); + ObjFile::instances.push_back(obj); } } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/SymbolTable.h =================================================================== --- vendor/lld/dist/COFF/SymbolTable.h (revision 351287) +++ vendor/lld/dist/COFF/SymbolTable.h (revision 351288) @@ -1,131 +1,131 @@ //===- SymbolTable.h --------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #ifndef LLD_COFF_SYMBOL_TABLE_H #define LLD_COFF_SYMBOL_TABLE_H #include "InputFiles.h" #include "LTO.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/Support/raw_ostream.h" namespace llvm { struct LTOCodeGenerator; } namespace lld { namespace coff { class Chunk; class CommonChunk; class Defined; class DefinedAbsolute; +class DefinedRegular; class DefinedRelative; class Lazy; class SectionChunk; class Symbol; // SymbolTable is a bucket of all known symbols, including defined, // undefined, or lazy symbols (the last one is symbols in archive // files whose archive members are not yet loaded). // // We put all symbols of all files to a SymbolTable, and the // SymbolTable selects the "best" symbols if there are name // conflicts. For example, obviously, a defined symbol is better than // an undefined symbol. Or, if there's a conflict between a lazy and a // undefined, it'll read an archive member to read a real definition // to replace the lazy symbol. The logic is implemented in the // add*() functions, which are called by input files as they are parsed. // There is one add* function per symbol type. class SymbolTable { public: - void addFile(InputFile *File); + void addFile(InputFile *file); // Try to resolve any undefined symbols and update the symbol table // accordingly, then print an error message for any remaining undefined // symbols. void reportRemainingUndefines(); void loadMinGWAutomaticImports(); - bool handleMinGWAutomaticImport(Symbol *Sym, StringRef Name); + bool handleMinGWAutomaticImport(Symbol *sym, StringRef name); // Returns a list of chunks of selected symbols. std::vector getChunks(); // Returns a symbol for a given name. Returns a nullptr if not found. - Symbol *find(StringRef Name); - Symbol *findUnderscore(StringRef Name); + Symbol *find(StringRef name); + Symbol *findUnderscore(StringRef name); // Occasionally we have to resolve an undefined symbol to its // mangled symbol. This function tries to find a mangled name // for U from the symbol table, and if found, set the symbol as // a weak alias for U. - void mangleMaybe(Symbol *B); - StringRef findMangle(StringRef Name); + Symbol *findMangle(StringRef name); // Build a set of COFF objects representing the combined contents of // BitcodeFiles and add them to the symbol table. Called after all files are // added and before the writer writes results to a file. void addCombinedLTOObjects(); std::vector compileBitcodeFiles(); // Creates an Undefined symbol for a given name. - Symbol *addUndefined(StringRef Name); + Symbol *addUndefined(StringRef name); - Symbol *addSynthetic(StringRef N, Chunk *C); - Symbol *addAbsolute(StringRef N, uint64_t VA); + Symbol *addSynthetic(StringRef n, Chunk *c); + Symbol *addAbsolute(StringRef n, uint64_t va); - 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, - const llvm::object::coff_symbol_generic *S = nullptr, - SectionChunk *C = nullptr); - std::pair - 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); + 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, + const llvm::object::coff_symbol_generic *s = nullptr, + SectionChunk *c = nullptr); + std::pair + 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); - void reportDuplicate(Symbol *Existing, InputFile *NewFile); + void reportDuplicate(Symbol *existing, InputFile *newFile); // A list of chunks which to be added to .rdata. - std::vector LocalImportChunks; + std::vector localImportChunks; // Iterates symbols in non-determinstic hash table order. - template void forEachSymbol(T Callback) { - for (auto &Pair : SymMap) - Callback(Pair.second); + template void forEachSymbol(T callback) { + for (auto &pair : symMap) + callback(pair.second); } private: /// Inserts symbol if not already present. - std::pair insert(StringRef Name); - /// Same as insert(Name), but also sets IsUsedInRegularObj. - std::pair insert(StringRef Name, InputFile *F); - StringRef findByPrefix(StringRef Prefix); + std::pair insert(StringRef name); + /// Same as insert(Name), but also sets isUsedInRegularObj. + std::pair insert(StringRef name, InputFile *f); - llvm::DenseMap SymMap; - std::unique_ptr LTO; + std::vector getSymsWithPrefix(StringRef prefix); + + llvm::DenseMap symMap; + std::unique_ptr lto; }; -extern SymbolTable *Symtab; +extern SymbolTable *symtab; -std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex); +std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex); } // namespace coff } // namespace lld #endif Index: vendor/lld/dist/COFF/Symbols.cpp =================================================================== --- vendor/lld/dist/COFF/Symbols.cpp (revision 351287) +++ vendor/lld/dist/COFF/Symbols.cpp (revision 351288) @@ -1,107 +1,117 @@ //===- Symbols.cpp --------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "Symbols.h" #include "InputFiles.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; using namespace llvm::object; +using namespace lld::coff; + +static_assert(sizeof(SymbolUnion) <= 48, + "symbols should be optimized for memory usage"); + // Returns a symbol name for an error message. -std::string lld::toString(coff::Symbol &B) { - if (Optional S = lld::demangleMSVC(B.getName())) - return ("\"" + *S + "\" (" + B.getName() + ")").str(); - return B.getName(); +std::string lld::toString(coff::Symbol &b) { + if (config->demangle) + if (Optional s = lld::demangleMSVC(b.getName())) + return *s; + return b.getName(); } namespace lld { namespace coff { 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 // symbol index. And because they are not external, no one can refer them by // name. Object files contain lots of non-external symbols, and creating // StringRefs for them (which involves lots of strlen() on the string table) // is a waste of time. - if (Name.empty()) { - auto *D = cast(this); - cast(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name); + if (nameData == nullptr) { + auto *d = cast(this); + StringRef nameStr; + cast(d->file)->getCOFFObj()->getSymbolName(d->sym, nameStr); + nameData = nameStr.data(); + nameSize = nameStr.size(); + assert(nameSize == nameStr.size() && "name length truncated"); } - return Name; + return StringRef(nameData, nameSize); } InputFile *Symbol::getFile() { - if (auto *Sym = dyn_cast(this)) - return Sym->File; - if (auto *Sym = dyn_cast(this)) - return Sym->File; + if (auto *sym = dyn_cast(this)) + return sym->file; + if (auto *sym = dyn_cast(this)) + return sym->file; return nullptr; } bool Symbol::isLive() const { - if (auto *R = dyn_cast(this)) - return R->getChunk()->Live; - if (auto *Imp = dyn_cast(this)) - return Imp->File->Live; - if (auto *Imp = dyn_cast(this)) - return Imp->WrappedSym->File->ThunkLive; + if (auto *r = dyn_cast(this)) + return r->getChunk()->live; + if (auto *imp = dyn_cast(this)) + return imp->file->live; + if (auto *imp = dyn_cast(this)) + return imp->wrappedSym->file->thunkLive; // Assume any other kind of symbol is live. return true; } // MinGW specific. -void Symbol::replaceKeepingName(Symbol *Other, size_t Size) { - StringRef OrigName = Name; - memcpy(this, Other, Size); - Name = OrigName; +void Symbol::replaceKeepingName(Symbol *other, size_t size) { + StringRef origName = getName(); + memcpy(this, other, size); + nameData = origName.data(); + nameSize = origName.size(); } COFFSymbolRef DefinedCOFF::getCOFFSymbol() { - size_t SymSize = cast(File)->getCOFFObj()->getSymbolTableEntrySize(); - if (SymSize == sizeof(coff_symbol16)) - return COFFSymbolRef(reinterpret_cast(Sym)); - assert(SymSize == sizeof(coff_symbol32)); - return COFFSymbolRef(reinterpret_cast(Sym)); + size_t symSize = cast(file)->getCOFFObj()->getSymbolTableEntrySize(); + if (symSize == sizeof(coff_symbol16)) + return COFFSymbolRef(reinterpret_cast(sym)); + assert(symSize == sizeof(coff_symbol32)); + return COFFSymbolRef(reinterpret_cast(sym)); } -uint16_t DefinedAbsolute::NumOutputSections; +uint16_t DefinedAbsolute::numOutputSections; -static Chunk *makeImportThunk(DefinedImportData *S, uint16_t Machine) { - if (Machine == AMD64) - return make(S); - if (Machine == I386) - return make(S); - if (Machine == ARM64) - return make(S); - assert(Machine == ARMNT); - return make(S); +static Chunk *makeImportThunk(DefinedImportData *s, uint16_t machine) { + if (machine == AMD64) + return make(s); + if (machine == I386) + return make(s); + if (machine == ARM64) + return make(s); + assert(machine == ARMNT); + return make(s); } -DefinedImportThunk::DefinedImportThunk(StringRef Name, DefinedImportData *S, - uint16_t Machine) - : Defined(DefinedImportThunkKind, Name), WrappedSym(S), - Data(makeImportThunk(S, Machine)) {} +DefinedImportThunk::DefinedImportThunk(StringRef name, DefinedImportData *s, + uint16_t machine) + : Defined(DefinedImportThunkKind, name), wrappedSym(s), + data(makeImportThunk(s, machine)) {} Defined *Undefined::getWeakAlias() { // A weak alias may be a weak alias to another symbol, so check recursively. - for (Symbol *A = WeakAlias; A; A = cast(A)->WeakAlias) - if (auto *D = dyn_cast(A)) - return D; + for (Symbol *a = weakAlias; a; a = cast(a)->weakAlias) + if (auto *d = dyn_cast(a)) + return d; return nullptr; } } // namespace coff } // namespace lld Index: vendor/lld/dist/COFF/Symbols.h =================================================================== --- vendor/lld/dist/COFF/Symbols.h (revision 351287) +++ vendor/lld/dist/COFF/Symbols.h (revision 351288) @@ -1,436 +1,435 @@ //===- Symbols.h ------------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #ifndef LLD_COFF_SYMBOLS_H #define LLD_COFF_SYMBOLS_H #include "Chunks.h" #include "Config.h" #include "lld/Common/LLVM.h" #include "lld/Common/Memory.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include #include #include namespace lld { namespace coff { using llvm::object::Archive; using llvm::object::COFFSymbolRef; using llvm::object::coff_import_header; using llvm::object::coff_symbol_generic; class ArchiveFile; class InputFile; class ObjFile; class SymbolTable; // The base class for real symbol classes. class Symbol { public: enum Kind { // The order of these is significant. We start with the regular defined // symbols as those are the most prevalent and the zero tag is the cheapest // to set. Among the defined kinds, the lower the kind is preferred over // the higher kind when testing whether one symbol should take precedence // over another. DefinedRegularKind = 0, DefinedCommonKind, DefinedLocalImportKind, DefinedImportThunkKind, DefinedImportDataKind, DefinedAbsoluteKind, DefinedSyntheticKind, UndefinedKind, LazyKind, LastDefinedCOFFKind = DefinedCommonKind, LastDefinedKind = DefinedSyntheticKind, }; - Kind kind() const { return static_cast(SymbolKind); } + Kind kind() const { return static_cast(symbolKind); } - // Returns true if this is an external symbol. - bool isExternal() { return IsExternal; } - // Returns the symbol name. StringRef getName(); - void replaceKeepingName(Symbol *Other, size_t Size); + void replaceKeepingName(Symbol *other, size_t size); // Returns the file from which this symbol was created. InputFile *getFile(); // Indicates that this symbol will be included in the final image. Only valid // after calling markLive. bool isLive() const; protected: friend SymbolTable; - explicit Symbol(Kind K, StringRef N = "") - : SymbolKind(K), IsExternal(true), IsCOMDAT(false), - WrittenToSymtab(false), PendingArchiveLoad(false), IsGCRoot(false), - IsRuntimePseudoReloc(false), Name(N) {} + explicit Symbol(Kind k, StringRef n = "") + : symbolKind(k), isExternal(true), isCOMDAT(false), + writtenToSymtab(false), pendingArchiveLoad(false), isGCRoot(false), + isRuntimePseudoReloc(false), nameSize(n.size()), + nameData(n.empty() ? nullptr : n.data()) {} - const unsigned SymbolKind : 8; - unsigned IsExternal : 1; + const unsigned symbolKind : 8; + unsigned isExternal : 1; +public: // This bit is used by the \c DefinedRegular subclass. - unsigned IsCOMDAT : 1; + unsigned isCOMDAT : 1; -public: // This bit is used by Writer::createSymbolAndStringTable() to prevent // symbols from being written to the symbol table more than once. - unsigned WrittenToSymtab : 1; + unsigned writtenToSymtab : 1; // True if this symbol was referenced by a regular (non-bitcode) object. - unsigned IsUsedInRegularObj : 1; + 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; + unsigned pendingArchiveLoad : 1; /// True if we've already added this symbol to the list of GC roots. - unsigned IsGCRoot : 1; + unsigned isGCRoot : 1; - unsigned IsRuntimePseudoReloc : 1; + unsigned isRuntimePseudoReloc : 1; protected: - StringRef Name; + // Symbol name length. Assume symbol lengths fit in a 32-bit integer. + uint32_t nameSize; + + const char *nameData; }; // The base class for any defined symbols, including absolute symbols, // etc. class Defined : public Symbol { public: - Defined(Kind K, StringRef N) : Symbol(K, N) {} + Defined(Kind k, StringRef n) : Symbol(k, n) {} - static bool classof(const Symbol *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. uint64_t getRVA(); // Returns the chunk containing this symbol. Absolute symbols and __ImageBase // do not have chunks, so this may return null. Chunk *getChunk(); }; // Symbols defined via a COFF object file or bitcode file. For COFF files, this // stores a coff_symbol_generic*, and names of internal symbols are lazily // loaded through that. For bitcode files, Sym is nullptr and the name is stored -// as a StringRef. +// as a decomposed StringRef. class DefinedCOFF : public Defined { friend Symbol; public: - DefinedCOFF(Kind K, InputFile *F, StringRef N, const coff_symbol_generic *S) - : Defined(K, N), File(F), Sym(S) {} + DefinedCOFF(Kind k, InputFile *f, StringRef n, const coff_symbol_generic *s) + : Defined(k, n), file(f), sym(s) {} - static bool classof(const Symbol *S) { - return S->kind() <= LastDefinedCOFFKind; + static bool classof(const Symbol *s) { + return s->kind() <= LastDefinedCOFFKind; } - InputFile *getFile() { return File; } + InputFile *getFile() { return file; } COFFSymbolRef getCOFFSymbol(); - InputFile *File; + InputFile *file; protected: - const coff_symbol_generic *Sym; + const coff_symbol_generic *sym; }; // Regular defined symbols read from object file symbol tables. class DefinedRegular : public DefinedCOFF { public: - DefinedRegular(InputFile *F, StringRef N, bool IsCOMDAT, - bool IsExternal = false, - const coff_symbol_generic *S = nullptr, - SectionChunk *C = nullptr) - : DefinedCOFF(DefinedRegularKind, F, N, S), Data(C ? &C->Repl : nullptr) { - this->IsExternal = IsExternal; - this->IsCOMDAT = IsCOMDAT; + DefinedRegular(InputFile *f, StringRef n, bool isCOMDAT, + bool isExternal = false, + const coff_symbol_generic *s = nullptr, + SectionChunk *c = nullptr) + : DefinedCOFF(DefinedRegularKind, f, n, s), data(c ? &c->repl : nullptr) { + this->isExternal = isExternal; + this->isCOMDAT = isCOMDAT; } - static bool classof(const Symbol *S) { - return S->kind() == DefinedRegularKind; + static bool classof(const Symbol *s) { + return s->kind() == DefinedRegularKind; } - 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; } + uint64_t getRVA() const { return (*data)->getRVA() + sym->Value; } + SectionChunk *getChunk() const { return *data; } + uint32_t getValue() const { return sym->Value; } - SectionChunk **Data; + SectionChunk **data; }; class DefinedCommon : public DefinedCOFF { public: - DefinedCommon(InputFile *F, StringRef N, uint64_t Size, - const coff_symbol_generic *S = nullptr, - CommonChunk *C = nullptr) - : DefinedCOFF(DefinedCommonKind, F, N, S), Data(C), Size(Size) { - this->IsExternal = true; + DefinedCommon(InputFile *f, StringRef n, uint64_t size, + const coff_symbol_generic *s = nullptr, + CommonChunk *c = nullptr) + : DefinedCOFF(DefinedCommonKind, f, n, s), data(c), size(size) { + this->isExternal = true; } - static bool classof(const Symbol *S) { - return S->kind() == DefinedCommonKind; + static bool classof(const Symbol *s) { + return s->kind() == DefinedCommonKind; } - uint64_t getRVA() { return Data->getRVA(); } - CommonChunk *getChunk() { return Data; } + uint64_t getRVA() { return data->getRVA(); } + CommonChunk *getChunk() { return data; } private: friend SymbolTable; - uint64_t getSize() const { return Size; } - CommonChunk *Data; - uint64_t Size; + uint64_t getSize() const { return size; } + CommonChunk *data; + uint64_t size; }; // Absolute symbols. class DefinedAbsolute : public Defined { public: - DefinedAbsolute(StringRef N, COFFSymbolRef S) - : Defined(DefinedAbsoluteKind, N), VA(S.getValue()) { - IsExternal = S.isExternal(); + DefinedAbsolute(StringRef n, COFFSymbolRef s) + : Defined(DefinedAbsoluteKind, n), va(s.getValue()) { + isExternal = s.isExternal(); } - DefinedAbsolute(StringRef N, uint64_t V) - : Defined(DefinedAbsoluteKind, N), VA(V) {} + DefinedAbsolute(StringRef n, uint64_t v) + : Defined(DefinedAbsoluteKind, n), va(v) {} - static bool classof(const Symbol *S) { - return S->kind() == DefinedAbsoluteKind; + static bool classof(const Symbol *s) { + return s->kind() == DefinedAbsoluteKind; } - uint64_t getRVA() { return VA - Config->ImageBase; } - void setVA(uint64_t V) { VA = V; } + uint64_t getRVA() { return va - config->imageBase; } + void setVA(uint64_t v) { va = v; } // Section index relocations against absolute symbols resolve to // this 16 bit number, and it is the largest valid section index // plus one. This variable keeps it. - static uint16_t NumOutputSections; + static uint16_t numOutputSections; private: - uint64_t VA; + uint64_t va; }; // This symbol is used for linker-synthesized symbols like __ImageBase and // __safe_se_handler_table. class DefinedSynthetic : public Defined { public: - explicit DefinedSynthetic(StringRef Name, Chunk *C) - : Defined(DefinedSyntheticKind, Name), C(C) {} + explicit DefinedSynthetic(StringRef name, Chunk *c) + : Defined(DefinedSyntheticKind, name), c(c) {} - static bool classof(const Symbol *S) { - return S->kind() == DefinedSyntheticKind; + static bool classof(const Symbol *s) { + return s->kind() == DefinedSyntheticKind; } // A null chunk indicates that this is __ImageBase. Otherwise, this is some // other synthesized chunk, like SEHTableChunk. - uint32_t getRVA() { return C ? C->getRVA() : 0; } - Chunk *getChunk() { return C; } + uint32_t getRVA() { return c ? c->getRVA() : 0; } + Chunk *getChunk() { return c; } private: - Chunk *C; + Chunk *c; }; // 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 Symbol { public: - Lazy(ArchiveFile *F, const Archive::Symbol S) - : Symbol(LazyKind, S.getName()), File(F), Sym(S) {} + Lazy(ArchiveFile *f, const Archive::Symbol s) + : Symbol(LazyKind, s.getName()), file(f), sym(s) {} - static bool classof(const Symbol *S) { return S->kind() == LazyKind; } + static bool classof(const Symbol *s) { return s->kind() == LazyKind; } - ArchiveFile *File; + ArchiveFile *file; private: friend SymbolTable; private: - const Archive::Symbol Sym; + const Archive::Symbol sym; }; // Undefined symbols. class Undefined : public Symbol { public: - explicit Undefined(StringRef N) : Symbol(UndefinedKind, N) {} + explicit Undefined(StringRef n) : Symbol(UndefinedKind, n) {} - static bool classof(const Symbol *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. - Symbol *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 // successful, otherwise returns null. Defined *getWeakAlias(); }; // Windows-specific classes. // This class represents a symbol imported from a DLL. This has two // names for internal use and external use. The former is used for // name resolution, and the latter is used for the import descriptor // table in an output. The former has "__imp_" prefix. class DefinedImportData : public Defined { public: - DefinedImportData(StringRef N, ImportFile *F) - : Defined(DefinedImportDataKind, N), File(F) { + DefinedImportData(StringRef n, ImportFile *f) + : Defined(DefinedImportDataKind, n), file(f) { } - static bool classof(const Symbol *S) { - return S->kind() == DefinedImportDataKind; + static bool classof(const Symbol *s) { + return s->kind() == DefinedImportDataKind; } - uint64_t getRVA() { return File->Location->getRVA(); } - Chunk *getChunk() { return File->Location; } - void setLocation(Chunk *AddressTable) { File->Location = AddressTable; } + uint64_t getRVA() { return file->location->getRVA(); } + Chunk *getChunk() { return file->location; } + void setLocation(Chunk *addressTable) { file->location = addressTable; } - StringRef getDLLName() { return File->DLLName; } - StringRef getExternalName() { return File->ExternalName; } - uint16_t getOrdinal() { return File->Hdr->OrdinalHint; } + StringRef getDLLName() { return file->dllName; } + StringRef getExternalName() { return file->externalName; } + uint16_t getOrdinal() { return file->hdr->OrdinalHint; } - ImportFile *File; + ImportFile *file; }; // This class represents a symbol for a jump table entry which jumps // to a function in a DLL. Linker are supposed to create such symbols // without "__imp_" prefix for all function symbols exported from // DLLs, so that you can call DLL functions as regular functions with // a regular name. A function pointer is given as a DefinedImportData. class DefinedImportThunk : public Defined { public: - DefinedImportThunk(StringRef Name, DefinedImportData *S, uint16_t Machine); + DefinedImportThunk(StringRef name, DefinedImportData *s, uint16_t machine); - static bool classof(const Symbol *S) { - return S->kind() == DefinedImportThunkKind; + static bool classof(const Symbol *s) { + return s->kind() == DefinedImportThunkKind; } - uint64_t getRVA() { return Data->getRVA(); } - Chunk *getChunk() { return Data; } + uint64_t getRVA() { return data->getRVA(); } + Chunk *getChunk() { return data; } - DefinedImportData *WrappedSym; + DefinedImportData *wrappedSym; private: - Chunk *Data; + Chunk *data; }; // If you have a symbol "foo" in your object file, a symbol name // "__imp_foo" becomes automatically available as a pointer to "foo". // This class is for such automatically-created symbols. // Yes, this is an odd feature. We didn't intend to implement that. // This is here just for compatibility with MSVC. class DefinedLocalImport : public Defined { public: - DefinedLocalImport(StringRef N, Defined *S) - : Defined(DefinedLocalImportKind, N), Data(make(S)) {} + DefinedLocalImport(StringRef n, Defined *s) + : Defined(DefinedLocalImportKind, n), data(make(s)) {} - static bool classof(const Symbol *S) { - return S->kind() == DefinedLocalImportKind; + static bool classof(const Symbol *s) { + return s->kind() == DefinedLocalImportKind; } - uint64_t getRVA() { return Data->getRVA(); } - Chunk *getChunk() { return Data; } + uint64_t getRVA() { return data->getRVA(); } + Chunk *getChunk() { return data; } private: - LocalImportChunk *Data; + LocalImportChunk *data; }; inline uint64_t Defined::getRVA() { switch (kind()) { case DefinedAbsoluteKind: return cast(this)->getRVA(); case DefinedSyntheticKind: return cast(this)->getRVA(); case DefinedImportDataKind: return cast(this)->getRVA(); case DefinedImportThunkKind: return cast(this)->getRVA(); case DefinedLocalImportKind: return cast(this)->getRVA(); case DefinedCommonKind: return cast(this)->getRVA(); case DefinedRegularKind: return cast(this)->getRVA(); case LazyKind: case UndefinedKind: llvm_unreachable("Cannot get the address for an undefined symbol."); } llvm_unreachable("unknown symbol kind"); } inline Chunk *Defined::getChunk() { switch (kind()) { case DefinedRegularKind: return cast(this)->getChunk(); case DefinedAbsoluteKind: return nullptr; case DefinedSyntheticKind: return cast(this)->getChunk(); case DefinedImportDataKind: return cast(this)->getChunk(); case DefinedImportThunkKind: return cast(this)->getChunk(); case DefinedLocalImportKind: return cast(this)->getChunk(); case DefinedCommonKind: return cast(this)->getChunk(); case LazyKind: case UndefinedKind: llvm_unreachable("Cannot get the chunk of an undefined symbol."); } llvm_unreachable("unknown symbol kind"); } // 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)]; + 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 -void replaceSymbol(Symbol *S, ArgT &&... Arg) { +void replaceSymbol(Symbol *s, ArgT &&... arg) { static_assert(std::is_trivially_destructible(), "Symbol types must be trivially destructible"); static_assert(sizeof(T) <= sizeof(SymbolUnion), "Symbol too small"); static_assert(alignof(T) <= alignof(SymbolUnion), "SymbolUnion not aligned enough"); assert(static_cast(static_cast(nullptr)) == nullptr && "Not a Symbol"); - new (S) T(std::forward(Arg)...); + new (s) T(std::forward(arg)...); } } // namespace coff -std::string toString(coff::Symbol &B); +std::string toString(coff::Symbol &b); } // namespace lld #endif Index: vendor/lld/dist/COFF/TypeMerger.h =================================================================== --- vendor/lld/dist/COFF/TypeMerger.h (nonexistent) +++ vendor/lld/dist/COFF/TypeMerger.h (revision 351288) @@ -0,0 +1,65 @@ +//===- TypeMerger.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COFF_TYPEMERGER_H +#define LLD_COFF_TYPEMERGER_H + +#include "Config.h" +#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" +#include "llvm/Support/Allocator.h" + +namespace lld { +namespace coff { + +class TypeMerger { +public: + TypeMerger(llvm::BumpPtrAllocator &alloc) + : typeTable(alloc), iDTable(alloc), globalTypeTable(alloc), + globalIDTable(alloc) {} + + /// Get the type table or the global type table if /DEBUG:GHASH is enabled. + inline llvm::codeview::TypeCollection &getTypeTable() { + if (config->debugGHashes) + return globalTypeTable; + return typeTable; + } + + /// Get the ID table or the global ID table if /DEBUG:GHASH is enabled. + inline llvm::codeview::TypeCollection &getIDTable() { + if (config->debugGHashes) + return globalIDTable; + return iDTable; + } + + /// Type records that will go into the PDB TPI stream. + llvm::codeview::MergingTypeTableBuilder typeTable; + + /// Item records that will go into the PDB IPI stream. + llvm::codeview::MergingTypeTableBuilder iDTable; + + /// Type records that will go into the PDB TPI stream (for /DEBUG:GHASH) + llvm::codeview::GlobalTypeTableBuilder globalTypeTable; + + /// Item records that will go into the PDB IPI stream (for /DEBUG:GHASH) + llvm::codeview::GlobalTypeTableBuilder globalIDTable; +}; + +/// Map from type index and item index in a type server PDB to the +/// corresponding index in the destination PDB. +struct CVIndexMap { + llvm::SmallVector tpiMap; + llvm::SmallVector ipiMap; + bool isTypeServerMap = false; + bool isPrecompiledTypeMap = false; +}; + +} // namespace coff +} // namespace lld + +#endif \ No newline at end of file Index: vendor/lld/dist/COFF/Writer.cpp =================================================================== --- vendor/lld/dist/COFF/Writer.cpp (revision 351287) +++ vendor/lld/dist/COFF/Writer.cpp (revision 351288) @@ -1,1719 +1,1901 @@ //===- Writer.cpp ---------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "Writer.h" #include "Config.h" #include "DLL.h" #include "InputFiles.h" #include "MapFile.h" #include "PDB.h" #include "SymbolTable.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "lld/Common/Threads.h" #include "lld/Common/Timer.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/Path.h" #include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Support/xxhash.h" #include #include #include #include #include using namespace llvm; using namespace llvm::COFF; using namespace llvm::object; using namespace llvm::support; using namespace llvm::support::endian; using namespace lld; using namespace lld::coff; /* To re-generate DOSProgram: $ cat > /tmp/DOSProgram.asm org 0 ; Copy cs to ds. push cs pop ds ; Point ds:dx at the $-terminated string. mov dx, str ; Int 21/AH=09h: Write string to standard output. mov ah, 0x9 int 0x21 ; Int 21/AH=4Ch: Exit with return code (in AL). mov ax, 0x4C01 int 0x21 str: db 'This program cannot be run in DOS mode.$' align 8, db 0 $ nasm -fbin /tmp/DOSProgram.asm -o /tmp/DOSProgram.bin $ xxd -i /tmp/DOSProgram.bin */ -static unsigned char DOSProgram[] = { +static unsigned char dosProgram[] = { 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x24, 0x00, 0x00 }; -static_assert(sizeof(DOSProgram) % 8 == 0, +static_assert(sizeof(dosProgram) % 8 == 0, "DOSProgram size must be multiple of 8"); -static const int SectorSize = 512; -static const int DOSStubSize = sizeof(dos_header) + sizeof(DOSProgram); -static_assert(DOSStubSize % 8 == 0, "DOSStub size must be multiple of 8"); +static const int dosStubSize = sizeof(dos_header) + sizeof(dosProgram); +static_assert(dosStubSize % 8 == 0, "DOSStub size must be multiple of 8"); -static const int NumberOfDataDirectory = 16; +static const int numberOfDataDirectory = 16; +// Global vector of all output sections. After output sections are finalized, +// this can be indexed by Chunk::getOutputSection. +static std::vector outputSections; + +OutputSection *Chunk::getOutputSection() const { + return osidx == 0 ? nullptr : outputSections[osidx - 1]; +} + namespace { -class DebugDirectoryChunk : public Chunk { +class DebugDirectoryChunk : public NonSectionChunk { public: - DebugDirectoryChunk(const std::vector &R, bool WriteRepro) - : Records(R), WriteRepro(WriteRepro) {} + DebugDirectoryChunk(const std::vector &r, bool writeRepro) + : records(r), writeRepro(writeRepro) {} size_t getSize() const override { - return (Records.size() + int(WriteRepro)) * sizeof(debug_directory); + return (records.size() + int(writeRepro)) * sizeof(debug_directory); } - void writeTo(uint8_t *B) const override { - auto *D = reinterpret_cast(B + OutputSectionOff); + void writeTo(uint8_t *b) const override { + auto *d = reinterpret_cast(b); - for (const Chunk *Record : Records) { - OutputSection *OS = Record->getOutputSection(); - uint64_t Offs = OS->getFileOff() + (Record->getRVA() - OS->getRVA()); - fillEntry(D, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, Record->getSize(), - Record->getRVA(), Offs); - ++D; + for (const Chunk *record : records) { + OutputSection *os = record->getOutputSection(); + uint64_t offs = os->getFileOff() + (record->getRVA() - os->getRVA()); + fillEntry(d, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, record->getSize(), + record->getRVA(), offs); + ++d; } - if (WriteRepro) { + if (writeRepro) { // FIXME: The COFF spec allows either a 0-sized entry to just say // "the timestamp field is really a hash", or a 4-byte size field // followed by that many bytes containing a longer hash (with the // lowest 4 bytes usually being the timestamp in little-endian order). // Consider storing the full 8 bytes computed by xxHash64 here. - fillEntry(D, COFF::IMAGE_DEBUG_TYPE_REPRO, 0, 0, 0); + fillEntry(d, COFF::IMAGE_DEBUG_TYPE_REPRO, 0, 0, 0); } } - void setTimeDateStamp(uint32_t TimeDateStamp) { - for (support::ulittle32_t *TDS : TimeDateStamps) - *TDS = TimeDateStamp; + void setTimeDateStamp(uint32_t timeDateStamp) { + for (support::ulittle32_t *tds : timeDateStamps) + *tds = timeDateStamp; } private: - void fillEntry(debug_directory *D, COFF::DebugType DebugType, size_t Size, - uint64_t RVA, uint64_t Offs) const { - D->Characteristics = 0; - D->TimeDateStamp = 0; - D->MajorVersion = 0; - D->MinorVersion = 0; - D->Type = DebugType; - D->SizeOfData = Size; - D->AddressOfRawData = RVA; - D->PointerToRawData = Offs; + void fillEntry(debug_directory *d, COFF::DebugType debugType, size_t size, + uint64_t rva, uint64_t offs) const { + d->Characteristics = 0; + d->TimeDateStamp = 0; + d->MajorVersion = 0; + d->MinorVersion = 0; + d->Type = debugType; + d->SizeOfData = size; + d->AddressOfRawData = rva; + d->PointerToRawData = offs; - TimeDateStamps.push_back(&D->TimeDateStamp); + timeDateStamps.push_back(&d->TimeDateStamp); } - mutable std::vector TimeDateStamps; - const std::vector &Records; - bool WriteRepro; + mutable std::vector timeDateStamps; + const std::vector &records; + bool writeRepro; }; -class CVDebugRecordChunk : public Chunk { +class CVDebugRecordChunk : public NonSectionChunk { public: size_t getSize() const override { - return sizeof(codeview::DebugInfo) + Config->PDBAltPath.size() + 1; + return sizeof(codeview::DebugInfo) + config->pdbAltPath.size() + 1; } - void writeTo(uint8_t *B) const override { + void writeTo(uint8_t *b) const override { // Save off the DebugInfo entry to backfill the file signature (build id) // in Writer::writeBuildId - BuildId = reinterpret_cast(B + OutputSectionOff); + buildId = reinterpret_cast(b); // variable sized field (PDB Path) - char *P = reinterpret_cast(B + OutputSectionOff + sizeof(*BuildId)); - if (!Config->PDBAltPath.empty()) - memcpy(P, Config->PDBAltPath.data(), Config->PDBAltPath.size()); - P[Config->PDBAltPath.size()] = '\0'; + char *p = reinterpret_cast(b + sizeof(*buildId)); + if (!config->pdbAltPath.empty()) + memcpy(p, config->pdbAltPath.data(), config->pdbAltPath.size()); + p[config->pdbAltPath.size()] = '\0'; } - mutable codeview::DebugInfo *BuildId = nullptr; + mutable codeview::DebugInfo *buildId = nullptr; }; +// PartialSection represents a group of chunks that contribute to an +// OutputSection. Collating a collection of PartialSections of same name and +// characteristics constitutes the OutputSection. +class PartialSectionKey { +public: + StringRef name; + unsigned characteristics; + + bool operator<(const PartialSectionKey &other) const { + int c = name.compare(other.name); + if (c == 1) + return false; + if (c == 0) + return characteristics < other.characteristics; + return true; + } +}; + // The writer writes a SymbolTable result to a file. class Writer { public: - Writer() : Buffer(errorHandler().OutputBuffer) {} + Writer() : buffer(errorHandler().outputBuffer) {} void run(); private: void createSections(); void createMiscChunks(); void createImportTables(); void appendImportThunks(); - void locateImportTables( - std::map, std::vector> &Map); + void locateImportTables(); void createExportTable(); void mergeSections(); - void readRelocTargets(); void removeUnusedSections(); void assignAddresses(); void finalizeAddresses(); void removeEmptySections(); + void assignOutputSectionIndices(); void createSymbolAndStringTable(); - void openFile(StringRef OutputPath); + void openFile(StringRef outputPath); template void writeHeader(); void createSEHTable(); void createRuntimePseudoRelocs(); void insertCtorDtorSymbols(); void createGuardCFTables(); - void markSymbolsForRVATable(ObjFile *File, - ArrayRef SymIdxChunks, - SymbolRVASet &TableSymbols); - void maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym, - StringRef CountSym); + void markSymbolsForRVATable(ObjFile *file, + ArrayRef symIdxChunks, + SymbolRVASet &tableSymbols); + void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, + StringRef countSym); void setSectionPermissions(); void writeSections(); void writeBuildId(); void sortExceptionTable(); - void sortCRTSectionChunks(std::vector &Chunks); + void sortCRTSectionChunks(std::vector &chunks); + void addSyntheticIdata(); + void fixPartialSectionChars(StringRef name, uint32_t chars); + bool fixGnuImportChunks(); + PartialSection *createPartialSection(StringRef name, uint32_t outChars); + PartialSection *findPartialSection(StringRef name, uint32_t outChars); - llvm::Optional createSymbol(Defined *D); - size_t addEntryToStringTable(StringRef Str); + llvm::Optional createSymbol(Defined *d); + size_t addEntryToStringTable(StringRef str); - OutputSection *findSection(StringRef Name); + OutputSection *findSection(StringRef name); void addBaserels(); - void addBaserelBlocks(std::vector &V); + void addBaserelBlocks(std::vector &v); uint32_t getSizeOfInitializedData(); - std::map> binImports(); - std::unique_ptr &Buffer; - std::vector OutputSections; - std::vector Strtab; - std::vector OutputSymtab; - IdataContents Idata; - Chunk *ImportTableStart = nullptr; - uint64_t ImportTableSize = 0; - Chunk *IATStart = nullptr; - uint64_t IATSize = 0; - DelayLoadContents DelayIdata; - EdataContents Edata; - bool SetNoSEHCharacteristic = false; + std::unique_ptr &buffer; + std::map partialSections; + std::vector strtab; + std::vector outputSymtab; + IdataContents idata; + Chunk *importTableStart = nullptr; + uint64_t importTableSize = 0; + Chunk *iatStart = nullptr; + uint64_t iatSize = 0; + DelayLoadContents delayIdata; + EdataContents edata; + bool setNoSEHCharacteristic = false; - DebugDirectoryChunk *DebugDirectory = nullptr; - std::vector DebugRecords; - CVDebugRecordChunk *BuildId = nullptr; - ArrayRef SectionTable; + DebugDirectoryChunk *debugDirectory = nullptr; + std::vector debugRecords; + CVDebugRecordChunk *buildId = nullptr; + ArrayRef sectionTable; - uint64_t FileSize; - uint32_t PointerToSymbolTable = 0; - uint64_t SizeOfImage; - uint64_t SizeOfHeaders; + uint64_t fileSize; + uint32_t pointerToSymbolTable = 0; + uint64_t sizeOfImage; + uint64_t sizeOfHeaders; - OutputSection *TextSec; - OutputSection *RdataSec; - OutputSection *BuildidSec; - OutputSection *DataSec; - OutputSection *PdataSec; - OutputSection *IdataSec; - OutputSection *EdataSec; - OutputSection *DidatSec; - OutputSection *RsrcSec; - OutputSection *RelocSec; - OutputSection *CtorsSec; - OutputSection *DtorsSec; + OutputSection *textSec; + OutputSection *rdataSec; + OutputSection *buildidSec; + OutputSection *dataSec; + OutputSection *pdataSec; + OutputSection *idataSec; + OutputSection *edataSec; + OutputSection *didatSec; + OutputSection *rsrcSec; + OutputSection *relocSec; + OutputSection *ctorsSec; + OutputSection *dtorsSec; // The first and last .pdata sections in the output file. // // We need to keep track of the location of .pdata in whichever section it // gets merged into so that we can sort its contents and emit a correct data // directory entry for the exception table. This is also the case for some // other sections (such as .edata) but because the contents of those sections // are entirely linker-generated we can keep track of their locations using // the chunks that the linker creates. All .pdata chunks come from input // files, so we need to keep track of them separately. - Chunk *FirstPdata = nullptr; - Chunk *LastPdata; + Chunk *firstPdata = nullptr; + Chunk *lastPdata; }; } // anonymous namespace namespace lld { namespace coff { -static Timer CodeLayoutTimer("Code Layout", Timer::root()); -static Timer DiskCommitTimer("Commit Output File", Timer::root()); +static Timer codeLayoutTimer("Code Layout", Timer::root()); +static Timer diskCommitTimer("Commit Output File", Timer::root()); void writeResult() { Writer().run(); } -void OutputSection::addChunk(Chunk *C) { - Chunks.push_back(C); - C->setOutputSection(this); +void OutputSection::addChunk(Chunk *c) { + chunks.push_back(c); } -void OutputSection::insertChunkAtStart(Chunk *C) { - Chunks.insert(Chunks.begin(), C); - C->setOutputSection(this); +void OutputSection::insertChunkAtStart(Chunk *c) { + chunks.insert(chunks.begin(), c); } -void OutputSection::setPermissions(uint32_t C) { - Header.Characteristics &= ~PermMask; - Header.Characteristics |= C; +void OutputSection::setPermissions(uint32_t c) { + header.Characteristics &= ~permMask; + header.Characteristics |= c; } -void OutputSection::merge(OutputSection *Other) { - for (Chunk *C : Other->Chunks) - C->setOutputSection(this); - Chunks.insert(Chunks.end(), Other->Chunks.begin(), Other->Chunks.end()); - Other->Chunks.clear(); +void OutputSection::merge(OutputSection *other) { + chunks.insert(chunks.end(), other->chunks.begin(), other->chunks.end()); + other->chunks.clear(); + contribSections.insert(contribSections.end(), other->contribSections.begin(), + other->contribSections.end()); + other->contribSections.clear(); } // Write the section header to a given buffer. -void OutputSection::writeHeaderTo(uint8_t *Buf) { - auto *Hdr = reinterpret_cast(Buf); - *Hdr = Header; - if (StringTableOff) { +void OutputSection::writeHeaderTo(uint8_t *buf) { + auto *hdr = reinterpret_cast(buf); + *hdr = header; + if (stringTableOff) { // If name is too long, write offset into the string table as a name. - sprintf(Hdr->Name, "/%d", StringTableOff); + sprintf(hdr->Name, "/%d", stringTableOff); } else { - 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)); + 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)); } } +void OutputSection::addContributingPartialSection(PartialSection *sec) { + contribSections.push_back(sec); +} + } // namespace coff } // namespace lld // Check whether the target address S is in range from a relocation -// of type RelType at address P. -static bool isInRange(uint16_t RelType, uint64_t S, uint64_t P, int Margin) { - assert(Config->Machine == ARMNT); - int64_t Diff = AbsoluteDifference(S, P + 4) + Margin; - switch (RelType) { - case IMAGE_REL_ARM_BRANCH20T: - return isInt<21>(Diff); - case IMAGE_REL_ARM_BRANCH24T: - case IMAGE_REL_ARM_BLX23T: - return isInt<25>(Diff); - default: - return true; +// of type relType at address P. +static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) { + if (config->machine == ARMNT) { + int64_t diff = AbsoluteDifference(s, p + 4) + margin; + switch (relType) { + case IMAGE_REL_ARM_BRANCH20T: + return isInt<21>(diff); + case IMAGE_REL_ARM_BRANCH24T: + case IMAGE_REL_ARM_BLX23T: + return isInt<25>(diff); + default: + return true; + } + } else if (config->machine == ARM64) { + int64_t diff = AbsoluteDifference(s, p) + margin; + switch (relType) { + case IMAGE_REL_ARM64_BRANCH26: + return isInt<28>(diff); + case IMAGE_REL_ARM64_BRANCH19: + return isInt<21>(diff); + case IMAGE_REL_ARM64_BRANCH14: + return isInt<16>(diff); + default: + return true; + } + } else { + llvm_unreachable("Unexpected architecture"); } } // Return the last thunk for the given target if it is in range, // or create a new one. static std::pair -getThunk(DenseMap &LastThunks, Defined *Target, uint64_t P, - uint16_t Type, int Margin) { - Defined *&LastThunk = LastThunks[Target->getRVA()]; - if (LastThunk && isInRange(Type, LastThunk->getRVA(), P, Margin)) - return {LastThunk, false}; - RangeExtensionThunk *C = make(Target); - Defined *D = make("", C); - LastThunk = D; - return {D, true}; +getThunk(DenseMap &lastThunks, Defined *target, uint64_t p, + uint16_t type, int margin) { + Defined *&lastThunk = lastThunks[target->getRVA()]; + if (lastThunk && isInRange(type, lastThunk->getRVA(), p, margin)) + return {lastThunk, false}; + Chunk *c; + switch (config->machine) { + case ARMNT: + c = make(target); + break; + case ARM64: + c = make(target); + break; + default: + llvm_unreachable("Unexpected architecture"); + } + Defined *d = make("", c); + lastThunk = d; + return {d, true}; } // This checks all relocations, and for any relocation which isn't in range // it adds a thunk after the section chunk that contains the relocation. // If the latest thunk for the specific target is in range, that is used // instead of creating a new thunk. All range checks are done with the // specified margin, to make sure that relocations that originally are in // range, but only barely, also get thunks - in case other added thunks makes // the target go out of range. // // After adding thunks, we verify that all relocations are in range (with // no extra margin requirements). If this failed, we restart (throwing away // the previously created thunks) and retry with a wider margin. -static bool createThunks(std::vector &Chunks, int Margin) { - bool AddressesChanged = false; - DenseMap LastThunks; - size_t ThunksSize = 0; +static bool createThunks(OutputSection *os, int margin) { + bool addressesChanged = false; + DenseMap lastThunks; + DenseMap, uint32_t> thunkSymtabIndices; + size_t thunksSize = 0; // Recheck Chunks.size() each iteration, since we can insert more // elements into it. - for (size_t I = 0; I != Chunks.size(); ++I) { - SectionChunk *SC = dyn_cast_or_null(Chunks[I]); - if (!SC) + for (size_t i = 0; i != os->chunks.size(); ++i) { + SectionChunk *sc = dyn_cast_or_null(os->chunks[i]); + if (!sc) continue; - size_t ThunkInsertionSpot = I + 1; + size_t thunkInsertionSpot = i + 1; // Try to get a good enough estimate of where new thunks will be placed. // Offset this by the size of the new thunks added so far, to make the // estimate slightly better. - size_t ThunkInsertionRVA = SC->getRVA() + SC->getSize() + ThunksSize; - for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) { - const coff_relocation &Rel = SC->Relocs[J]; - Symbol *&RelocTarget = SC->RelocTargets[J]; + size_t thunkInsertionRVA = sc->getRVA() + sc->getSize() + thunksSize; + ObjFile *file = sc->file; + std::vector> relocReplacements; + ArrayRef originalRelocs = + file->getCOFFObj()->getRelocations(sc->header); + for (size_t j = 0, e = originalRelocs.size(); j < e; ++j) { + const coff_relocation &rel = originalRelocs[j]; + Symbol *relocTarget = file->getSymbol(rel.SymbolTableIndex); // The estimate of the source address P should be pretty accurate, // but we don't know whether the target Symbol address should be - // offset by ThunkSize or not (or by some of ThunksSize but not all of + // offset by thunksSize or not (or by some of thunksSize but not all of // it), giving us some uncertainty once we have added one thunk. - uint64_t P = SC->getRVA() + Rel.VirtualAddress + ThunksSize; + uint64_t p = sc->getRVA() + rel.VirtualAddress + thunksSize; - Defined *Sym = dyn_cast_or_null(RelocTarget); - if (!Sym) + Defined *sym = dyn_cast_or_null(relocTarget); + if (!sym) continue; - uint64_t S = Sym->getRVA(); + uint64_t s = sym->getRVA(); - if (isInRange(Rel.Type, S, P, Margin)) + if (isInRange(rel.Type, s, p, margin)) continue; // If the target isn't in range, hook it up to an existing or new // thunk. - Defined *Thunk; - bool WasNew; - std::tie(Thunk, WasNew) = getThunk(LastThunks, Sym, P, Rel.Type, Margin); - if (WasNew) { - Chunk *ThunkChunk = Thunk->getChunk(); - ThunkChunk->setRVA( - ThunkInsertionRVA); // Estimate of where it will be located. - Chunks.insert(Chunks.begin() + ThunkInsertionSpot, ThunkChunk); - ThunkInsertionSpot++; - ThunksSize += ThunkChunk->getSize(); - ThunkInsertionRVA += ThunkChunk->getSize(); - AddressesChanged = true; + Defined *thunk; + bool wasNew; + std::tie(thunk, wasNew) = getThunk(lastThunks, sym, p, rel.Type, margin); + if (wasNew) { + Chunk *thunkChunk = thunk->getChunk(); + thunkChunk->setRVA( + thunkInsertionRVA); // Estimate of where it will be located. + os->chunks.insert(os->chunks.begin() + thunkInsertionSpot, thunkChunk); + thunkInsertionSpot++; + thunksSize += thunkChunk->getSize(); + thunkInsertionRVA += thunkChunk->getSize(); + addressesChanged = true; } - RelocTarget = Thunk; + + // To redirect the relocation, add a symbol to the parent object file's + // symbol table, and replace the relocation symbol table index with the + // new index. + auto insertion = thunkSymtabIndices.insert({{file, thunk}, ~0U}); + uint32_t &thunkSymbolIndex = insertion.first->second; + if (insertion.second) + thunkSymbolIndex = file->addRangeThunkSymbol(thunk); + relocReplacements.push_back({j, thunkSymbolIndex}); } + + // Get a writable copy of this section's relocations so they can be + // modified. If the relocations point into the object file, allocate new + // memory. Otherwise, this must be previously allocated memory that can be + // modified in place. + ArrayRef curRelocs = sc->getRelocs(); + MutableArrayRef newRelocs; + if (originalRelocs.data() == curRelocs.data()) { + newRelocs = makeMutableArrayRef( + bAlloc.Allocate(originalRelocs.size()), + originalRelocs.size()); + } else { + newRelocs = makeMutableArrayRef( + const_cast(curRelocs.data()), curRelocs.size()); + } + + // Copy each relocation, but replace the symbol table indices which need + // thunks. + auto nextReplacement = relocReplacements.begin(); + auto endReplacement = relocReplacements.end(); + for (size_t i = 0, e = originalRelocs.size(); i != e; ++i) { + newRelocs[i] = originalRelocs[i]; + if (nextReplacement != endReplacement && nextReplacement->first == i) { + newRelocs[i].SymbolTableIndex = nextReplacement->second; + ++nextReplacement; + } + } + + sc->setRelocs(newRelocs); } - return AddressesChanged; + return addressesChanged; } // Verify that all relocations are in range, with no extra margin requirements. -static bool verifyRanges(const std::vector Chunks) { - for (Chunk *C : Chunks) { - SectionChunk *SC = dyn_cast_or_null(C); - if (!SC) +static bool verifyRanges(const std::vector chunks) { + for (Chunk *c : chunks) { + SectionChunk *sc = dyn_cast_or_null(c); + if (!sc) continue; - for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) { - const coff_relocation &Rel = SC->Relocs[J]; - Symbol *RelocTarget = SC->RelocTargets[J]; + ArrayRef relocs = sc->getRelocs(); + for (size_t j = 0, e = relocs.size(); j < e; ++j) { + const coff_relocation &rel = relocs[j]; + Symbol *relocTarget = sc->file->getSymbol(rel.SymbolTableIndex); - Defined *Sym = dyn_cast_or_null(RelocTarget); - if (!Sym) + Defined *sym = dyn_cast_or_null(relocTarget); + if (!sym) continue; - uint64_t P = SC->getRVA() + Rel.VirtualAddress; - uint64_t S = Sym->getRVA(); + uint64_t p = sc->getRVA() + rel.VirtualAddress; + uint64_t s = sym->getRVA(); - if (!isInRange(Rel.Type, S, P, 0)) + if (!isInRange(rel.Type, s, p, 0)) return false; } } return true; } // Assign addresses and add thunks if necessary. void Writer::finalizeAddresses() { assignAddresses(); - if (Config->Machine != ARMNT) + if (config->machine != ARMNT && config->machine != ARM64) return; - size_t OrigNumChunks = 0; - for (OutputSection *Sec : OutputSections) { - Sec->OrigChunks = Sec->Chunks; - OrigNumChunks += Sec->Chunks.size(); + size_t origNumChunks = 0; + for (OutputSection *sec : outputSections) { + sec->origChunks = sec->chunks; + origNumChunks += sec->chunks.size(); } - int Pass = 0; - int Margin = 1024 * 100; + int pass = 0; + int margin = 1024 * 100; while (true) { // First check whether we need thunks at all, or if the previous pass of // adding them turned out ok. - bool RangesOk = true; - size_t NumChunks = 0; - for (OutputSection *Sec : OutputSections) { - if (!verifyRanges(Sec->Chunks)) { - RangesOk = false; + bool rangesOk = true; + size_t numChunks = 0; + for (OutputSection *sec : outputSections) { + if (!verifyRanges(sec->chunks)) { + rangesOk = false; break; } - NumChunks += Sec->Chunks.size(); + numChunks += sec->chunks.size(); } - if (RangesOk) { - if (Pass > 0) - log("Added " + Twine(NumChunks - OrigNumChunks) + " thunks with " + - "margin " + Twine(Margin) + " in " + Twine(Pass) + " passes"); + if (rangesOk) { + if (pass > 0) + log("Added " + Twine(numChunks - origNumChunks) + " thunks with " + + "margin " + Twine(margin) + " in " + Twine(pass) + " passes"); return; } - if (Pass >= 10) - fatal("adding thunks hasn't converged after " + Twine(Pass) + " passes"); + if (pass >= 10) + fatal("adding thunks hasn't converged after " + Twine(pass) + " passes"); - if (Pass > 0) { + if (pass > 0) { // If the previous pass didn't work out, reset everything back to the // original conditions before retrying with a wider margin. This should // ideally never happen under real circumstances. - for (OutputSection *Sec : OutputSections) { - Sec->Chunks = Sec->OrigChunks; - for (Chunk *C : Sec->Chunks) - C->resetRelocTargets(); - } - Margin *= 2; + for (OutputSection *sec : outputSections) + sec->chunks = sec->origChunks; + margin *= 2; } // Try adding thunks everywhere where it is needed, with a margin // to avoid things going out of range due to the added thunks. - bool AddressesChanged = false; - for (OutputSection *Sec : OutputSections) - AddressesChanged |= createThunks(Sec->Chunks, Margin); + bool addressesChanged = false; + for (OutputSection *sec : outputSections) + addressesChanged |= createThunks(sec, margin); // If the verification above thought we needed thunks, we should have // added some. - assert(AddressesChanged); + assert(addressesChanged); // Recalculate the layout for the whole image (and verify the ranges at // the start of the next round). assignAddresses(); - Pass++; + pass++; } } // The main function of the writer. void Writer::run() { - ScopedTimer T1(CodeLayoutTimer); + ScopedTimer t1(codeLayoutTimer); createImportTables(); createSections(); createMiscChunks(); appendImportThunks(); createExportTable(); mergeSections(); - readRelocTargets(); removeUnusedSections(); finalizeAddresses(); removeEmptySections(); + assignOutputSectionIndices(); setSectionPermissions(); createSymbolAndStringTable(); - if (FileSize > UINT32_MAX) - fatal("image size (" + Twine(FileSize) + ") " + + if (fileSize > UINT32_MAX) + fatal("image size (" + Twine(fileSize) + ") " + "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")"); - openFile(Config->OutputFile); - if (Config->is64()) { + openFile(config->outputFile); + if (config->is64()) { writeHeader(); } else { writeHeader(); } writeSections(); sortExceptionTable(); - T1.stop(); + t1.stop(); - if (!Config->PDBPath.empty() && Config->Debug) { - assert(BuildId); - createPDB(Symtab, OutputSections, SectionTable, BuildId->BuildId); + if (!config->pdbPath.empty() && config->debug) { + assert(buildId); + createPDB(symtab, outputSections, sectionTable, buildId->buildId); } writeBuildId(); - writeMapFile(OutputSections); + writeMapFile(outputSections); - ScopedTimer T2(DiskCommitTimer); - if (auto E = Buffer->commit()) - fatal("failed to write the output file: " + toString(std::move(E))); + ScopedTimer t2(diskCommitTimer); + if (auto e = buffer->commit()) + fatal("failed to write the output file: " + toString(std::move(e))); } -static StringRef getOutputSectionName(StringRef Name) { - StringRef S = Name.split('$').first; +static StringRef getOutputSectionName(StringRef name) { + StringRef s = name.split('$').first; // Treat a later period as a separator for MinGW, for sections like // ".ctors.01234". - return S.substr(0, S.find('.', 1)); + return s.substr(0, s.find('.', 1)); } // For /order. -static void sortBySectionOrder(std::vector &Chunks) { - auto GetPriority = [](const Chunk *C) { - if (auto *Sec = dyn_cast(C)) - if (Sec->Sym) - return Config->Order.lookup(Sec->Sym->getName()); +static void sortBySectionOrder(std::vector &chunks) { + auto getPriority = [](const Chunk *c) { + if (auto *sec = dyn_cast(c)) + if (sec->sym) + return config->order.lookup(sec->sym->getName()); return 0; }; - std::stable_sort(Chunks.begin(), Chunks.end(), - [=](const Chunk *A, const Chunk *B) { - return GetPriority(A) < GetPriority(B); - }); + llvm::stable_sort(chunks, [=](const Chunk *a, const Chunk *b) { + return getPriority(a) < getPriority(b); + }); } +// Change the characteristics of existing PartialSections that belong to the +// section Name to Chars. +void Writer::fixPartialSectionChars(StringRef name, uint32_t chars) { + for (auto it : partialSections) { + PartialSection *pSec = it.second; + StringRef curName = pSec->name; + if (!curName.consume_front(name) || + (!curName.empty() && !curName.startswith("$"))) + continue; + if (pSec->characteristics == chars) + continue; + PartialSection *destSec = createPartialSection(pSec->name, chars); + destSec->chunks.insert(destSec->chunks.end(), pSec->chunks.begin(), + pSec->chunks.end()); + pSec->chunks.clear(); + } +} + // Sort concrete section chunks from GNU import libraries. // // GNU binutils doesn't use short import files, but instead produces import // libraries that consist of object files, with section chunks for the .idata$* // sections. These are linked just as regular static libraries. Each import // library consists of one header object, one object file for every imported // symbol, and one trailer object. In order for the .idata tables/lists to // be formed correctly, the section chunks within each .idata$* section need // to be grouped by library, and sorted alphabetically within each library // (which makes sure the header comes first and the trailer last). -static bool fixGnuImportChunks( - std::map, std::vector> &Map) { - uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; +bool Writer::fixGnuImportChunks() { + uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; // Make sure all .idata$* section chunks are mapped as RDATA in order to // be sorted into the same sections as our own synthesized .idata chunks. - for (auto &Pair : Map) { - StringRef SectionName = Pair.first.first; - uint32_t OutChars = Pair.first.second; - if (!SectionName.startswith(".idata")) - continue; - if (OutChars == RDATA) - continue; - std::vector &SrcVect = Pair.second; - std::vector &DestVect = Map[{SectionName, RDATA}]; - DestVect.insert(DestVect.end(), SrcVect.begin(), SrcVect.end()); - SrcVect.clear(); - } + fixPartialSectionChars(".idata", rdata); - bool HasIdata = false; + bool hasIdata = false; // Sort all .idata$* chunks, grouping chunks from the same library, // with alphabetical ordering of the object fils within a library. - for (auto &Pair : Map) { - StringRef SectionName = Pair.first.first; - if (!SectionName.startswith(".idata")) + for (auto it : partialSections) { + PartialSection *pSec = it.second; + if (!pSec->name.startswith(".idata")) continue; - std::vector &Chunks = Pair.second; - if (!Chunks.empty()) - HasIdata = true; - std::stable_sort(Chunks.begin(), Chunks.end(), [&](Chunk *S, Chunk *T) { - SectionChunk *SC1 = dyn_cast_or_null(S); - SectionChunk *SC2 = dyn_cast_or_null(T); - if (!SC1 || !SC2) { + if (!pSec->chunks.empty()) + hasIdata = true; + llvm::stable_sort(pSec->chunks, [&](Chunk *s, Chunk *t) { + SectionChunk *sc1 = dyn_cast_or_null(s); + SectionChunk *sc2 = dyn_cast_or_null(t); + if (!sc1 || !sc2) { // if SC1, order them ascending. If SC2 or both null, // S is not less than T. - return SC1 != nullptr; + return sc1 != nullptr; } // Make a string with "libraryname/objectfile" for sorting, achieving // both grouping by library and sorting of objects within a library, // at once. - std::string Key1 = - (SC1->File->ParentName + "/" + SC1->File->getName()).str(); - std::string Key2 = - (SC2->File->ParentName + "/" + SC2->File->getName()).str(); - return Key1 < Key2; + std::string key1 = + (sc1->file->parentName + "/" + sc1->file->getName()).str(); + std::string key2 = + (sc2->file->parentName + "/" + sc2->file->getName()).str(); + return key1 < key2; }); } - return HasIdata; + return hasIdata; } // Add generated idata chunks, for imported symbols and DLLs, and a // terminator in .idata$2. -static void addSyntheticIdata( - IdataContents &Idata, - std::map, std::vector> &Map) { - uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; - Idata.create(); +void Writer::addSyntheticIdata() { + uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; + idata.create(); // Add the .idata content in the right section groups, to allow // chunks from other linked in object files to be grouped together. // See Microsoft PE/COFF spec 5.4 for details. - auto Add = [&](StringRef N, std::vector &V) { - std::vector &DestVect = Map[{N, RDATA}]; - DestVect.insert(DestVect.end(), V.begin(), V.end()); + auto add = [&](StringRef n, std::vector &v) { + PartialSection *pSec = createPartialSection(n, rdata); + pSec->chunks.insert(pSec->chunks.end(), v.begin(), v.end()); }; // The loader assumes a specific order of data. // Add each type in the correct order. - Add(".idata$2", Idata.Dirs); - Add(".idata$4", Idata.Lookups); - Add(".idata$5", Idata.Addresses); - Add(".idata$6", Idata.Hints); - Add(".idata$7", Idata.DLLNames); + add(".idata$2", idata.dirs); + add(".idata$4", idata.lookups); + add(".idata$5", idata.addresses); + add(".idata$6", idata.hints); + add(".idata$7", idata.dllNames); } // Locate the first Chunk and size of the import directory list and the // IAT. -void Writer::locateImportTables( - std::map, std::vector> &Map) { - uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; - std::vector &ImportTables = Map[{".idata$2", RDATA}]; - if (!ImportTables.empty()) - ImportTableStart = ImportTables.front(); - for (Chunk *C : ImportTables) - ImportTableSize += C->getSize(); +void Writer::locateImportTables() { + uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; - std::vector &IAT = Map[{".idata$5", RDATA}]; - if (!IAT.empty()) - IATStart = IAT.front(); - for (Chunk *C : IAT) - IATSize += C->getSize(); + if (PartialSection *importDirs = findPartialSection(".idata$2", rdata)) { + if (!importDirs->chunks.empty()) + importTableStart = importDirs->chunks.front(); + for (Chunk *c : importDirs->chunks) + importTableSize += c->getSize(); + } + + if (PartialSection *importAddresses = findPartialSection(".idata$5", rdata)) { + if (!importAddresses->chunks.empty()) + iatStart = importAddresses->chunks.front(); + for (Chunk *c : importAddresses->chunks) + iatSize += c->getSize(); + } } // Create output section objects and add them to OutputSections. void Writer::createSections() { // First, create the builtin sections. - const uint32_t DATA = IMAGE_SCN_CNT_INITIALIZED_DATA; - const uint32_t BSS = IMAGE_SCN_CNT_UNINITIALIZED_DATA; - const uint32_t CODE = IMAGE_SCN_CNT_CODE; - const uint32_t DISCARDABLE = IMAGE_SCN_MEM_DISCARDABLE; - const uint32_t R = IMAGE_SCN_MEM_READ; - const uint32_t W = IMAGE_SCN_MEM_WRITE; - const uint32_t X = IMAGE_SCN_MEM_EXECUTE; + const uint32_t data = IMAGE_SCN_CNT_INITIALIZED_DATA; + const uint32_t bss = IMAGE_SCN_CNT_UNINITIALIZED_DATA; + const uint32_t code = IMAGE_SCN_CNT_CODE; + const uint32_t discardable = IMAGE_SCN_MEM_DISCARDABLE; + const uint32_t r = IMAGE_SCN_MEM_READ; + const uint32_t w = IMAGE_SCN_MEM_WRITE; + const uint32_t x = IMAGE_SCN_MEM_EXECUTE; - SmallDenseMap, OutputSection *> Sections; - auto CreateSection = [&](StringRef Name, uint32_t OutChars) { - OutputSection *&Sec = Sections[{Name, OutChars}]; - if (!Sec) { - Sec = make(Name, OutChars); - OutputSections.push_back(Sec); + SmallDenseMap, OutputSection *> sections; + auto createSection = [&](StringRef name, uint32_t outChars) { + OutputSection *&sec = sections[{name, outChars}]; + if (!sec) { + sec = make(name, outChars); + outputSections.push_back(sec); } - return Sec; + return sec; }; // Try to match the section order used by link.exe. - TextSec = CreateSection(".text", CODE | R | X); - CreateSection(".bss", BSS | R | W); - RdataSec = CreateSection(".rdata", DATA | R); - BuildidSec = CreateSection(".buildid", DATA | R); - DataSec = CreateSection(".data", DATA | R | W); - PdataSec = CreateSection(".pdata", DATA | R); - IdataSec = CreateSection(".idata", DATA | R); - EdataSec = CreateSection(".edata", DATA | R); - DidatSec = CreateSection(".didat", DATA | R); - RsrcSec = CreateSection(".rsrc", DATA | R); - RelocSec = CreateSection(".reloc", DATA | DISCARDABLE | R); - CtorsSec = CreateSection(".ctors", DATA | R | W); - DtorsSec = CreateSection(".dtors", DATA | R | W); + textSec = createSection(".text", code | r | x); + createSection(".bss", bss | r | w); + rdataSec = createSection(".rdata", data | r); + buildidSec = createSection(".buildid", data | r); + dataSec = createSection(".data", data | r | w); + pdataSec = createSection(".pdata", data | r); + idataSec = createSection(".idata", data | r); + edataSec = createSection(".edata", data | r); + didatSec = createSection(".didat", data | r); + rsrcSec = createSection(".rsrc", data | r); + relocSec = createSection(".reloc", data | discardable | r); + ctorsSec = createSection(".ctors", data | r | w); + dtorsSec = createSection(".dtors", data | r | w); // Then bin chunks by name and output characteristics. - std::map, std::vector> Map; - for (Chunk *C : Symtab->getChunks()) { - auto *SC = dyn_cast(C); - if (SC && !SC->Live) { - if (Config->Verbose) - SC->printDiscardedMessage(); + for (Chunk *c : symtab->getChunks()) { + auto *sc = dyn_cast(c); + if (sc && !sc->live) { + if (config->verbose) + sc->printDiscardedMessage(); continue; } - Map[{C->getSectionName(), C->getOutputCharacteristics()}].push_back(C); + StringRef name = c->getSectionName(); + // On MinGW, comdat groups are formed by putting the comdat group name + // after the '$' in the section name. Such a section name suffix shouldn't + // imply separate alphabetical sorting of those section chunks though. + if (config->mingw && sc && sc->isCOMDAT()) + name = name.split('$').first; + PartialSection *pSec = createPartialSection(name, + c->getOutputCharacteristics()); + pSec->chunks.push_back(c); } + fixPartialSectionChars(".rsrc", data | r); // Even in non MinGW cases, we might need to link against GNU import // libraries. - bool HasIdata = fixGnuImportChunks(Map); - if (!Idata.empty()) - HasIdata = true; + bool hasIdata = fixGnuImportChunks(); + if (!idata.empty()) + hasIdata = true; - if (HasIdata) - addSyntheticIdata(Idata, Map); + if (hasIdata) + addSyntheticIdata(); // Process an /order option. - if (!Config->Order.empty()) - for (auto &Pair : Map) - sortBySectionOrder(Pair.second); + if (!config->order.empty()) + for (auto it : partialSections) + sortBySectionOrder(it.second->chunks); - if (HasIdata) - locateImportTables(Map); + if (hasIdata) + locateImportTables(); // Then create an OutputSection for each section. // '$' and all following characters in input section names are // discarded when determining output section. So, .text$foo // contributes to .text, for example. See PE/COFF spec 3.2. - for (auto &Pair : Map) { - StringRef Name = getOutputSectionName(Pair.first.first); - uint32_t OutChars = Pair.first.second; + for (auto it : partialSections) { + PartialSection *pSec = it.second; + StringRef name = getOutputSectionName(pSec->name); + uint32_t outChars = pSec->characteristics; - if (Name == ".CRT") { + if (name == ".CRT") { // In link.exe, there is a special case for the I386 target where .CRT // sections are treated as if they have output characteristics DATA | R if // their characteristics are DATA | R | W. This implements the same // special case for all architectures. - OutChars = DATA | R; + outChars = data | r; - log("Processing section " + Pair.first.first + " -> " + Name); + log("Processing section " + pSec->name + " -> " + name); - sortCRTSectionChunks(Pair.second); + sortCRTSectionChunks(pSec->chunks); } - OutputSection *Sec = CreateSection(Name, OutChars); - std::vector &Chunks = Pair.second; - for (Chunk *C : Chunks) - Sec->addChunk(C); + OutputSection *sec = createSection(name, outChars); + for (Chunk *c : pSec->chunks) + sec->addChunk(c); + + sec->addContributingPartialSection(pSec); } // Finally, move some output sections to the end. - auto SectionOrder = [&](OutputSection *S) { - // Move DISCARDABLE (or non-memory-mapped) sections to the end of file because - // the loader cannot handle holes. Stripping can remove other discardable ones - // than .reloc, which is first of them (created early). - if (S->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) + auto sectionOrder = [&](const OutputSection *s) { + // Move DISCARDABLE (or non-memory-mapped) sections to the end of file + // because the loader cannot handle holes. Stripping can remove other + // discardable ones than .reloc, which is first of them (created early). + if (s->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) return 2; // .rsrc should come at the end of the non-discardable sections because its // size may change by the Win32 UpdateResources() function, causing // subsequent sections to move (see https://crbug.com/827082). - if (S == RsrcSec) + if (s == rsrcSec) return 1; return 0; }; - std::stable_sort(OutputSections.begin(), OutputSections.end(), - [&](OutputSection *S, OutputSection *T) { - return SectionOrder(S) < SectionOrder(T); - }); + llvm::stable_sort(outputSections, + [&](const OutputSection *s, const OutputSection *t) { + return sectionOrder(s) < sectionOrder(t); + }); } void Writer::createMiscChunks() { - for (auto &P : MergeChunk::Instances) - RdataSec->addChunk(P.second); + for (MergeChunk *p : MergeChunk::instances) { + if (p) { + p->finalizeContents(); + rdataSec->addChunk(p); + } + } // Create thunks for locally-dllimported symbols. - if (!Symtab->LocalImportChunks.empty()) { - for (Chunk *C : Symtab->LocalImportChunks) - RdataSec->addChunk(C); + if (!symtab->localImportChunks.empty()) { + for (Chunk *c : symtab->localImportChunks) + rdataSec->addChunk(c); } // Create Debug Information Chunks - OutputSection *DebugInfoSec = Config->MinGW ? BuildidSec : RdataSec; - if (Config->Debug || Config->Repro) { - DebugDirectory = make(DebugRecords, Config->Repro); - DebugInfoSec->addChunk(DebugDirectory); + OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec; + if (config->debug || config->repro) { + debugDirectory = make(debugRecords, config->repro); + debugInfoSec->addChunk(debugDirectory); } - if (Config->Debug) { + if (config->debug) { // 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. - BuildId = make(); - DebugRecords.push_back(BuildId); + buildId = make(); + debugRecords.push_back(buildId); - for (Chunk *C : DebugRecords) - DebugInfoSec->addChunk(C); + for (Chunk *c : debugRecords) + debugInfoSec->addChunk(c); } // Create SEH table. x86-only. - if (Config->Machine == I386) + if (config->safeSEH) createSEHTable(); // Create /guard:cf tables if requested. - if (Config->GuardCF != GuardCFLevel::Off) + if (config->guardCF != GuardCFLevel::Off) createGuardCFTables(); - if (Config->MinGW) { + if (config->mingw) { createRuntimePseudoRelocs(); insertCtorDtorSymbols(); } } // Create .idata section for the DLL-imported symbol table. // The format of this section is inherently Windows-specific. // IdataContents class abstracted away the details for us, // so we just let it create chunks and add them to the section. void Writer::createImportTables() { // 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 : ImportFile::Instances) { - if (!File->Live) + for (ImportFile *file : ImportFile::instances) { + if (!file->live) continue; - std::string DLL = StringRef(File->DLLName).lower(); - if (Config->DLLOrder.count(DLL) == 0) - Config->DLLOrder[DLL] = Config->DLLOrder.size(); + std::string dll = StringRef(file->dllName).lower(); + if (config->dllOrder.count(dll) == 0) + config->dllOrder[dll] = config->dllOrder.size(); - if (File->ImpSym && !isa(File->ImpSym)) - fatal(toString(*File->ImpSym) + " was replaced"); - DefinedImportData *ImpSym = cast_or_null(File->ImpSym); - if (Config->DelayLoads.count(StringRef(File->DLLName).lower())) { - if (!File->ThunkSym) - fatal("cannot delay-load " + toString(File) + - " due to import of data: " + toString(*ImpSym)); - DelayIdata.add(ImpSym); + if (file->impSym && !isa(file->impSym)) + fatal(toString(*file->impSym) + " was replaced"); + DefinedImportData *impSym = cast_or_null(file->impSym); + if (config->delayLoads.count(StringRef(file->dllName).lower())) { + if (!file->thunkSym) + fatal("cannot delay-load " + toString(file) + + " due to import of data: " + toString(*impSym)); + delayIdata.add(impSym); } else { - Idata.add(ImpSym); + idata.add(impSym); } } } void Writer::appendImportThunks() { - if (ImportFile::Instances.empty()) + if (ImportFile::instances.empty()) return; - for (ImportFile *File : ImportFile::Instances) { - if (!File->Live) + for (ImportFile *file : ImportFile::instances) { + if (!file->live) continue; - if (!File->ThunkSym) + if (!file->thunkSym) continue; - if (!isa(File->ThunkSym)) - fatal(toString(*File->ThunkSym) + " was replaced"); - DefinedImportThunk *Thunk = cast(File->ThunkSym); - if (File->ThunkLive) - TextSec->addChunk(Thunk->getChunk()); + if (!isa(file->thunkSym)) + fatal(toString(*file->thunkSym) + " was replaced"); + DefinedImportThunk *thunk = cast(file->thunkSym); + if (file->thunkLive) + textSec->addChunk(thunk->getChunk()); } - if (!DelayIdata.empty()) { - Defined *Helper = cast(Config->DelayLoadHelper); - DelayIdata.create(Helper); - for (Chunk *C : DelayIdata.getChunks()) - DidatSec->addChunk(C); - for (Chunk *C : DelayIdata.getDataChunks()) - DataSec->addChunk(C); - for (Chunk *C : DelayIdata.getCodeChunks()) - TextSec->addChunk(C); + if (!delayIdata.empty()) { + Defined *helper = cast(config->delayLoadHelper); + delayIdata.create(helper); + for (Chunk *c : delayIdata.getChunks()) + didatSec->addChunk(c); + for (Chunk *c : delayIdata.getDataChunks()) + dataSec->addChunk(c); + for (Chunk *c : delayIdata.getCodeChunks()) + textSec->addChunk(c); } } void Writer::createExportTable() { - if (Config->Exports.empty()) + if (config->exports.empty()) return; - for (Chunk *C : Edata.Chunks) - EdataSec->addChunk(C); + for (Chunk *c : edata.chunks) + edataSec->addChunk(c); } void Writer::removeUnusedSections() { // Remove sections that we can be sure won't get content, to avoid // allocating space for their section headers. - auto IsUnused = [this](OutputSection *S) { - if (S == RelocSec) + auto isUnused = [this](OutputSection *s) { + if (s == relocSec) return false; // This section is populated later. // MergeChunks have zero size at this point, as their size is finalized // later. Only remove sections that have no Chunks at all. - return S->Chunks.empty(); + return s->chunks.empty(); }; - OutputSections.erase( - std::remove_if(OutputSections.begin(), OutputSections.end(), IsUnused), - OutputSections.end()); + outputSections.erase( + std::remove_if(outputSections.begin(), outputSections.end(), isUnused), + outputSections.end()); } // The Windows loader doesn't seem to like empty sections, // so we remove them if any. void Writer::removeEmptySections() { - auto IsEmpty = [](OutputSection *S) { return S->getVirtualSize() == 0; }; - OutputSections.erase( - std::remove_if(OutputSections.begin(), OutputSections.end(), IsEmpty), - OutputSections.end()); - uint32_t Idx = 1; - for (OutputSection *Sec : OutputSections) - Sec->SectionIndex = Idx++; + auto isEmpty = [](OutputSection *s) { return s->getVirtualSize() == 0; }; + outputSections.erase( + std::remove_if(outputSections.begin(), outputSections.end(), isEmpty), + outputSections.end()); } -size_t Writer::addEntryToStringTable(StringRef Str) { - assert(Str.size() > COFF::NameSize); - size_t OffsetOfEntry = Strtab.size() + 4; // +4 for the size field - Strtab.insert(Strtab.end(), Str.begin(), Str.end()); - Strtab.push_back('\0'); - return OffsetOfEntry; +void Writer::assignOutputSectionIndices() { + // Assign final output section indices, and assign each chunk to its output + // section. + uint32_t idx = 1; + for (OutputSection *os : outputSections) { + os->sectionIndex = idx; + for (Chunk *c : os->chunks) + c->setOutputSectionIdx(idx); + ++idx; + } + + // Merge chunks are containers of chunks, so assign those an output section + // too. + for (MergeChunk *mc : MergeChunk::instances) + if (mc) + for (SectionChunk *sc : mc->sections) + if (sc && sc->live) + sc->setOutputSectionIdx(mc->getOutputSectionIdx()); } -Optional Writer::createSymbol(Defined *Def) { - coff_symbol16 Sym; - switch (Def->kind()) { +size_t Writer::addEntryToStringTable(StringRef str) { + assert(str.size() > COFF::NameSize); + size_t offsetOfEntry = strtab.size() + 4; // +4 for the size field + strtab.insert(strtab.end(), str.begin(), str.end()); + strtab.push_back('\0'); + return offsetOfEntry; +} + +Optional Writer::createSymbol(Defined *def) { + coff_symbol16 sym; + switch (def->kind()) { case Symbol::DefinedAbsoluteKind: - Sym.Value = Def->getRVA(); - Sym.SectionNumber = IMAGE_SYM_ABSOLUTE; + sym.Value = def->getRVA(); + sym.SectionNumber = IMAGE_SYM_ABSOLUTE; break; case Symbol::DefinedSyntheticKind: // Relative symbols are unrepresentable in a COFF symbol table. return None; default: { // Don't write symbols that won't be written to the output to the symbol // table. - Chunk *C = Def->getChunk(); - if (!C) + Chunk *c = def->getChunk(); + if (!c) return None; - OutputSection *OS = C->getOutputSection(); - if (!OS) + OutputSection *os = c->getOutputSection(); + if (!os) return None; - Sym.Value = Def->getRVA() - OS->getRVA(); - Sym.SectionNumber = OS->SectionIndex; + sym.Value = def->getRVA() - os->getRVA(); + sym.SectionNumber = os->sectionIndex; break; } } - StringRef Name = Def->getName(); - if (Name.size() > COFF::NameSize) { - Sym.Name.Offset.Zeroes = 0; - Sym.Name.Offset.Offset = addEntryToStringTable(Name); + StringRef name = def->getName(); + if (name.size() > COFF::NameSize) { + sym.Name.Offset.Zeroes = 0; + sym.Name.Offset.Offset = addEntryToStringTable(name); } else { - memset(Sym.Name.ShortName, 0, COFF::NameSize); - memcpy(Sym.Name.ShortName, Name.data(), Name.size()); + memset(sym.Name.ShortName, 0, COFF::NameSize); + memcpy(sym.Name.ShortName, name.data(), name.size()); } - if (auto *D = dyn_cast(Def)) { - COFFSymbolRef Ref = D->getCOFFSymbol(); - Sym.Type = Ref.getType(); - Sym.StorageClass = Ref.getStorageClass(); + if (auto *d = dyn_cast(def)) { + COFFSymbolRef ref = d->getCOFFSymbol(); + sym.Type = ref.getType(); + sym.StorageClass = ref.getStorageClass(); } else { - Sym.Type = IMAGE_SYM_TYPE_NULL; - Sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; + sym.Type = IMAGE_SYM_TYPE_NULL; + sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; } - Sym.NumberOfAuxSymbols = 0; - return Sym; + sym.NumberOfAuxSymbols = 0; + return sym; } void Writer::createSymbolAndStringTable() { // PE/COFF images are limited to 8 byte section names. Longer names can be // supported by writing a non-standard string table, but this string table is // not mapped at runtime and the long names will therefore be inaccessible. // link.exe always truncates section names to 8 bytes, whereas binutils always // preserves long section names via the string table. LLD adopts a hybrid // solution where discardable sections have long names preserved and // non-discardable sections have their names truncated, to ensure that any // section which is mapped at runtime also has its name mapped at runtime. - for (OutputSection *Sec : OutputSections) { - if (Sec->Name.size() <= COFF::NameSize) + for (OutputSection *sec : outputSections) { + if (sec->name.size() <= COFF::NameSize) continue; - if ((Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) + if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) continue; - Sec->setStringTableOff(addEntryToStringTable(Sec->Name)); + sec->setStringTableOff(addEntryToStringTable(sec->name)); } - if (Config->DebugDwarf || Config->DebugSymtab) { - for (ObjFile *File : ObjFile::Instances) { - for (Symbol *B : File->getSymbols()) { - auto *D = dyn_cast_or_null(B); - if (!D || D->WrittenToSymtab) + if (config->debugDwarf || config->debugSymtab) { + for (ObjFile *file : ObjFile::instances) { + for (Symbol *b : file->getSymbols()) { + auto *d = dyn_cast_or_null(b); + if (!d || d->writtenToSymtab) continue; - D->WrittenToSymtab = true; + d->writtenToSymtab = true; - if (Optional Sym = createSymbol(D)) - OutputSymtab.push_back(*Sym); + if (Optional sym = createSymbol(d)) + outputSymtab.push_back(*sym); } } } - if (OutputSymtab.empty() && Strtab.empty()) + if (outputSymtab.empty() && strtab.empty()) return; // We position the symbol table to be adjacent to the end of the last section. - uint64_t FileOff = FileSize; - PointerToSymbolTable = FileOff; - FileOff += OutputSymtab.size() * sizeof(coff_symbol16); - FileOff += 4 + Strtab.size(); - FileSize = alignTo(FileOff, SectorSize); + uint64_t fileOff = fileSize; + pointerToSymbolTable = fileOff; + fileOff += outputSymtab.size() * sizeof(coff_symbol16); + fileOff += 4 + strtab.size(); + fileSize = alignTo(fileOff, config->fileAlign); } void Writer::mergeSections() { - if (!PdataSec->Chunks.empty()) { - FirstPdata = PdataSec->Chunks.front(); - LastPdata = PdataSec->Chunks.back(); + if (!pdataSec->chunks.empty()) { + firstPdata = pdataSec->chunks.front(); + lastPdata = pdataSec->chunks.back(); } - for (auto &P : Config->Merge) { - StringRef ToName = P.second; - if (P.first == ToName) + for (auto &p : config->merge) { + StringRef toName = p.second; + if (p.first == toName) continue; - StringSet<> Names; + StringSet<> names; while (1) { - if (!Names.insert(ToName).second) - fatal("/merge: cycle found for section '" + P.first + "'"); - auto I = Config->Merge.find(ToName); - if (I == Config->Merge.end()) + if (!names.insert(toName).second) + fatal("/merge: cycle found for section '" + p.first + "'"); + auto i = config->merge.find(toName); + if (i == config->merge.end()) break; - ToName = I->second; + toName = i->second; } - OutputSection *From = findSection(P.first); - OutputSection *To = findSection(ToName); - if (!From) + OutputSection *from = findSection(p.first); + OutputSection *to = findSection(toName); + if (!from) continue; - if (!To) { - From->Name = ToName; + if (!to) { + from->name = toName; continue; } - To->merge(From); + to->merge(from); } } -// Visits all sections to initialize their relocation targets. -void Writer::readRelocTargets() { - for (OutputSection *Sec : OutputSections) - for_each(parallel::par, Sec->Chunks.begin(), Sec->Chunks.end(), - [&](Chunk *C) { C->readRelocTargets(); }); -} - // Visits all sections to assign incremental, non-overlapping RVAs and // file offsets. void Writer::assignAddresses() { - SizeOfHeaders = DOSStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + - sizeof(data_directory) * NumberOfDataDirectory + - sizeof(coff_section) * OutputSections.size(); - SizeOfHeaders += - Config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); - SizeOfHeaders = alignTo(SizeOfHeaders, SectorSize); - uint64_t RVA = PageSize; // The first page is kept unmapped. - FileSize = SizeOfHeaders; + sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + + sizeof(data_directory) * numberOfDataDirectory + + sizeof(coff_section) * outputSections.size(); + sizeOfHeaders += + config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); + sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign); + uint64_t rva = pageSize; // The first page is kept unmapped. + fileSize = sizeOfHeaders; - for (OutputSection *Sec : OutputSections) { - if (Sec == RelocSec) + for (OutputSection *sec : outputSections) { + if (sec == relocSec) addBaserels(); - uint64_t RawSize = 0, VirtualSize = 0; - Sec->Header.VirtualAddress = RVA; - for (Chunk *C : Sec->Chunks) { - VirtualSize = alignTo(VirtualSize, C->Alignment); - C->setRVA(RVA + VirtualSize); - C->OutputSectionOff = VirtualSize; - C->finalizeContents(); - VirtualSize += C->getSize(); - if (C->hasData()) - RawSize = alignTo(VirtualSize, SectorSize); + uint64_t rawSize = 0, virtualSize = 0; + sec->header.VirtualAddress = rva; + + // If /FUNCTIONPADMIN is used, functions are padded in order to create a + // hotpatchable image. + const bool isCodeSection = + (sec->header.Characteristics & IMAGE_SCN_CNT_CODE) && + (sec->header.Characteristics & IMAGE_SCN_MEM_READ) && + (sec->header.Characteristics & IMAGE_SCN_MEM_EXECUTE); + uint32_t padding = isCodeSection ? config->functionPadMin : 0; + + for (Chunk *c : sec->chunks) { + if (padding && c->isHotPatchable()) + virtualSize += padding; + virtualSize = alignTo(virtualSize, c->getAlignment()); + c->setRVA(rva + virtualSize); + virtualSize += c->getSize(); + if (c->hasData) + rawSize = alignTo(virtualSize, config->fileAlign); } - if (VirtualSize > UINT32_MAX) - error("section larger than 4 GiB: " + Sec->Name); - Sec->Header.VirtualSize = VirtualSize; - Sec->Header.SizeOfRawData = RawSize; - if (RawSize != 0) - Sec->Header.PointerToRawData = FileSize; - RVA += alignTo(VirtualSize, PageSize); - FileSize += alignTo(RawSize, SectorSize); + if (virtualSize > UINT32_MAX) + error("section larger than 4 GiB: " + sec->name); + sec->header.VirtualSize = virtualSize; + sec->header.SizeOfRawData = rawSize; + if (rawSize != 0) + sec->header.PointerToRawData = fileSize; + rva += alignTo(virtualSize, pageSize); + fileSize += alignTo(rawSize, config->fileAlign); } - SizeOfImage = alignTo(RVA, PageSize); + sizeOfImage = alignTo(rva, pageSize); + + // Assign addresses to sections in MergeChunks. + for (MergeChunk *mc : MergeChunk::instances) + if (mc) + mc->assignSubsectionRVAs(); } template void Writer::writeHeader() { // Write DOS header. For backwards compatibility, the first part of a PE/COFF // executable consists of an MS-DOS MZ executable. If the executable is run // under DOS, that program gets run (usually to just print an error message). // When run under Windows, the loader looks at AddressOfNewExeHeader and uses // the PE header instead. - uint8_t *Buf = Buffer->getBufferStart(); - auto *DOS = reinterpret_cast(Buf); - Buf += sizeof(dos_header); - DOS->Magic[0] = 'M'; - DOS->Magic[1] = 'Z'; - DOS->UsedBytesInTheLastPage = DOSStubSize % 512; - DOS->FileSizeInPages = divideCeil(DOSStubSize, 512); - DOS->HeaderSizeInParagraphs = sizeof(dos_header) / 16; + uint8_t *buf = buffer->getBufferStart(); + auto *dos = reinterpret_cast(buf); + buf += sizeof(dos_header); + dos->Magic[0] = 'M'; + dos->Magic[1] = 'Z'; + dos->UsedBytesInTheLastPage = dosStubSize % 512; + dos->FileSizeInPages = divideCeil(dosStubSize, 512); + dos->HeaderSizeInParagraphs = sizeof(dos_header) / 16; - DOS->AddressOfRelocationTable = sizeof(dos_header); - DOS->AddressOfNewExeHeader = DOSStubSize; + dos->AddressOfRelocationTable = sizeof(dos_header); + dos->AddressOfNewExeHeader = dosStubSize; // Write DOS program. - memcpy(Buf, DOSProgram, sizeof(DOSProgram)); - Buf += sizeof(DOSProgram); + memcpy(buf, dosProgram, sizeof(dosProgram)); + buf += sizeof(dosProgram); // Write PE magic - memcpy(Buf, PEMagic, sizeof(PEMagic)); - Buf += sizeof(PEMagic); + memcpy(buf, PEMagic, sizeof(PEMagic)); + buf += sizeof(PEMagic); // Write COFF header - auto *COFF = reinterpret_cast(Buf); - Buf += sizeof(*COFF); - COFF->Machine = Config->Machine; - COFF->NumberOfSections = OutputSections.size(); - COFF->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE; - if (Config->LargeAddressAware) - COFF->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE; - if (!Config->is64()) - COFF->Characteristics |= IMAGE_FILE_32BIT_MACHINE; - if (Config->DLL) - COFF->Characteristics |= IMAGE_FILE_DLL; - if (!Config->Relocatable) - COFF->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED; - COFF->SizeOfOptionalHeader = - sizeof(PEHeaderTy) + sizeof(data_directory) * NumberOfDataDirectory; + auto *coff = reinterpret_cast(buf); + buf += sizeof(*coff); + coff->Machine = config->machine; + coff->NumberOfSections = outputSections.size(); + coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE; + if (config->largeAddressAware) + coff->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE; + if (!config->is64()) + coff->Characteristics |= IMAGE_FILE_32BIT_MACHINE; + if (config->dll) + coff->Characteristics |= IMAGE_FILE_DLL; + if (!config->relocatable) + coff->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED; + if (config->swaprunCD) + coff->Characteristics |= IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP; + if (config->swaprunNet) + coff->Characteristics |= IMAGE_FILE_NET_RUN_FROM_SWAP; + coff->SizeOfOptionalHeader = + sizeof(PEHeaderTy) + sizeof(data_directory) * numberOfDataDirectory; // Write PE header - auto *PE = reinterpret_cast(Buf); - Buf += sizeof(*PE); - PE->Magic = Config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32; + auto *pe = reinterpret_cast(buf); + buf += sizeof(*pe); + pe->Magic = config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32; // If {Major,Minor}LinkerVersion is left at 0.0, then for some // reason signing the resulting PE file with Authenticode produces a // signature that fails to validate on Windows 7 (but is OK on 10). // Set it to 14.0, which is what VS2015 outputs, and which avoids // that problem. - PE->MajorLinkerVersion = 14; - PE->MinorLinkerVersion = 0; + pe->MajorLinkerVersion = 14; + pe->MinorLinkerVersion = 0; - PE->ImageBase = Config->ImageBase; - PE->SectionAlignment = PageSize; - PE->FileAlignment = SectorSize; - PE->MajorImageVersion = Config->MajorImageVersion; - PE->MinorImageVersion = Config->MinorImageVersion; - PE->MajorOperatingSystemVersion = Config->MajorOSVersion; - PE->MinorOperatingSystemVersion = Config->MinorOSVersion; - PE->MajorSubsystemVersion = Config->MajorOSVersion; - PE->MinorSubsystemVersion = Config->MinorOSVersion; - PE->Subsystem = Config->Subsystem; - PE->SizeOfImage = SizeOfImage; - PE->SizeOfHeaders = SizeOfHeaders; - if (!Config->NoEntry) { - Defined *Entry = cast(Config->Entry); - PE->AddressOfEntryPoint = Entry->getRVA(); + pe->ImageBase = config->imageBase; + pe->SectionAlignment = pageSize; + pe->FileAlignment = config->fileAlign; + pe->MajorImageVersion = config->majorImageVersion; + pe->MinorImageVersion = config->minorImageVersion; + pe->MajorOperatingSystemVersion = config->majorOSVersion; + pe->MinorOperatingSystemVersion = config->minorOSVersion; + pe->MajorSubsystemVersion = config->majorOSVersion; + pe->MinorSubsystemVersion = config->minorOSVersion; + pe->Subsystem = config->subsystem; + pe->SizeOfImage = sizeOfImage; + pe->SizeOfHeaders = sizeOfHeaders; + if (!config->noEntry) { + Defined *entry = cast(config->entry); + pe->AddressOfEntryPoint = entry->getRVA(); // Pointer to thumb code must have the LSB set, so adjust it. - if (Config->Machine == ARMNT) - PE->AddressOfEntryPoint |= 1; + if (config->machine == ARMNT) + pe->AddressOfEntryPoint |= 1; } - PE->SizeOfStackReserve = Config->StackReserve; - PE->SizeOfStackCommit = Config->StackCommit; - PE->SizeOfHeapReserve = Config->HeapReserve; - PE->SizeOfHeapCommit = Config->HeapCommit; - if (Config->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->GuardCF != GuardCFLevel::Off) - PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF; - if (Config->IntegrityCheck) - PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY; - if (SetNoSEHCharacteristic) - PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH; - if (Config->TerminalServerAware) - PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; - PE->NumberOfRvaAndSize = NumberOfDataDirectory; - if (TextSec->getVirtualSize()) { - PE->BaseOfCode = TextSec->getRVA(); - PE->SizeOfCode = TextSec->getRawSize(); + pe->SizeOfStackReserve = config->stackReserve; + pe->SizeOfStackCommit = config->stackCommit; + pe->SizeOfHeapReserve = config->heapReserve; + pe->SizeOfHeapCommit = config->heapCommit; + 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->guardCF != GuardCFLevel::Off) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF; + if (config->integrityCheck) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY; + if (setNoSEHCharacteristic) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH; + if (config->terminalServerAware) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; + pe->NumberOfRvaAndSize = numberOfDataDirectory; + if (textSec->getVirtualSize()) { + pe->BaseOfCode = textSec->getRVA(); + pe->SizeOfCode = textSec->getRawSize(); } - PE->SizeOfInitializedData = getSizeOfInitializedData(); + pe->SizeOfInitializedData = getSizeOfInitializedData(); // Write data directory - auto *Dir = reinterpret_cast(Buf); - Buf += sizeof(*Dir) * NumberOfDataDirectory; - if (!Config->Exports.empty()) { - Dir[EXPORT_TABLE].RelativeVirtualAddress = Edata.getRVA(); - Dir[EXPORT_TABLE].Size = Edata.getSize(); + auto *dir = reinterpret_cast(buf); + buf += sizeof(*dir) * numberOfDataDirectory; + if (!config->exports.empty()) { + dir[EXPORT_TABLE].RelativeVirtualAddress = edata.getRVA(); + dir[EXPORT_TABLE].Size = edata.getSize(); } - if (ImportTableStart) { - Dir[IMPORT_TABLE].RelativeVirtualAddress = ImportTableStart->getRVA(); - Dir[IMPORT_TABLE].Size = ImportTableSize; + if (importTableStart) { + dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA(); + dir[IMPORT_TABLE].Size = importTableSize; } - if (IATStart) { - Dir[IAT].RelativeVirtualAddress = IATStart->getRVA(); - Dir[IAT].Size = IATSize; + if (iatStart) { + dir[IAT].RelativeVirtualAddress = iatStart->getRVA(); + dir[IAT].Size = iatSize; } - if (RsrcSec->getVirtualSize()) { - Dir[RESOURCE_TABLE].RelativeVirtualAddress = RsrcSec->getRVA(); - Dir[RESOURCE_TABLE].Size = RsrcSec->getVirtualSize(); + if (rsrcSec->getVirtualSize()) { + dir[RESOURCE_TABLE].RelativeVirtualAddress = rsrcSec->getRVA(); + dir[RESOURCE_TABLE].Size = rsrcSec->getVirtualSize(); } - if (FirstPdata) { - Dir[EXCEPTION_TABLE].RelativeVirtualAddress = FirstPdata->getRVA(); - Dir[EXCEPTION_TABLE].Size = - LastPdata->getRVA() + LastPdata->getSize() - FirstPdata->getRVA(); + if (firstPdata) { + dir[EXCEPTION_TABLE].RelativeVirtualAddress = firstPdata->getRVA(); + dir[EXCEPTION_TABLE].Size = + lastPdata->getRVA() + lastPdata->getSize() - firstPdata->getRVA(); } - if (RelocSec->getVirtualSize()) { - Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = RelocSec->getRVA(); - Dir[BASE_RELOCATION_TABLE].Size = RelocSec->getVirtualSize(); + if (relocSec->getVirtualSize()) { + dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA(); + dir[BASE_RELOCATION_TABLE].Size = relocSec->getVirtualSize(); } - if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) { - if (Defined *B = dyn_cast(Sym)) { - Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA(); - Dir[TLS_TABLE].Size = Config->is64() + if (Symbol *sym = symtab->findUnderscore("_tls_used")) { + if (Defined *b = dyn_cast(sym)) { + dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA(); + dir[TLS_TABLE].Size = config->is64() ? sizeof(object::coff_tls_directory64) : sizeof(object::coff_tls_directory32); } } - if (DebugDirectory) { - Dir[DEBUG_DIRECTORY].RelativeVirtualAddress = DebugDirectory->getRVA(); - Dir[DEBUG_DIRECTORY].Size = DebugDirectory->getSize(); + if (debugDirectory) { + dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA(); + dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize(); } - if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) { - if (auto *B = dyn_cast(Sym)) { - SectionChunk *SC = B->getChunk(); - assert(B->getRVA() >= SC->getRVA()); - uint64_t OffsetInChunk = B->getRVA() - SC->getRVA(); - if (!SC->hasData() || OffsetInChunk + 4 > SC->getSize()) + if (Symbol *sym = symtab->findUnderscore("_load_config_used")) { + if (auto *b = dyn_cast(sym)) { + SectionChunk *sc = b->getChunk(); + assert(b->getRVA() >= sc->getRVA()); + uint64_t offsetInChunk = b->getRVA() - sc->getRVA(); + if (!sc->hasData || offsetInChunk + 4 > sc->getSize()) fatal("_load_config_used is malformed"); - ArrayRef SecContents = SC->getContents(); - uint32_t LoadConfigSize = - *reinterpret_cast(&SecContents[OffsetInChunk]); - if (OffsetInChunk + LoadConfigSize > SC->getSize()) + ArrayRef secContents = sc->getContents(); + uint32_t loadConfigSize = + *reinterpret_cast(&secContents[offsetInChunk]); + if (offsetInChunk + loadConfigSize > sc->getSize()) fatal("_load_config_used is too large"); - Dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = B->getRVA(); - Dir[LOAD_CONFIG_TABLE].Size = LoadConfigSize; + dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = b->getRVA(); + dir[LOAD_CONFIG_TABLE].Size = loadConfigSize; } } - if (!DelayIdata.empty()) { - Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress = - DelayIdata.getDirRVA(); - Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize(); + if (!delayIdata.empty()) { + dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress = + delayIdata.getDirRVA(); + dir[DELAY_IMPORT_DESCRIPTOR].Size = delayIdata.getDirSize(); } // Write section table - for (OutputSection *Sec : OutputSections) { - Sec->writeHeaderTo(Buf); - Buf += sizeof(coff_section); + for (OutputSection *sec : outputSections) { + sec->writeHeaderTo(buf); + buf += sizeof(coff_section); } - SectionTable = ArrayRef( - Buf - OutputSections.size() * sizeof(coff_section), Buf); + sectionTable = ArrayRef( + buf - outputSections.size() * sizeof(coff_section), buf); - if (OutputSymtab.empty() && Strtab.empty()) + if (outputSymtab.empty() && strtab.empty()) return; - COFF->PointerToSymbolTable = PointerToSymbolTable; - uint32_t NumberOfSymbols = OutputSymtab.size(); - COFF->NumberOfSymbols = NumberOfSymbols; - auto *SymbolTable = reinterpret_cast( - Buffer->getBufferStart() + COFF->PointerToSymbolTable); - for (size_t I = 0; I != NumberOfSymbols; ++I) - SymbolTable[I] = OutputSymtab[I]; + coff->PointerToSymbolTable = pointerToSymbolTable; + uint32_t numberOfSymbols = outputSymtab.size(); + coff->NumberOfSymbols = numberOfSymbols; + auto *symbolTable = reinterpret_cast( + buffer->getBufferStart() + coff->PointerToSymbolTable); + for (size_t i = 0; i != numberOfSymbols; ++i) + symbolTable[i] = outputSymtab[i]; // Create the string table, it follows immediately after the symbol table. // The first 4 bytes is length including itself. - Buf = reinterpret_cast(&SymbolTable[NumberOfSymbols]); - write32le(Buf, Strtab.size() + 4); - if (!Strtab.empty()) - memcpy(Buf + 4, Strtab.data(), Strtab.size()); + buf = reinterpret_cast(&symbolTable[numberOfSymbols]); + write32le(buf, strtab.size() + 4); + if (!strtab.empty()) + memcpy(buf + 4, strtab.data(), strtab.size()); } -void Writer::openFile(StringRef Path) { - Buffer = CHECK( - FileOutputBuffer::create(Path, FileSize, FileOutputBuffer::F_executable), - "failed to open " + Path); +void Writer::openFile(StringRef path) { + buffer = CHECK( + FileOutputBuffer::create(path, fileSize, FileOutputBuffer::F_executable), + "failed to open " + path); } void Writer::createSEHTable() { - // Set the no SEH characteristic on x86 binaries unless we find exception - // handlers. - SetNoSEHCharacteristic = true; - - SymbolRVASet Handlers; - for (ObjFile *File : ObjFile::Instances) { - // FIXME: We should error here instead of earlier unless /safeseh:no was - // passed. - if (!File->hasSafeSEH()) - return; - - markSymbolsForRVATable(File, File->getSXDataChunks(), Handlers); + SymbolRVASet handlers; + for (ObjFile *file : ObjFile::instances) { + if (!file->hasSafeSEH()) + error("/safeseh: " + file->getName() + " is not compatible with SEH"); + markSymbolsForRVATable(file, file->getSXDataChunks(), handlers); } - // Remove the "no SEH" characteristic if all object files were built with - // safeseh, we found some exception handlers, and there is a load config in - // the object. - SetNoSEHCharacteristic = - Handlers.empty() || !Symtab->findUnderscore("_load_config_used"); + // Set the "no SEH" characteristic if there really were no handlers, or if + // there is no load config object to point to the table of handlers. + setNoSEHCharacteristic = + handlers.empty() || !symtab->findUnderscore("_load_config_used"); - maybeAddRVATable(std::move(Handlers), "__safe_se_handler_table", + maybeAddRVATable(std::move(handlers), "__safe_se_handler_table", "__safe_se_handler_count"); } // Add a symbol to an RVA set. Two symbols may have the same RVA, but an RVA set // cannot contain duplicates. Therefore, the set is uniqued by Chunk and the // symbol's offset into that Chunk. -static void addSymbolToRVASet(SymbolRVASet &RVASet, Defined *S) { - Chunk *C = S->getChunk(); - if (auto *SC = dyn_cast(C)) - C = SC->Repl; // Look through ICF replacement. - uint32_t Off = S->getRVA() - (C ? C->getRVA() : 0); - RVASet.insert({C, Off}); +static void addSymbolToRVASet(SymbolRVASet &rvaSet, Defined *s) { + Chunk *c = s->getChunk(); + if (auto *sc = dyn_cast(c)) + c = sc->repl; // Look through ICF replacement. + uint32_t off = s->getRVA() - (c ? c->getRVA() : 0); + rvaSet.insert({c, off}); } // Given a symbol, add it to the GFIDs table if it is a live, defined, function // symbol in an executable section. -static void maybeAddAddressTakenFunction(SymbolRVASet &AddressTakenSyms, - Symbol *S) { - auto *D = dyn_cast_or_null(S); - - // Ignore undefined symbols and references to non-functions (e.g. globals and - // labels). - if (!D || - D->getCOFFSymbol().getComplexType() != COFF::IMAGE_SYM_DTYPE_FUNCTION) +static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms, + Symbol *s) { + if (!s) return; - // Mark the symbol as address taken if it's in an executable section. - Chunk *RefChunk = D->getChunk(); - OutputSection *OS = RefChunk ? RefChunk->getOutputSection() : nullptr; - if (OS && OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE) - addSymbolToRVASet(AddressTakenSyms, D); + switch (s->kind()) { + case Symbol::DefinedLocalImportKind: + case Symbol::DefinedImportDataKind: + // Defines an __imp_ pointer, so it is data, so it is ignored. + break; + case Symbol::DefinedCommonKind: + // Common is always data, so it is ignored. + break; + case Symbol::DefinedAbsoluteKind: + case Symbol::DefinedSyntheticKind: + // Absolute is never code, synthetic generally isn't and usually isn't + // determinable. + break; + case Symbol::LazyKind: + case Symbol::UndefinedKind: + // Undefined symbols resolve to zero, so they don't have an RVA. Lazy + // symbols shouldn't have relocations. + break; + + case Symbol::DefinedImportThunkKind: + // Thunks are always code, include them. + addSymbolToRVASet(addressTakenSyms, cast(s)); + break; + + case Symbol::DefinedRegularKind: { + // This is a regular, defined, symbol from a COFF file. Mark the symbol as + // address taken if the symbol type is function and it's in an executable + // section. + auto *d = cast(s); + if (d->getCOFFSymbol().getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION) { + SectionChunk *sc = dyn_cast(d->getChunk()); + if (sc && sc->live && + sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) + addSymbolToRVASet(addressTakenSyms, d); + } + break; + } + } } // Visit all relocations from all section contributions of this object file and // mark the relocation target as address-taken. -static void markSymbolsWithRelocations(ObjFile *File, - SymbolRVASet &UsedSymbols) { - for (Chunk *C : File->getChunks()) { +static void markSymbolsWithRelocations(ObjFile *file, + SymbolRVASet &usedSymbols) { + for (Chunk *c : file->getChunks()) { // We only care about live section chunks. Common chunks and other chunks // don't generally contain relocations. - SectionChunk *SC = dyn_cast(C); - if (!SC || !SC->Live) + SectionChunk *sc = dyn_cast(c); + if (!sc || !sc->live) continue; - for (const coff_relocation &Reloc : SC->Relocs) { - if (Config->Machine == I386 && Reloc.Type == COFF::IMAGE_REL_I386_REL32) + for (const coff_relocation &reloc : sc->getRelocs()) { + if (config->machine == I386 && reloc.Type == COFF::IMAGE_REL_I386_REL32) // Ignore relative relocations on x86. On x86_64 they can't be ignored // since they're also used to compute absolute addresses. continue; - Symbol *Ref = SC->File->getSymbol(Reloc.SymbolTableIndex); - maybeAddAddressTakenFunction(UsedSymbols, Ref); + Symbol *ref = sc->file->getSymbol(reloc.SymbolTableIndex); + maybeAddAddressTakenFunction(usedSymbols, ref); } } } // Create the guard function id table. This is a table of RVAs of all // address-taken functions. It is sorted and uniqued, just like the safe SEH // table. void Writer::createGuardCFTables() { - SymbolRVASet AddressTakenSyms; - SymbolRVASet LongJmpTargets; - for (ObjFile *File : ObjFile::Instances) { + SymbolRVASet addressTakenSyms; + SymbolRVASet longJmpTargets; + for (ObjFile *file : ObjFile::instances) { // If the object was compiled with /guard:cf, the address taken symbols // are in .gfids$y sections, and the longjmp targets are in .gljmp$y // sections. If the object was not compiled with /guard:cf, we assume there // were no setjmp targets, and that all code symbols with relocations are // possibly address-taken. - if (File->hasGuardCF()) { - markSymbolsForRVATable(File, File->getGuardFidChunks(), AddressTakenSyms); - markSymbolsForRVATable(File, File->getGuardLJmpChunks(), LongJmpTargets); + if (file->hasGuardCF()) { + markSymbolsForRVATable(file, file->getGuardFidChunks(), addressTakenSyms); + markSymbolsForRVATable(file, file->getGuardLJmpChunks(), longJmpTargets); } else { - markSymbolsWithRelocations(File, AddressTakenSyms); + markSymbolsWithRelocations(file, addressTakenSyms); } } // Mark the image entry as address-taken. - if (Config->Entry) - maybeAddAddressTakenFunction(AddressTakenSyms, Config->Entry); + if (config->entry) + maybeAddAddressTakenFunction(addressTakenSyms, config->entry); // Mark exported symbols in executable sections as address-taken. - for (Export &E : Config->Exports) - maybeAddAddressTakenFunction(AddressTakenSyms, E.Sym); + for (Export &e : config->exports) + maybeAddAddressTakenFunction(addressTakenSyms, e.sym); // Ensure sections referenced in the gfid table are 16-byte aligned. - for (const ChunkAndOffset &C : AddressTakenSyms) - if (C.InputChunk->Alignment < 16) - C.InputChunk->Alignment = 16; + for (const ChunkAndOffset &c : addressTakenSyms) + if (c.inputChunk->getAlignment() < 16) + c.inputChunk->setAlignment(16); - maybeAddRVATable(std::move(AddressTakenSyms), "__guard_fids_table", + maybeAddRVATable(std::move(addressTakenSyms), "__guard_fids_table", "__guard_fids_count"); // Add the longjmp target table unless the user told us not to. - if (Config->GuardCF == GuardCFLevel::Full) - maybeAddRVATable(std::move(LongJmpTargets), "__guard_longjmp_table", + if (config->guardCF == GuardCFLevel::Full) + maybeAddRVATable(std::move(longJmpTargets), "__guard_longjmp_table", "__guard_longjmp_count"); // Set __guard_flags, which will be used in the load config to indicate that // /guard:cf was enabled. - uint32_t GuardFlags = uint32_t(coff_guard_flags::CFInstrumented) | + uint32_t guardFlags = uint32_t(coff_guard_flags::CFInstrumented) | uint32_t(coff_guard_flags::HasFidTable); - if (Config->GuardCF == GuardCFLevel::Full) - GuardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable); - Symbol *FlagSym = Symtab->findUnderscore("__guard_flags"); - cast(FlagSym)->setVA(GuardFlags); + if (config->guardCF == GuardCFLevel::Full) + guardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable); + Symbol *flagSym = symtab->findUnderscore("__guard_flags"); + cast(flagSym)->setVA(guardFlags); } // Take a list of input sections containing symbol table indices and add those // symbols to an RVA table. The challenge is that symbol RVAs are not known and // depend on the table size, so we can't directly build a set of integers. -void Writer::markSymbolsForRVATable(ObjFile *File, - ArrayRef SymIdxChunks, - SymbolRVASet &TableSymbols) { - for (SectionChunk *C : SymIdxChunks) { +void Writer::markSymbolsForRVATable(ObjFile *file, + ArrayRef symIdxChunks, + SymbolRVASet &tableSymbols) { + for (SectionChunk *c : symIdxChunks) { // Skip sections discarded by linker GC. This comes up when a .gfids section // is associated with something like a vtable and the vtable is discarded. // In this case, the associated gfids section is discarded, and we don't // mark the virtual member functions as address-taken by the vtable. - if (!C->Live) + if (!c->live) continue; // Validate that the contents look like symbol table indices. - ArrayRef Data = C->getContents(); - if (Data.size() % 4 != 0) { - warn("ignoring " + C->getSectionName() + - " symbol table index section in object " + toString(File)); + ArrayRef data = c->getContents(); + if (data.size() % 4 != 0) { + warn("ignoring " + c->getSectionName() + + " symbol table index section in object " + toString(file)); continue; } // Read each symbol table index and check if that symbol was included in the // final link. If so, add it to the table symbol set. - ArrayRef SymIndices( - reinterpret_cast(Data.data()), Data.size() / 4); - ArrayRef ObjSymbols = File->getSymbols(); - for (uint32_t SymIndex : SymIndices) { - if (SymIndex >= ObjSymbols.size()) { + ArrayRef symIndices( + reinterpret_cast(data.data()), data.size() / 4); + ArrayRef objSymbols = file->getSymbols(); + for (uint32_t symIndex : symIndices) { + if (symIndex >= objSymbols.size()) { warn("ignoring invalid symbol table index in section " + - C->getSectionName() + " in object " + toString(File)); + c->getSectionName() + " in object " + toString(file)); continue; } - if (Symbol *S = ObjSymbols[SymIndex]) { - if (S->isLive()) - addSymbolToRVASet(TableSymbols, cast(S)); + if (Symbol *s = objSymbols[symIndex]) { + if (s->isLive()) + addSymbolToRVASet(tableSymbols, cast(s)); } } } } // Replace the absolute table symbol with a synthetic symbol pointing to -// TableChunk so that we can emit base relocations for it and resolve section +// tableChunk so that we can emit base relocations for it and resolve section // relative relocations. -void Writer::maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym, - StringRef CountSym) { - if (TableSymbols.empty()) +void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, + StringRef countSym) { + if (tableSymbols.empty()) return; - RVATableChunk *TableChunk = make(std::move(TableSymbols)); - RdataSec->addChunk(TableChunk); + RVATableChunk *tableChunk = make(std::move(tableSymbols)); + rdataSec->addChunk(tableChunk); - Symbol *T = Symtab->findUnderscore(TableSym); - Symbol *C = Symtab->findUnderscore(CountSym); - replaceSymbol(T, T->getName(), TableChunk); - cast(C)->setVA(TableChunk->getSize() / 4); + Symbol *t = symtab->findUnderscore(tableSym); + Symbol *c = symtab->findUnderscore(countSym); + replaceSymbol(t, t->getName(), tableChunk); + cast(c)->setVA(tableChunk->getSize() / 4); } // MinGW specific. Gather all relocations that are imported from a DLL even // though the code didn't expect it to, produce the table that the runtime // uses for fixing them up, and provide the synthetic symbols that the // runtime uses for finding the table. void Writer::createRuntimePseudoRelocs() { - std::vector Rels; + std::vector rels; - for (Chunk *C : Symtab->getChunks()) { - auto *SC = dyn_cast(C); - if (!SC || !SC->Live) + for (Chunk *c : symtab->getChunks()) { + auto *sc = dyn_cast(c); + if (!sc || !sc->live) continue; - SC->getRuntimePseudoRelocs(Rels); + sc->getRuntimePseudoRelocs(rels); } - if (!Rels.empty()) - log("Writing " + Twine(Rels.size()) + " runtime pseudo relocations"); - PseudoRelocTableChunk *Table = make(Rels); - RdataSec->addChunk(Table); - EmptyChunk *EndOfList = make(); - RdataSec->addChunk(EndOfList); + if (!rels.empty()) + log("Writing " + Twine(rels.size()) + " runtime pseudo relocations"); + PseudoRelocTableChunk *table = make(rels); + rdataSec->addChunk(table); + EmptyChunk *endOfList = make(); + rdataSec->addChunk(endOfList); - Symbol *HeadSym = Symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__"); - Symbol *EndSym = Symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__"); - replaceSymbol(HeadSym, HeadSym->getName(), Table); - replaceSymbol(EndSym, EndSym->getName(), EndOfList); + Symbol *headSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__"); + Symbol *endSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__"); + replaceSymbol(headSym, headSym->getName(), table); + replaceSymbol(endSym, endSym->getName(), endOfList); } // MinGW specific. // The MinGW .ctors and .dtors lists have sentinels at each end; // a (uintptr_t)-1 at the start and a (uintptr_t)0 at the end. // There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__ // and __DTOR_LIST__ respectively. void Writer::insertCtorDtorSymbols() { - AbsolutePointerChunk *CtorListHead = make(-1); - AbsolutePointerChunk *CtorListEnd = make(0); - AbsolutePointerChunk *DtorListHead = make(-1); - AbsolutePointerChunk *DtorListEnd = make(0); - CtorsSec->insertChunkAtStart(CtorListHead); - CtorsSec->addChunk(CtorListEnd); - DtorsSec->insertChunkAtStart(DtorListHead); - DtorsSec->addChunk(DtorListEnd); + AbsolutePointerChunk *ctorListHead = make(-1); + AbsolutePointerChunk *ctorListEnd = make(0); + AbsolutePointerChunk *dtorListHead = make(-1); + AbsolutePointerChunk *dtorListEnd = make(0); + ctorsSec->insertChunkAtStart(ctorListHead); + ctorsSec->addChunk(ctorListEnd); + dtorsSec->insertChunkAtStart(dtorListHead); + dtorsSec->addChunk(dtorListEnd); - Symbol *CtorListSym = Symtab->findUnderscore("__CTOR_LIST__"); - Symbol *DtorListSym = Symtab->findUnderscore("__DTOR_LIST__"); - replaceSymbol(CtorListSym, CtorListSym->getName(), - CtorListHead); - replaceSymbol(DtorListSym, DtorListSym->getName(), - DtorListHead); + Symbol *ctorListSym = symtab->findUnderscore("__CTOR_LIST__"); + Symbol *dtorListSym = symtab->findUnderscore("__DTOR_LIST__"); + replaceSymbol(ctorListSym, ctorListSym->getName(), + ctorListHead); + replaceSymbol(dtorListSym, dtorListSym->getName(), + dtorListHead); } // Handles /section options to allow users to overwrite // section attributes. void Writer::setSectionPermissions() { - for (auto &P : Config->Section) { - StringRef Name = P.first; - uint32_t Perm = P.second; - for (OutputSection *Sec : OutputSections) - if (Sec->Name == Name) - Sec->setPermissions(Perm); + for (auto &p : config->section) { + StringRef name = p.first; + uint32_t perm = p.second; + for (OutputSection *sec : outputSections) + if (sec->name == name) + sec->setPermissions(perm); } } // Write section contents to a mmap'ed file. void Writer::writeSections() { // Record the number of sections to apply section index relocations // against absolute symbols. See applySecIdx in Chunks.cpp.. - DefinedAbsolute::NumOutputSections = OutputSections.size(); + DefinedAbsolute::numOutputSections = outputSections.size(); - uint8_t *Buf = Buffer->getBufferStart(); - for (OutputSection *Sec : OutputSections) { - uint8_t *SecBuf = Buf + Sec->getFileOff(); + uint8_t *buf = buffer->getBufferStart(); + for (OutputSection *sec : outputSections) { + uint8_t *secBuf = buf + sec->getFileOff(); // Fill gaps between functions in .text with INT3 instructions // instead of leaving as NUL bytes (which can be interpreted as // ADD instructions). - if (Sec->Header.Characteristics & IMAGE_SCN_CNT_CODE) - memset(SecBuf, 0xCC, Sec->getRawSize()); - for_each(parallel::par, Sec->Chunks.begin(), Sec->Chunks.end(), - [&](Chunk *C) { C->writeTo(SecBuf); }); + if (sec->header.Characteristics & IMAGE_SCN_CNT_CODE) + memset(secBuf, 0xCC, sec->getRawSize()); + parallelForEach(sec->chunks, [&](Chunk *c) { + c->writeTo(secBuf + c->getRVA() - sec->getRVA()); + }); } } void Writer::writeBuildId() { // There are two important parts to the build ID. // 1) If building with debug info, the COFF debug directory contains a // timestamp as well as a Guid and Age of the PDB. // 2) In all cases, the PE COFF file header also contains a timestamp. // For reproducibility, instead of a timestamp we want to use a hash of the // PE contents. - if (Config->Debug) { - assert(BuildId && "BuildId is not set!"); + if (config->debug) { + assert(buildId && "BuildId is not set!"); // BuildId->BuildId was filled in when the PDB was written. } // At this point the only fields in the COFF file which remain unset are the // "timestamp" in the COFF file header, and the ones in the coff debug // directory. Now we can hash the file and write that hash to the various // timestamp fields in the file. - StringRef OutputFileData( - reinterpret_cast(Buffer->getBufferStart()), - Buffer->getBufferSize()); + StringRef outputFileData( + reinterpret_cast(buffer->getBufferStart()), + buffer->getBufferSize()); - uint32_t Timestamp = Config->Timestamp; - uint64_t Hash = 0; - bool GenerateSyntheticBuildId = - Config->MinGW && Config->Debug && Config->PDBPath.empty(); + uint32_t timestamp = config->timestamp; + uint64_t hash = 0; + bool generateSyntheticBuildId = + config->mingw && config->debug && config->pdbPath.empty(); - if (Config->Repro || GenerateSyntheticBuildId) - Hash = xxHash64(OutputFileData); + if (config->repro || generateSyntheticBuildId) + hash = xxHash64(outputFileData); - if (Config->Repro) - Timestamp = static_cast(Hash); + if (config->repro) + timestamp = static_cast(hash); - if (GenerateSyntheticBuildId) { + if (generateSyntheticBuildId) { // For MinGW builds without a PDB file, we still generate a build id // to allow associating a crash dump to the executable. - BuildId->BuildId->PDB70.CVSignature = OMF::Signature::PDB70; - BuildId->BuildId->PDB70.Age = 1; - memcpy(BuildId->BuildId->PDB70.Signature, &Hash, 8); + buildId->buildId->PDB70.CVSignature = OMF::Signature::PDB70; + buildId->buildId->PDB70.Age = 1; + memcpy(buildId->buildId->PDB70.Signature, &hash, 8); // xxhash only gives us 8 bytes, so put some fixed data in the other half. - memcpy(&BuildId->BuildId->PDB70.Signature[8], "LLD PDB.", 8); + memcpy(&buildId->buildId->PDB70.Signature[8], "LLD PDB.", 8); } - if (DebugDirectory) - DebugDirectory->setTimeDateStamp(Timestamp); + if (debugDirectory) + debugDirectory->setTimeDateStamp(timestamp); - uint8_t *Buf = Buffer->getBufferStart(); - Buf += DOSStubSize + sizeof(PEMagic); - object::coff_file_header *CoffHeader = - reinterpret_cast(Buf); - CoffHeader->TimeDateStamp = Timestamp; + uint8_t *buf = buffer->getBufferStart(); + buf += dosStubSize + sizeof(PEMagic); + object::coff_file_header *coffHeader = + reinterpret_cast(buf); + coffHeader->TimeDateStamp = timestamp; } // Sort .pdata section contents according to PE/COFF spec 5.5. void Writer::sortExceptionTable() { - if (!FirstPdata) + if (!firstPdata) return; // We assume .pdata contains function table entries only. - auto BufAddr = [&](Chunk *C) { - return Buffer->getBufferStart() + C->getOutputSection()->getFileOff() + - C->getRVA() - C->getOutputSection()->getRVA(); + auto bufAddr = [&](Chunk *c) { + OutputSection *os = c->getOutputSection(); + return buffer->getBufferStart() + os->getFileOff() + c->getRVA() - + os->getRVA(); }; - uint8_t *Begin = BufAddr(FirstPdata); - uint8_t *End = BufAddr(LastPdata) + LastPdata->getSize(); - if (Config->Machine == AMD64) { - struct Entry { ulittle32_t Begin, End, Unwind; }; - sort(parallel::par, (Entry *)Begin, (Entry *)End, - [](const Entry &A, const Entry &B) { return A.Begin < B.Begin; }); + uint8_t *begin = bufAddr(firstPdata); + uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize(); + if (config->machine == AMD64) { + struct Entry { ulittle32_t begin, end, unwind; }; + parallelSort( + MutableArrayRef((Entry *)begin, (Entry *)end), + [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); return; } - 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; }); + if (config->machine == ARMNT || config->machine == ARM64) { + struct Entry { ulittle32_t begin, unwind; }; + parallelSort( + MutableArrayRef((Entry *)begin, (Entry *)end), + [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); return; } errs() << "warning: don't know how to handle .pdata.\n"; } // The CRT section contains, among other things, the array of function // pointers that initialize every global variable that is not trivially // constructed. The CRT calls them one after the other prior to invoking // main(). // // As per C++ spec, 3.6.2/2.3, // "Variables with ordered initialization defined within a single // translation unit shall be initialized in the order of their definitions // in the translation unit" // // It is therefore critical to sort the chunks containing the function // pointers in the order that they are listed in the object file (top to // bottom), otherwise global objects might not be initialized in the // correct order. -void Writer::sortCRTSectionChunks(std::vector &Chunks) { - auto SectionChunkOrder = [](const Chunk *A, const Chunk *B) { - auto SA = dyn_cast(A); - auto SB = dyn_cast(B); - assert(SA && SB && "Non-section chunks in CRT section!"); +void Writer::sortCRTSectionChunks(std::vector &chunks) { + auto sectionChunkOrder = [](const Chunk *a, const Chunk *b) { + auto sa = dyn_cast(a); + auto sb = dyn_cast(b); + assert(sa && sb && "Non-section chunks in CRT section!"); - StringRef SAObj = SA->File->MB.getBufferIdentifier(); - StringRef SBObj = SB->File->MB.getBufferIdentifier(); + StringRef sAObj = sa->file->mb.getBufferIdentifier(); + StringRef sBObj = sb->file->mb.getBufferIdentifier(); - return SAObj == SBObj && SA->getSectionNumber() < SB->getSectionNumber(); + return sAObj == sBObj && sa->getSectionNumber() < sb->getSectionNumber(); }; - std::stable_sort(Chunks.begin(), Chunks.end(), SectionChunkOrder); + llvm::stable_sort(chunks, sectionChunkOrder); - if (Config->Verbose) { - for (auto &C : Chunks) { - auto SC = dyn_cast(C); - log(" " + SC->File->MB.getBufferIdentifier().str() + - ", SectionID: " + Twine(SC->getSectionNumber())); + if (config->verbose) { + for (auto &c : chunks) { + auto sc = dyn_cast(c); + log(" " + sc->file->mb.getBufferIdentifier().str() + + ", SectionID: " + Twine(sc->getSectionNumber())); } } } -OutputSection *Writer::findSection(StringRef Name) { - for (OutputSection *Sec : OutputSections) - if (Sec->Name == Name) - return Sec; +OutputSection *Writer::findSection(StringRef name) { + for (OutputSection *sec : outputSections) + if (sec->name == name) + return sec; return nullptr; } uint32_t Writer::getSizeOfInitializedData() { - uint32_t Res = 0; - for (OutputSection *S : OutputSections) - if (S->Header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) - Res += S->getRawSize(); - return Res; + uint32_t res = 0; + for (OutputSection *s : outputSections) + if (s->header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) + res += s->getRawSize(); + return res; } // Add base relocations to .reloc section. void Writer::addBaserels() { - if (!Config->Relocatable) + if (!config->relocatable) return; - RelocSec->Chunks.clear(); - std::vector V; - for (OutputSection *Sec : OutputSections) { - if (Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) + relocSec->chunks.clear(); + std::vector v; + for (OutputSection *sec : outputSections) { + if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) continue; // Collect all locations for base relocations. - for (Chunk *C : Sec->Chunks) - C->getBaserels(&V); + for (Chunk *c : sec->chunks) + c->getBaserels(&v); // Add the addresses to .reloc section. - if (!V.empty()) - addBaserelBlocks(V); - V.clear(); + if (!v.empty()) + addBaserelBlocks(v); + v.clear(); } } // Add addresses to .reloc section. Note that addresses are grouped by page. -void Writer::addBaserelBlocks(std::vector &V) { - const uint32_t Mask = ~uint32_t(PageSize - 1); - uint32_t Page = V[0].RVA & Mask; - size_t I = 0, J = 1; - for (size_t E = V.size(); J < E; ++J) { - uint32_t P = V[J].RVA & Mask; - if (P == Page) +void Writer::addBaserelBlocks(std::vector &v) { + const uint32_t mask = ~uint32_t(pageSize - 1); + uint32_t page = v[0].rva & mask; + size_t i = 0, j = 1; + for (size_t e = v.size(); j < e; ++j) { + uint32_t p = v[j].rva & mask; + if (p == page) continue; - RelocSec->addChunk(make(Page, &V[I], &V[0] + J)); - I = J; - Page = P; + relocSec->addChunk(make(page, &v[i], &v[0] + j)); + i = j; + page = p; } - if (I == J) + if (i == j) return; - RelocSec->addChunk(make(Page, &V[I], &V[0] + J)); + relocSec->addChunk(make(page, &v[i], &v[0] + j)); +} + +PartialSection *Writer::createPartialSection(StringRef name, + uint32_t outChars) { + PartialSection *&pSec = partialSections[{name, outChars}]; + if (pSec) + return pSec; + pSec = make(name, outChars); + return pSec; +} + +PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) { + auto it = partialSections.find({name, outChars}); + if (it != partialSections.end()) + return it->second; + return nullptr; } Index: vendor/lld/dist/COFF/Writer.h =================================================================== --- vendor/lld/dist/COFF/Writer.h (revision 351287) +++ vendor/lld/dist/COFF/Writer.h (revision 351288) @@ -1,75 +1,85 @@ //===- Writer.h -------------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #ifndef LLD_COFF_WRITER_H #define LLD_COFF_WRITER_H #include "Chunks.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/COFF.h" #include #include #include namespace lld { namespace coff { -static const int PageSize = 4096; +static const int pageSize = 4096; void writeResult(); +class PartialSection { +public: + PartialSection(StringRef n, uint32_t chars) + : name(n), characteristics(chars) {} + StringRef name; + unsigned characteristics; + std::vector chunks; +}; + // OutputSection represents a section in an output file. It's a // container of chunks. OutputSection and Chunk are 1:N relationship. // Chunks cannot belong to more than one OutputSections. The writer // creates multiple OutputSections and assign them unique, // non-overlapping file offsets and RVAs. class OutputSection { public: - OutputSection(llvm::StringRef N, uint32_t Chars) : Name(N) { - Header.Characteristics = Chars; + OutputSection(llvm::StringRef n, uint32_t chars) : name(n) { + header.Characteristics = chars; } - void addChunk(Chunk *C); - void insertChunkAtStart(Chunk *C); - void merge(OutputSection *Other); - void addPermissions(uint32_t C); - void setPermissions(uint32_t C); - uint64_t getRVA() { return Header.VirtualAddress; } - uint64_t getFileOff() { return Header.PointerToRawData; } - void writeHeaderTo(uint8_t *Buf); + void addChunk(Chunk *c); + void insertChunkAtStart(Chunk *c); + void merge(OutputSection *other); + void setPermissions(uint32_t c); + uint64_t getRVA() { return header.VirtualAddress; } + uint64_t getFileOff() { return header.PointerToRawData; } + void writeHeaderTo(uint8_t *buf); + void addContributingPartialSection(PartialSection *sec); // Returns the size of this section in an executable memory image. // This may be smaller than the raw size (the raw size is multiple // of disk sector size, so there may be padding at end), or may be // larger (if that's the case, the loader reserves spaces after end // of raw data). - uint64_t getVirtualSize() { return Header.VirtualSize; } + uint64_t getVirtualSize() { return header.VirtualSize; } // Returns the size of the section in the output file. - uint64_t getRawSize() { return Header.SizeOfRawData; } + uint64_t getRawSize() { return header.SizeOfRawData; } // Set offset into the string table storing this section name. // Used only when the name is longer than 8 bytes. - void setStringTableOff(uint32_t V) { StringTableOff = V; } + void setStringTableOff(uint32_t v) { stringTableOff = v; } // N.B. The section index is one based. - uint32_t SectionIndex = 0; + uint32_t sectionIndex = 0; - llvm::StringRef Name; - llvm::object::coff_section Header = {}; + llvm::StringRef name; + llvm::object::coff_section header = {}; - std::vector Chunks; - std::vector OrigChunks; + std::vector chunks; + std::vector origChunks; + std::vector contribSections; + private: - uint32_t StringTableOff = 0; + uint32_t stringTableOff = 0; }; -} -} +} // namespace coff +} // namespace lld #endif Index: vendor/lld/dist/Common/Args.cpp =================================================================== --- vendor/lld/dist/Common/Args.cpp (revision 351287) +++ vendor/lld/dist/Common/Args.cpp (revision 351288) @@ -1,73 +1,82 @@ //===- Args.cpp -----------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// 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" #include "llvm/Support/Path.h" using namespace llvm; using namespace lld; -int lld::args::getInteger(opt::InputArgList &Args, unsigned Key, int Default) { - auto *A = Args.getLastArg(Key); - if (!A) +// TODO(sbc): Remove this once CGOptLevel can be set completely based on bitcode +// function metadata. +CodeGenOpt::Level lld::args::getCGOptLevel(int optLevelLTO) { + if (optLevelLTO == 3) + return CodeGenOpt::Aggressive; + assert(optLevelLTO < 3); + return CodeGenOpt::Default; +} + +int64_t lld::args::getInteger(opt::InputArgList &args, unsigned key, + int64_t Default) { + auto *a = args.getLastArg(key); + if (!a) return Default; - int V; - if (to_integer(A->getValue(), V, 10)) - return V; + int64_t v; + if (to_integer(a->getValue(), v, 10)) + return v; - StringRef Spelling = Args.getArgString(A->getIndex()); - error(Spelling + ": number expected, but got '" + A->getValue() + "'"); + StringRef spelling = args.getArgString(a->getIndex()); + error(spelling + ": number expected, but got '" + a->getValue() + "'"); return 0; } -std::vector lld::args::getStrings(opt::InputArgList &Args, int Id) { - std::vector V; - for (auto *Arg : Args.filtered(Id)) - V.push_back(Arg->getValue()); - return V; +std::vector lld::args::getStrings(opt::InputArgList &args, int id) { + std::vector 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_reverse(Id)) { - std::pair 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; +uint64_t lld::args::getZOptionValue(opt::InputArgList &args, int id, + StringRef key, uint64_t Default) { + for (auto *arg : args.filtered_reverse(id)) { + std::pair 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 lld::args::getLines(MemoryBufferRef MB) { - SmallVector Arr; - MB.getBuffer().split(Arr, '\n'); +std::vector lld::args::getLines(MemoryBufferRef mb) { + SmallVector arr; + mb.getBuffer().split(arr, '\n'); - std::vector Ret; - for (StringRef S : Arr) { - S = S.trim(); - if (!S.empty() && S[0] != '#') - Ret.push_back(S); + std::vector ret; + for (StringRef s : arr) { + s = s.trim(); + if (!s.empty() && s[0] != '#') + ret.push_back(s); } - return Ret; + return ret; } -StringRef lld::args::getFilenameWithoutExe(StringRef Path) { - if (Path.endswith_lower(".exe")) - return sys::path::stem(Path); - return sys::path::filename(Path); +StringRef lld::args::getFilenameWithoutExe(StringRef path) { + if (path.endswith_lower(".exe")) + return sys::path::stem(path); + return sys::path::filename(path); } Index: vendor/lld/dist/Common/CMakeLists.txt =================================================================== --- vendor/lld/dist/Common/CMakeLists.txt (revision 351287) +++ vendor/lld/dist/Common/CMakeLists.txt (revision 351288) @@ -1,33 +1,60 @@ if(NOT LLD_BUILT_STANDALONE) set(tablegen_deps intrinsics_gen) endif() +find_first_existing_vc_file("${LLVM_MAIN_SRC_DIR}" llvm_vc) +find_first_existing_vc_file("${LLD_SOURCE_DIR}" lld_vc) + +set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/VCSVersion.inc") +set(generate_vcs_version_script "${LLVM_CMAKE_PATH}/GenerateVersionFromVCS.cmake") + +if(lld_vc) + set(lld_source_dir ${LLD_SOURCE_DIR}) +endif() + +add_custom_command(OUTPUT "${version_inc}" + DEPENDS "${lld_vc}" "${generate_vcs_version_script}" + COMMAND ${CMAKE_COMMAND} "-DNAMES=LLD" + "-DLLD_SOURCE_DIR=${LLD_SOURCE_DIR}" + "-DHEADER_FILE=${version_inc}" + -P "${generate_vcs_version_script}") + +# Mark the generated header as being generated. +set_source_files_properties("${version_inc}" + PROPERTIES GENERATED TRUE + HEADER_FILE_ONLY TRUE) + +set_property(SOURCE Version.cpp APPEND PROPERTY + COMPILE_DEFINITIONS "HAVE_VCS_VERSION_INC") + add_lld_library(lldCommon Args.cpp ErrorHandler.cpp + Filesystem.cpp Memory.cpp Reproduce.cpp Strings.cpp TargetOptionsCommandFlags.cpp Threads.cpp Timer.cpp + VCSVersion.inc 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} ) Index: vendor/lld/dist/Common/ErrorHandler.cpp =================================================================== --- vendor/lld/dist/Common/ErrorHandler.cpp (revision 351287) +++ vendor/lld/dist/Common/ErrorHandler.cpp (revision 351288) @@ -1,144 +1,178 @@ //===- ErrorHandler.cpp ---------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "lld/Common/ErrorHandler.h" #include "lld/Common/Threads.h" #include "llvm/ADT/Twine.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/raw_ostream.h" #include +#include #if !defined(_MSC_VER) && !defined(__MINGW32__) #include #endif using namespace llvm; using 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; +static std::mutex mu; // Prints "\n" or does nothing, depending on Msg contents of // the previous call of this function. -static void newline(raw_ostream *ErrorOS, 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; + static bool flag; - if (Flag) - *ErrorOS << "\n"; - Flag = StringRef(Msg.str()).contains('\n'); + if (flag) + *errorOS << "\n"; + flag = StringRef(msg.str()).contains('\n'); } ErrorHandler &lld::errorHandler() { - static ErrorHandler Handler; - return Handler; + static ErrorHandler handler; + return handler; } -void lld::exitLld(int Val) { +void lld::exitLld(int val) { // Delete any temporary file, while keeping the memory mapping open. - if (errorHandler().OutputBuffer) - errorHandler().OutputBuffer->discard(); + if (errorHandler().outputBuffer) + errorHandler().outputBuffer->discard(); // Dealloc/destroy ManagedStatic variables before calling // _exit(). In a non-LTO build, this is a nop. In an LTO // build allows us to get the output of -time-passes. llvm_shutdown(); outs().flush(); errs().flush(); - _exit(Val); + _exit(val); } -void lld::diagnosticHandler(const DiagnosticInfo &DI) { - SmallString<128> S; - raw_svector_ostream OS(S); - DiagnosticPrinterRawOStream DP(OS); - DI.print(DP); - switch (DI.getSeverity()) { +void lld::diagnosticHandler(const DiagnosticInfo &di) { + SmallString<128> s; + raw_svector_ostream os(s); + DiagnosticPrinterRawOStream dp(os); + di.print(dp); + switch (di.getSeverity()) { case DS_Error: - error(S); + error(s); break; case DS_Warning: - warn(S); + warn(s); break; case DS_Remark: case DS_Note: - message(S); + message(s); break; } } -void lld::checkError(Error E) { - handleAllErrors(std::move(E), - [&](ErrorInfoBase &EIB) { error(EIB.message()); }); +void lld::checkError(Error e) { + handleAllErrors(std::move(e), + [&](ErrorInfoBase &eib) { error(eib.message()); }); } -void ErrorHandler::print(StringRef S, raw_ostream::Colors C) { - *ErrorOS << LogName << ": "; - if (ColorDiagnostics) { - ErrorOS->changeColor(C, true); - *ErrorOS << S; - ErrorOS->resetColor(); +static std::string getLocation(std::string msg, std::string defaultMsg) { + static std::vector Regexes{ + std::regex(R"(^undefined symbol:.*\n>>> referenced by (\S+):(\d+)\n.*)"), + std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)"), + std::regex( + R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"), + std::regex( + R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+).*)"), + std::regex( + R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"), + std::regex( + R"(^undefined (internal|hidden|protected) symbol: .*\n>>> referenced by (\S+):(\d+)\n.*)"), + std::regex(R"((\S+):(\d+): unclosed quote)"), + }; + + std::smatch Match; + for (std::regex &Re : Regexes) { + if (std::regex_search(msg, Match, Re)) { + return Match.size() > 2 ? Match.str(1) + "(" + Match.str(2) + ")" + : Match.str(1); + } + } + return defaultMsg; +} + +void ErrorHandler::printHeader(StringRef s, raw_ostream::Colors c, + const Twine &msg) { + + if (vsDiagnostics) { + // A Visual Studio-style error message starts with an error location. + // If a location cannot be extracted then we default to LogName. + *errorOS << getLocation(msg.str(), logName) << ": "; } else { - *ErrorOS << S; + *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 Lock(Mu); - *ErrorOS << LogName << ": " << Msg << "\n"; +void ErrorHandler::log(const Twine &msg) { + if (verbose) { + std::lock_guard lock(mu); + *errorOS << logName << ": " << msg << "\n"; } } -void ErrorHandler::message(const Twine &Msg) { - std::lock_guard Lock(Mu); - outs() << Msg << "\n"; +void ErrorHandler::message(const Twine &msg) { + std::lock_guard lock(mu); + outs() << msg << "\n"; outs().flush(); } -void ErrorHandler::warn(const Twine &Msg) { - if (FatalWarnings) { - error(Msg); +void ErrorHandler::warn(const Twine &msg) { + if (fatalWarnings) { + error(msg); return; } - std::lock_guard Lock(Mu); - newline(ErrorOS, Msg); - print("warning: ", raw_ostream::MAGENTA); - *ErrorOS << Msg << "\n"; + std::lock_guard lock(mu); + newline(errorOS, msg); + printHeader("warning: ", raw_ostream::MAGENTA, msg); + *errorOS << msg << "\n"; } -void ErrorHandler::error(const Twine &Msg) { - std::lock_guard Lock(Mu); - newline(ErrorOS, Msg); +void ErrorHandler::error(const Twine &msg) { + std::lock_guard 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) + if (errorLimit == 0 || errorCount < errorLimit) { + printHeader("error: ", raw_ostream::RED, msg); + *errorOS << msg << "\n"; + } else if (errorCount == errorLimit) { + printHeader("error: ", raw_ostream::RED, msg); + *errorOS << errorLimitExceededMsg << "\n"; + if (exitEarly) exitLld(1); } - ++ErrorCount; + ++errorCount; } -void ErrorHandler::fatal(const Twine &Msg) { - error(Msg); +void ErrorHandler::fatal(const Twine &msg) { + error(msg); exitLld(1); } Index: vendor/lld/dist/Common/Filesystem.cpp =================================================================== --- vendor/lld/dist/Common/Filesystem.cpp (nonexistent) +++ vendor/lld/dist/Common/Filesystem.cpp (revision 351288) @@ -0,0 +1,99 @@ +//===- Filesystem.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains a few utility functions to handle files. +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/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 +#endif +#include + +using namespace llvm; +using namespace lld; + +// Removes a given file asynchronously. This is a performance hack, +// so remove this when operating systems are improved. +// +// On Linux (and probably on other Unix-like systems), unlink(2) is a +// noticeably slow system call. As of 2016, unlink takes 250 +// milliseconds to remove a 1 GB file on ext4 filesystem on my machine. +// +// To create a new result file, we first remove existing file. So, if +// you repeatedly link a 1 GB program in a regular compile-link-debug +// cycle, every cycle wastes 250 milliseconds only to remove a file. +// Since LLD can link a 1 GB binary in about 5 seconds, that waste +// actually counts. +// +// This function spawns a background thread to remove the file. +// The calling thread returns almost immediately. +void lld::unlinkAsync(StringRef path) { +// Removing a file is async on windows. +#if defined(_WIN32) + sys::fs::remove(path); +#else + if (!threadsEnabled || !sys::fs::exists(path) || + !sys::fs::is_regular_file(path)) + 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); + + if (ec) + return; + + // close and therefore remove TempPath in background. + std::mutex m; + std::condition_variable cv; + bool started = false; + std::thread([&, fd] { + { + std::lock_guard l(m); + started = true; + cv.notify_all(); + } + ::close(fd); + }).detach(); + + // GLIBC 2.26 and earlier have race condition that crashes an entire process + // if the main thread calls exit(2) while other thread is starting up. + std::unique_lock l(m); + cv.wait(l, [&] { return started; }); +#endif +} + +// Simulate file creation to see if Path is writable. +// +// Determining whether a file is writable or not is amazingly hard, +// and after all the only reliable way of doing that is to actually +// create a file. But we don't want to do that in this function +// because LLD shouldn't update any file if it will end in a failure. +// We also don't want to reimplement heuristics to determine if a +// file is writable. So we'll let FileOutputBuffer do the work. +// +// FileOutputBuffer doesn't touch a desitnation file until commit() +// is called. We use that class without calling commit() to predict +// if the given file is writable. +std::error_code lld::tryCreateFile(StringRef path) { + if (path.empty()) + return std::error_code(); + if (path == "-") + return std::error_code(); + return errorToErrorCode(FileOutputBuffer::create(path, 1).takeError()); +} Index: vendor/lld/dist/Common/Memory.cpp =================================================================== --- vendor/lld/dist/Common/Memory.cpp (revision 351287) +++ vendor/lld/dist/Common/Memory.cpp (revision 351288) @@ -1,23 +1,22 @@ //===- Memory.cpp ---------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// 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 lld::SpecificAllocBase::Instances; +BumpPtrAllocator lld::bAlloc; +StringSaver lld::saver{bAlloc}; +std::vector lld::SpecificAllocBase::instances; void lld::freeArena() { - for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances) - Alloc->reset(); - BAlloc.Reset(); + for (SpecificAllocBase *alloc : SpecificAllocBase::instances) + alloc->reset(); + bAlloc.Reset(); } Index: vendor/lld/dist/Common/Reproduce.cpp =================================================================== --- vendor/lld/dist/Common/Reproduce.cpp (revision 351287) +++ vendor/lld/dist/Common/Reproduce.cpp (revision 351288) @@ -1,66 +1,61 @@ //===- Reproduce.cpp - Utilities for creating reproducers -----------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "lld/Common/Reproduce.h" #include "llvm/Option/Arg.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" using namespace lld; using namespace llvm; using namespace llvm::sys; // Makes a given pathname an absolute path first, and then remove // beginning /. For example, "../foo.o" is converted to "home/john/foo.o", // assuming that the current directory is "/home/john/bar". // Returned string is a forward slash separated path even on Windows to avoid // a mess with backslash-as-escape and backslash-as-path-separator. -std::string lld::relativeToRoot(StringRef Path) { - SmallString<128> Abs = Path; - if (fs::make_absolute(Abs)) - return Path; - path::remove_dots(Abs, /*remove_dot_dot=*/true); +std::string lld::relativeToRoot(StringRef path) { + SmallString<128> abs = path; + if (fs::make_absolute(abs)) + return path; + path::remove_dots(abs, /*remove_dot_dot=*/true); // This is Windows specific. root_name() returns a drive letter // (e.g. "c:") or a UNC name (//net). We want to keep it as part // of the result. - SmallString<128> Res; - StringRef Root = path::root_name(Abs); - if (Root.endswith(":")) - Res = Root.drop_back(); - else if (Root.startswith("//")) - Res = Root.substr(2); + SmallString<128> res; + StringRef root = path::root_name(abs); + if (root.endswith(":")) + res = root.drop_back(); + else if (root.startswith("//")) + res = root.substr(2); - path::append(Res, path::relative_path(Abs)); - return path::convert_to_slash(Res); + path::append(res, path::relative_path(abs)); + return path::convert_to_slash(res); } // Quote a given string if it contains a space character. -std::string lld::quote(StringRef S) { - if (S.contains(' ')) - return ("\"" + S + "\"").str(); - return S; +std::string lld::quote(StringRef s) { + if (s.contains(' ')) + return ("\"" + s + "\"").str(); + return s; } -std::string lld::rewritePath(StringRef S) { - if (fs::exists(S)) - return relativeToRoot(S); - return S; -} - -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) - return K + V; - return K + " " + V; +// Converts an Arg to a string representation suitable for a response file. +// To show an Arg in a diagnostic, use Arg::getAsString() instead. +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) + return k + v; + return k + " " + v; } Index: vendor/lld/dist/Common/Strings.cpp =================================================================== --- vendor/lld/dist/Common/Strings.cpp (revision 351287) +++ vendor/lld/dist/Common/Strings.cpp (revision 351288) @@ -1,104 +1,103 @@ //===- Strings.cpp -------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "lld/Common/Strings.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/LLVM.h" #include "llvm/Demangle/Demangle.h" #include "llvm/Support/GlobPattern.h" #include #include #include using namespace llvm; using namespace lld; // Returns the demangled C++ symbol name for Name. -Optional lld::demangleItanium(StringRef Name) { +Optional 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")) + if (!name.startswith("_Z")) return None; - char *Buf = itaniumDemangle(Name.str().c_str(), nullptr, nullptr, nullptr); - if (!Buf) + char *buf = itaniumDemangle(name.str().c_str(), nullptr, nullptr, nullptr); + if (!buf) return None; - std::string S(Buf); - free(Buf); - return S; + std::string s(buf); + free(buf); + return s; } -Optional lld::demangleMSVC(StringRef Name) { - std::string Prefix; - if (Name.consume_front("__imp_")) - Prefix = "__declspec(dllimport) "; +Optional lld::demangleMSVC(StringRef name) { + std::string prefix; + if (name.consume_front("__imp_")) + prefix = "__declspec(dllimport) "; // Demangle only C++ names. - if (!Name.startswith("?")) + if (!name.startswith("?")) return None; - char *Buf = microsoftDemangle(Name.str().c_str(), nullptr, nullptr, nullptr); - if (!Buf) + char *buf = microsoftDemangle(name.str().c_str(), nullptr, nullptr, nullptr); + if (!buf) return None; - std::string S(Buf); - free(Buf); - return Prefix + S; + std::string s(buf); + free(buf); + return prefix + s; } -StringMatcher::StringMatcher(ArrayRef Pat) { - for (StringRef S : Pat) { - Expected Pat = GlobPattern::create(S); - if (!Pat) - error(toString(Pat.takeError())); +StringMatcher::StringMatcher(ArrayRef pat) { + for (StringRef s : pat) { + Expected pat = GlobPattern::create(s); + if (!pat) + error(toString(pat.takeError())); else - Patterns.push_back(*Pat); + patterns.push_back(*pat); } } -bool StringMatcher::match(StringRef S) const { - for (const GlobPattern &Pat : Patterns) - if (Pat.match(S)) +bool StringMatcher::match(StringRef s) const { + for (const GlobPattern &pat : patterns) + if (pat.match(s)) return true; return false; } // Converts a hex string (e.g. "deadbeef") to a vector. -std::vector lld::parseHex(StringRef S) { - std::vector Hex; - while (!S.empty()) { - StringRef B = S.substr(0, 2); - S = S.substr(2); - uint8_t H; - if (!to_integer(B, H, 16)) { - error("not a hexadecimal value: " + B); +std::vector lld::parseHex(StringRef s) { + std::vector hex; + while (!s.empty()) { + StringRef b = s.substr(0, 2); + s = s.substr(2); + uint8_t h; + if (!to_integer(b, h, 16)) { + error("not a hexadecimal value: " + b); return {}; } - Hex.push_back(H); + hex.push_back(h); } - return Hex; + return hex; } // Returns true if S is valid as a C language identifier. -bool lld::isValidCIdentifier(StringRef S) { - return !S.empty() && (isAlpha(S[0]) || S[0] == '_') && - std::all_of(S.begin() + 1, S.end(), - [](char C) { return C == '_' || isAlnum(C); }); +bool lld::isValidCIdentifier(StringRef s) { + return !s.empty() && (isAlpha(s[0]) || s[0] == '_') && + std::all_of(s.begin() + 1, s.end(), + [](char c) { return c == '_' || isAlnum(c); }); } // Write the contents of the a buffer to a file -void lld::saveBuffer(StringRef Buffer, const Twine &Path) { - std::error_code EC; - raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None); - if (EC) - error("cannot create " + Path + ": " + EC.message()); - OS << Buffer; +void lld::saveBuffer(StringRef buffer, const Twine &path) { + std::error_code ec; + raw_fd_ostream os(path.str(), ec, sys::fs::OpenFlags::F_None); + if (ec) + error("cannot create " + path + ": " + ec.message()); + os << buffer; } Index: vendor/lld/dist/Common/TargetOptionsCommandFlags.cpp =================================================================== --- vendor/lld/dist/Common/TargetOptionsCommandFlags.cpp (revision 351287) +++ vendor/lld/dist/Common/TargetOptionsCommandFlags.cpp (revision 351288) @@ -1,35 +1,35 @@ //===-- TargetOptionsCommandFlags.cpp ---------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// // // This file exists as a place for global variables defined in LLVM's // CodeGen/CommandFlags.inc. By putting the resulting object file in // an archive and linking with it, the definitions will automatically be // included when needed and skipped when already present. // //===----------------------------------------------------------------------===// #include "lld/Common/TargetOptionsCommandFlags.h" #include "llvm/CodeGen/CommandFlags.inc" #include "llvm/Target/TargetOptions.h" // Define an externally visible version of -// InitTargetOptionsFromCodeGenFlags, so that its functionality can be +// initTargetOptionsFromCodeGenFlags, so that its functionality can be // used without having to include llvm/CodeGen/CommandFlags.inc, which // would lead to multiple definitions of the command line flags. -llvm::TargetOptions lld::InitTargetOptionsFromCodeGenFlags() { +llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() { return ::InitTargetOptionsFromCodeGenFlags(); } -llvm::Optional lld::GetCodeModelFromCMModel() { +llvm::Optional lld::getCodeModelFromCMModel() { return getCodeModel(); } -std::string lld::GetCPUStr() { return ::getCPUStr(); } -std::vector lld::GetMAttrs() { return ::MAttrs; } +std::string lld::getCPUStr() { return ::getCPUStr(); } + +std::vector lld::getMAttrs() { return ::MAttrs; } Index: vendor/lld/dist/Common/Threads.cpp =================================================================== --- vendor/lld/dist/Common/Threads.cpp (revision 351287) +++ vendor/lld/dist/Common/Threads.cpp (revision 351288) @@ -1,12 +1,11 @@ //===- Threads.cpp --------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// 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; +bool lld::threadsEnabled = true; Index: vendor/lld/dist/Common/Timer.cpp =================================================================== --- vendor/lld/dist/Common/Timer.cpp (revision 351287) +++ vendor/lld/dist/Common/Timer.cpp (revision 351288) @@ -1,80 +1,79 @@ //===- Timer.cpp ----------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "lld/Common/Timer.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Support/Format.h" using namespace lld; using namespace llvm; -ScopedTimer::ScopedTimer(Timer &T) : T(&T) { T.start(); } +ScopedTimer::ScopedTimer(Timer &t) : t(&t) { t.start(); } void ScopedTimer::stop() { - if (!T) + if (!t) return; - T->stop(); - T = nullptr; + t->stop(); + t = nullptr; } ScopedTimer::~ScopedTimer() { stop(); } -Timer::Timer(llvm::StringRef Name) : Name(Name), Parent(nullptr) {} -Timer::Timer(llvm::StringRef Name, Timer &Parent) - : Name(Name), Parent(&Parent) {} +Timer::Timer(llvm::StringRef name) : name(name), parent(nullptr) {} +Timer::Timer(llvm::StringRef name, Timer &parent) + : name(name), parent(&parent) {} void Timer::start() { - if (Parent && Total.count() == 0) - Parent->Children.push_back(this); - StartTime = std::chrono::high_resolution_clock::now(); + if (parent && total.count() == 0) + parent->children.push_back(this); + startTime = std::chrono::high_resolution_clock::now(); } void Timer::stop() { - Total += (std::chrono::high_resolution_clock::now() - StartTime); + total += (std::chrono::high_resolution_clock::now() - startTime); } Timer &Timer::root() { - static Timer RootTimer("Total Link Time"); - return RootTimer; + static Timer rootTimer("Total Link Time"); + return rootTimer; } void Timer::print() { - double TotalDuration = static_cast(root().millis()); + double totalDuration = static_cast(root().millis()); // We want to print the grand total under all the intermediate phases, so we // print all children first, then print the total under that. - for (const auto &Child : Children) - Child->print(1, TotalDuration); + for (const auto &child : children) + child->print(1, totalDuration); message(std::string(49, '-')); root().print(0, root().millis(), false); } double Timer::millis() const { return std::chrono::duration_cast>( - Total) + total) .count(); } -void Timer::print(int Depth, double TotalDuration, bool Recurse) const { - double P = 100.0 * millis() / TotalDuration; +void Timer::print(int depth, double totalDuration, bool recurse) const { + double p = 100.0 * millis() / totalDuration; - SmallString<32> Str; - llvm::raw_svector_ostream Stream(Str); - std::string S = std::string(Depth * 2, ' ') + Name + std::string(":"); - Stream << format("%-30s%5d ms (%5.1f%%)", S.c_str(), (int)millis(), P); + SmallString<32> str; + llvm::raw_svector_ostream stream(str); + std::string s = std::string(depth * 2, ' ') + name + std::string(":"); + stream << format("%-30s%5d ms (%5.1f%%)", s.c_str(), (int)millis(), p); - message(Str); + message(str); - if (Recurse) { - for (const auto &Child : Children) - Child->print(Depth + 1, TotalDuration); + if (recurse) { + for (const auto &child : children) + child->print(depth + 1, totalDuration); } } Index: vendor/lld/dist/Common/Version.cpp =================================================================== --- vendor/lld/dist/Common/Version.cpp (revision 351287) +++ vendor/lld/dist/Common/Version.cpp (revision 351288) @@ -1,43 +1,27 @@ //===- lib/Common/Version.cpp - LLD Version Number ---------------*- C++-=====// // -// The LLVM Compiler Infrastructure +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// // // This file defines several version-related utility functions for LLD. // //===----------------------------------------------------------------------===// #include "lld/Common/Version.h" -using namespace llvm; +#ifdef HAVE_VCS_VERSION_INC +#include "VCSVersion.inc" +#endif -// Returns an SVN repository path, which is usually "trunk". -static std::string getRepositoryPath() { - StringRef S = LLD_REPOSITORY_STRING; - size_t Pos = S.find("lld/"); - if (Pos != StringRef::npos) - return S.substr(Pos + 4); - return S; -} - -// Returns an SVN repository name, e.g., " (trunk 284614)" -// or an empty string if no repository info is available. -static std::string getRepository() { - std::string Repo = getRepositoryPath(); - std::string Rev = LLD_REVISION_STRING; - - if (Repo.empty() && Rev.empty()) - return ""; - if (!Repo.empty() && !Rev.empty()) - return " (" + Repo + " " + Rev + ")"; - return " (" + Repo + Rev + ")"; -} - -// Returns a version string, e.g., "LLD 4.0 (lld/trunk 284614)". +// Returns a version string, e.g.: +// lld 9.0.0 (https://github.com/llvm/llvm-project.git 9efdd7ac5e914d3c9fa1ef) std::string lld::getLLDVersion() { - return "LLD " + std::string(LLD_VERSION_STRING) + getRepository(); +#if defined(LLD_REPOSITORY) && defined(LLD_REVISION) + return "LLD " LLD_VERSION_STRING " (" LLD_REPOSITORY " " LLD_REVISION ")"; +#else + return "LLD " LLD_VERSION_STRING; +#endif } Index: vendor/lld/dist/ELF/Filesystem.cpp =================================================================== --- vendor/lld/dist/ELF/Filesystem.cpp (revision 351287) +++ vendor/lld/dist/ELF/Filesystem.cpp (nonexistent) @@ -1,86 +0,0 @@ -//===- Filesystem.cpp -----------------------------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file contains a few utility functions to handle files. -// -//===----------------------------------------------------------------------===// - -#include "Filesystem.h" -#include "Config.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 -#endif -#include - -using namespace llvm; - -using namespace lld; -using namespace lld::elf; - -// Removes a given file asynchronously. This is a performance hack, -// so remove this when operating systems are improved. -// -// On Linux (and probably on other Unix-like systems), unlink(2) is a -// noticeably slow system call. As of 2016, unlink takes 250 -// milliseconds to remove a 1 GB file on ext4 filesystem on my machine. -// -// To create a new result file, we first remove existing file. So, if -// you repeatedly link a 1 GB program in a regular compile-link-debug -// cycle, every cycle wastes 250 milliseconds only to remove a file. -// Since LLD can link a 1 GB binary in about 5 seconds, that waste -// actually counts. -// -// This function spawns a background thread to remove the file. -// The calling thread returns almost immediately. -void elf::unlinkAsync(StringRef Path) { -// Removing a file is async on windows. -#if defined(_WIN32) - sys::fs::remove(Path); -#else - if (!ThreadsEnabled || !sys::fs::exists(Path) || - !sys::fs::is_regular_file(Path)) - 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); - - // close and therefore remove TempPath in background. - if (!EC) - std::thread([=] { ::close(FD); }).detach(); -#endif -} - -// Simulate file creation to see if Path is writable. -// -// Determining whether a file is writable or not is amazingly hard, -// and after all the only reliable way of doing that is to actually -// create a file. But we don't want to do that in this function -// because LLD shouldn't update any file if it will end in a failure. -// We also don't want to reimplement heuristics to determine if a -// file is writable. So we'll let FileOutputBuffer do the work. -// -// FileOutputBuffer doesn't touch a desitnation file until commit() -// is called. We use that class without calling commit() to predict -// if the given file is writable. -std::error_code elf::tryCreateFile(StringRef Path) { - if (Path.empty()) - return std::error_code(); - if (Path == "-") - return std::error_code(); - return errorToErrorCode(FileOutputBuffer::create(Path, 1).takeError()); -} Property changes on: vendor/lld/dist/ELF/Filesystem.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/lld/dist/ELF/Bits.h =================================================================== --- vendor/lld/dist/ELF/Bits.h (revision 351287) +++ vendor/lld/dist/ELF/Bits.h (nonexistent) @@ -1,35 +0,0 @@ -//===- 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 Property changes on: vendor/lld/dist/ELF/Bits.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/lld/dist/ELF/Filesystem.h =================================================================== --- vendor/lld/dist/ELF/Filesystem.h (revision 351287) +++ vendor/lld/dist/ELF/Filesystem.h (nonexistent) @@ -1,23 +0,0 @@ -//===- Filesystem.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_FILESYSTEM_H -#define LLD_ELF_FILESYSTEM_H - -#include "lld/Common/LLVM.h" -#include - -namespace lld { -namespace elf { -void unlinkAsync(StringRef Path); -std::error_code tryCreateFile(StringRef Path); -} // namespace elf -} // namespace lld - -#endif Property changes on: vendor/lld/dist/ELF/Filesystem.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/lld/dist/ELF/AArch64ErrataFix.cpp =================================================================== --- vendor/lld/dist/ELF/AArch64ErrataFix.cpp (revision 351287) +++ vendor/lld/dist/ELF/AArch64ErrataFix.cpp (revision 351288) @@ -1,652 +1,651 @@ //===- AArch64ErrataFix.cpp -----------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// 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 "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "llvm/Support/Endian.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::support; 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; +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; +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 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); +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); +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 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); +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 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); +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 isLoadStoreExclusive(uint32_t instr) { + return (instr & 0x3f000000) == 0x08000000; } -static bool isLoadExclusive(uint32_t Instr) { - return (Instr & 0x3f400000) == 0x08400000; +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; +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; +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; +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; +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 isSTPPre(uint32_t instr) { + return (instr & 0x3bc00000) == 0x29800000; } -static bool isSTP(uint32_t Instr) { - return isSTPPost(Instr) || isSTPOffset(Instr) || isSTPPre(Instr); +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; +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; +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; +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; +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; +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; +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); } +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; } +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 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); +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)) +static bool isV8NonStructureLoad(uint32_t instr) { + if (isLoadExclusive(instr)) return true; - if (isLoadLiteral(Instr)) + if (isLoadLiteral(instr)) return true; - else if (isV8SingleRegisterNonStructureLoadStore(Instr)) { + 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; + 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 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); +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); +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)) +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; + 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->getVA(0); +static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off, + uint64_t limit) { + uint64_t isecAddr = isec->getVA(0); // Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8. - uint64_t InitialPageOff = (ISAddr + Off) & 0xfff; - if (InitialPageOff < 0xff8) - Off += 0xff8 - InitialPageOff; + uint64_t initialPageOff = (isecAddr + off) & 0xfff; + if (initialPageOff < 0xff8) + off += 0xff8 - initialPageOff; - bool OptionalAllowed = Limit - Off > 12; - if (Off >= Limit || Limit - Off < 12) { + bool optionalAllowed = limit - off > 12; + if (off >= limit || limit - off < 12) { // Need at least 3 4-byte sized instructions to trigger erratum. - Off = Limit; + off = limit; return 0; } - uint64_t PatchOff = 0; - const uint8_t *Buf = IS->data().begin(); - const ulittle32_t *InstBuf = reinterpret_cast(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; + uint64_t patchOff = 0; + const uint8_t *buf = isec->data().begin(); + const ulittle32_t *instBuf = reinterpret_cast(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; + if (((isecAddr + off) & 0xfff) == 0xff8) + off += 4; else - Off += 0xffc; - return PatchOff; + off += 0xffc; + return patchOff; } class lld::elf::Patch843419Section : public SyntheticSection { public: - Patch843419Section(InputSection *P, uint64_t Off); + Patch843419Section(InputSection *p, uint64_t off); - void writeTo(uint8_t *Buf) override; + 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; + const InputSection *patchee; // The offset of the instruction in the Patchee section we are patching. - uint64_t PatcheeOffset; + uint64_t patcheeOffset; // A label for the start of the Patch that we can use as a relocation target. - Symbol *PatchSym; + Symbol *patchSym; }; -lld::elf::Patch843419Section::Patch843419Section(InputSection *P, uint64_t Off) +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, + 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); + addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this); } uint64_t lld::elf::Patch843419Section::getLDSTAddr() const { - return Patchee->getVA(PatcheeOffset); + return patchee->getVA(patcheeOffset); } -void lld::elf::Patch843419Section::writeTo(uint8_t *Buf) { +void lld::elf::Patch843419Section::writeTo(uint8_t *buf) { // Copy the instruction that we will be replacing with a branch in the // Patchee Section. - write32le(Buf, read32le(Patchee->data().begin() + PatcheeOffset)); + write32le(buf, read32le(patchee->data().begin() + patcheeOffset)); // Apply any relocation transferred from the original PatcheeSection. - // For a SyntheticSection Buf already has OutSecOff added, but relocateAlloc - // also adds OutSecOff so we need to subtract to avoid double counting. - this->relocateAlloc(Buf - OutSecOff, Buf - OutSecOff + getSize()); + // 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); + 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. + // 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 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."); + 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>(File); - for (Symbol *B : F->getLocalSymbols()) { - auto *Def = dyn_cast(B); - if (!Def) + for (InputFile *file : objectFiles) { + auto *f = cast>(file); + for (Symbol *b : f->getLocalSymbols()) { + auto *def = dyn_cast(b); + if (!def) continue; - if (!IsCodeMapSymbol(Def) && !IsDataMapSymbol(Def)) + if (!isCodeMapSymbol(def) && !isDataMapSymbol(def)) continue; - if (auto *Sec = dyn_cast_or_null(Def->Section)) - if (Sec->Flags & SHF_EXECINSTR) - SectionMap[Sec].push_back(Def); + if (auto *sec = dyn_cast_or_null(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 &MapSyms = KV.second; - if (MapSyms.size() <= 1) + for (auto &kv : sectionMap) { + std::vector &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)); + llvm::stable_sort(mapSyms, [](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()); + mapSyms.end()); } - Initialized = true; + 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 &Patches) { - uint64_t ISLimit; - uint64_t PrevISLimit = ISD.Sections.front()->OutSecOff; - uint64_t PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing(); - uint64_t OutSecAddr = ISD.Sections.front()->getParent()->Addr; + InputSectionDescription &isd, std::vector &patches) { + uint64_t isecLimit; + uint64_t prevIsecLimit = isd.sections.front()->outSecOff; + uint64_t patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing(); + uint64_t outSecAddr = isd.sections.front()->getParent()->addr; - // Set the OutSecOff of patches to the place where we want to insert them. + // 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() - OutSecAddr >= PrevISLimit) + auto patchIt = patches.begin(); + auto patchEnd = patches.end(); + for (const InputSection *isec : isd.sections) { + isecLimit = isec->outSecOff + isec->getSize(); + if (isecLimit > patchUpperBound) { + while (patchIt != patchEnd) { + if ((*patchIt)->getLDSTAddr() - outSecAddr >= prevIsecLimit) break; - (*PatchIt)->OutSecOff = PrevISLimit; - ++PatchIt; + (*patchIt)->outSecOff = prevIsecLimit; + ++patchIt; } - PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing(); + patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing(); } - PrevISLimit = ISLimit; + prevIsecLimit = isecLimit; } - for (; PatchIt != PatchEnd; ++PatchIt) { - (*PatchIt)->OutSecOff = ISLimit; + for (; patchIt != patchEnd; ++patchIt) { + (*patchIt)->outSecOff = isecLimit; } - // merge all patch sections. We use the OutSecOff assigned above to + // 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 Tmp; - Tmp.reserve(ISD.Sections.size() + Patches.size()); - auto MergeCmp = [](const InputSection *A, const InputSection *B) { - if (A->OutSecOff < B->OutSecOff) + // assignAddresses() will recalculate all the outSecOff values. + std::vector 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(A) && - !isa(B)) + if (a->outSecOff == b->outSecOff && isa(a) && + !isa(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); + 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 +// 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 &Patches) { +static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset, + InputSection *isec, + std::vector &patches) { // There may be a relocation at the same offset that we are patching. There // are four cases that we need to consider. // Case 1: R_AARCH64_JUMP26 branch relocation. We have already patched this // instance of the erratum on a previous patch and altered the relocation. We // have nothing more to do. // Case 2: A TLS Relaxation R_RELAX_TLS_IE_TO_LE. In this case the ADRP that // we read will be transformed into a MOVZ later so we actually don't match // the sequence and have nothing more to do. // Case 3: A load/store register (unsigned immediate) class relocation. There // are two of these R_AARCH_LD64_ABS_LO12_NC and R_AARCH_LD64_GOT_LO12_NC and // they are both absolute. We need to add the same relocation to the patch, // and replace the relocation with a R_AARCH_JUMP26 branch relocation. // Case 4: No relocation. We must create a new R_AARCH64_JUMP26 branch // relocation at the offset. - auto RelIt = std::find_if( - IS->Relocations.begin(), IS->Relocations.end(), - [=](const Relocation &R) { return R.Offset == PatcheeOffset; }); - if (RelIt != IS->Relocations.end() && - (RelIt->Type == R_AARCH64_JUMP26 || RelIt->Expr == R_RELAX_TLS_IE_TO_LE)) + auto relIt = llvm::find_if(isec->relocations, [=](const Relocation &r) { + return r.offset == patcheeOffset; + }); + if (relIt != isec->relocations.end() && + (relIt->type == R_AARCH64_JUMP26 || relIt->expr == R_RELAX_TLS_IE_TO_LE)) return; log("detected cortex-a53-843419 erratum sequence starting at " + - utohexstr(AdrpAddr) + " in unpatched output."); + utohexstr(adrpAddr) + " in unpatched output."); - auto *PS = make(IS, PatcheeOffset); - Patches.push_back(PS); + auto *ps = make(isec, patcheeOffset); + patches.push_back(ps); - auto MakeRelToPatch = [](uint64_t Offset, Symbol *PatchSym) { - return Relocation{R_PC, R_AARCH64_JUMP26, Offset, 0, PatchSym}; + 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); + if (relIt != isec->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)); + isec->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 AArch64Err843419Patcher::patchInputSectionDescription( - InputSectionDescription &ISD) { - std::vector Patches; - for (InputSection *IS : ISD.Sections) { + InputSectionDescription &isd) { + std::vector patches; + for (InputSection *isec : isd.sections) { // LLD doesn't use the erratum sequence in SyntheticSections. - if (isa(IS)) + if (isa(isec)) continue; - // Use SectionMap to make sure we only scan code and not inline data. + // 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, + // scan is therefore [codeSym->value, dataSym->value) or [codeSym->value, // section size). - std::vector &MapSyms = SectionMap[IS]; + std::vector &mapSyms = sectionMap[isec]; - auto CodeSym = llvm::find_if(MapSyms, [&](const Defined *MS) { - return MS->getName().startswith("$x"); + 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 (codeSym != mapSyms.end()) { + auto dataSym = std::next(codeSym); + uint64_t off = (*codeSym)->value; + uint64_t limit = + (dataSym == mapSyms.end()) ? isec->data().size() : (*dataSym)->value; - while (Off < Limit) { - uint64_t StartAddr = IS->getVA(Off); - if (uint64_t PatcheeOffset = scanCortexA53Errata843419(IS, Off, Limit)) - implementPatch(StartAddr, PatcheeOffset, IS, Patches); + while (off < limit) { + uint64_t startAddr = isec->getVA(off); + if (uint64_t patcheeOffset = scanCortexA53Errata843419(isec, off, limit)) + implementPatch(startAddr, patcheeOffset, isec, patches); } - if (DataSym == MapSyms.end()) + if (dataSym == mapSyms.end()) break; - CodeSym = std::next(DataSym); + codeSym = std::next(dataSym); } } - return Patches; + 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) + if (initialized == false) init(); - bool AddressesChanged = false; - for (OutputSection *OS : OutputSections) { - if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR)) + 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(BC)) { - std::vector Patches = - patchInputSectionDescription(*ISD); - if (!Patches.empty()) { - insertPatches(*ISD, Patches); - AddressesChanged = true; + for (BaseCommand *bc : os->sectionCommands) + if (auto *isd = dyn_cast(bc)) { + std::vector patches = + patchInputSectionDescription(*isd); + if (!patches.empty()) { + insertPatches(*isd, patches); + addressesChanged = true; } } } - return AddressesChanged; + return addressesChanged; } Index: vendor/lld/dist/ELF/AArch64ErrataFix.h =================================================================== --- vendor/lld/dist/ELF/AArch64ErrataFix.h (revision 351287) +++ vendor/lld/dist/ELF/AArch64ErrataFix.h (revision 351288) @@ -1,51 +1,50 @@ //===- AArch64ErrataFix.h ---------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// 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 #include 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 - patchInputSectionDescription(InputSectionDescription &ISD); + patchInputSectionDescription(InputSectionDescription &isd); - void insertPatches(InputSectionDescription &ISD, - std::vector &Patches); + void insertPatches(InputSectionDescription &isd, + std::vector &patches); void init(); - // A cache of the mapping symbols defined by the InputSecion sorted in order + // A cache of the mapping symbols defined by the InputSection sorted in order // of ascending value with redundant symbols removed. These describe // the ranges of code and data in an executable InputSection. - std::map> SectionMap; + std::map> sectionMap; - bool Initialized = false; + bool initialized = false; }; } // namespace elf } // namespace lld #endif Index: vendor/lld/dist/ELF/Arch/AArch64.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/AArch64.cpp (revision 351287) +++ vendor/lld/dist/ELF/Arch/AArch64.cpp (revision 351288) @@ -1,440 +1,590 @@ //===- AArch64.cpp --------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #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" using namespace llvm; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; // Page(Expr) is the page address of the expression Expr, defined // as (Expr & ~0xFFF). (This applies even if the machine page size // supported by the platform has a different value.) -uint64_t elf::getAArch64Page(uint64_t Expr) { - return Expr & ~static_cast(0xFFF); +uint64_t elf::getAArch64Page(uint64_t expr) { + return expr & ~static_cast(0xFFF); } namespace { -class AArch64 final : public TargetInfo { +class AArch64 : public TargetInfo { public: AArch64(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) 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, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) 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, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; uint32_t getThunkSectionSpacing() const override; - bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; - bool usesOnlyLowPageBits(RelType Type) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) 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; + 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, 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 AArch64::AArch64() { - CopyRel = R_AARCH64_COPY; - RelativeRel = R_AARCH64_RELATIVE; - IRelativeRel = R_AARCH64_IRELATIVE; - GotRel = R_AARCH64_GLOB_DAT; - NoneRel = R_AARCH64_NONE; - PltRel = R_AARCH64_JUMP_SLOT; - TlsDescRel = R_AARCH64_TLSDESC; - TlsGotRel = R_AARCH64_TLS_TPREL64; - GotEntrySize = 8; - GotPltEntrySize = 8; - PltEntrySize = 16; - PltHeaderSize = 32; - DefaultMaxPageSize = 65536; + copyRel = R_AARCH64_COPY; + relativeRel = R_AARCH64_RELATIVE; + iRelativeRel = R_AARCH64_IRELATIVE; + gotRel = R_AARCH64_GLOB_DAT; + noneRel = R_AARCH64_NONE; + pltRel = R_AARCH64_JUMP_SLOT; + symbolicRel = R_AARCH64_ABS64; + tlsDescRel = R_AARCH64_TLSDESC; + tlsGotRel = R_AARCH64_TLS_TPREL64; + pltEntrySize = 16; + pltHeaderSize = 32; + defaultMaxPageSize = 65536; // Align to the 2 MiB page size (known as a superpage or huge page). // FreeBSD automatically promotes 2 MiB-aligned allocations. - DefaultImageBase = 0x200000; + defaultImageBase = 0x200000; - NeedsThunks = true; + needsThunks = true; } -RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_AARCH64_TLSDESC_ADR_PAGE21: return R_AARCH64_TLSDESC_PAGE; case R_AARCH64_TLSDESC_LD64_LO12: case R_AARCH64_TLSDESC_ADD_LO12: return R_TLSDESC; case R_AARCH64_TLSDESC_CALL: return R_TLSDESC_CALL; case R_AARCH64_TLSLE_ADD_TPREL_HI12: case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC: case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: return R_TLS; case R_AARCH64_CALL26: case R_AARCH64_CONDBR19: case R_AARCH64_JUMP26: case R_AARCH64_TSTBR14: return R_PLT_PC; case R_AARCH64_PREL16: 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: + case R_AARCH64_ADR_PREL_PG_HI21_NC: return R_AARCH64_PAGE_PC; case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: return R_GOT; case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: return R_AARCH64_GOT_PAGE_PC; case R_AARCH64_NONE: return R_NONE; default: return R_ABS; } } -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) +RelExpr AArch64::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + if (expr == R_RELAX_TLS_GD_TO_IE) { + if (type == R_AARCH64_TLSDESC_ADR_PAGE21) return R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC; return R_RELAX_TLS_GD_TO_IE_ABS; } - return Expr; + return expr; } -bool AArch64::usesOnlyLowPageBits(RelType Type) const { - switch (Type) { +bool AArch64::usesOnlyLowPageBits(RelType type) const { + switch (type) { default: return false; case R_AARCH64_ADD_ABS_LO12_NC: case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_LDST128_ABS_LO12_NC: case R_AARCH64_LDST16_ABS_LO12_NC: case R_AARCH64_LDST32_ABS_LO12_NC: case R_AARCH64_LDST64_ABS_LO12_NC: case R_AARCH64_LDST8_ABS_LO12_NC: case R_AARCH64_TLSDESC_ADD_LO12: case R_AARCH64_TLSDESC_LD64_LO12: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: return true; } } -RelType AArch64::getDynRel(RelType Type) const { - if (Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64) - return Type; +RelType AArch64::getDynRel(RelType type) const { + if (type == R_AARCH64_ABS64) + return type; return R_AARCH64_NONE; } -void AArch64::writeGotPlt(uint8_t *Buf, const Symbol &) const { - write64le(Buf, In.Plt->getVA()); +void AArch64::writeGotPlt(uint8_t *buf, const Symbol &) const { + write64le(buf, in.plt->getVA()); } -void AArch64::writePltHeader(uint8_t *Buf) const { - const uint8_t PltData[] = { +void AArch64::writePltHeader(uint8_t *buf) const { + const uint8_t pltData[] = { 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]! 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2])) 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))] 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2])) 0x20, 0x02, 0x1f, 0xd6, // br x17 0x1f, 0x20, 0x03, 0xd5, // nop 0x1f, 0x20, 0x03, 0xd5, // nop 0x1f, 0x20, 0x03, 0xd5 // nop }; - memcpy(Buf, PltData, sizeof(PltData)); + memcpy(buf, pltData, sizeof(pltData)); - uint64_t Got = In.GotPlt->getVA(); - uint64_t Plt = In.Plt->getVA(); - relocateOne(Buf + 4, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(Got + 16) - getAArch64Page(Plt + 4)); - relocateOne(Buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, Got + 16); - relocateOne(Buf + 12, R_AARCH64_ADD_ABS_LO12_NC, Got + 16); + uint64_t got = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(got + 16) - getAArch64Page(plt + 4)); + relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16); + relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16); } -void AArch64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Inst[] = { +void AArch64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t inst[] = { 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n])) 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))] 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[n])) 0x20, 0x02, 0x1f, 0xd6 // br x17 }; - memcpy(Buf, Inst, sizeof(Inst)); + memcpy(buf, inst, sizeof(inst)); - relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(GotPltEntryAddr) - getAArch64Page(PltEntryAddr)); - relocateOne(Buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, GotPltEntryAddr); - relocateOne(Buf + 8, R_AARCH64_ADD_ABS_LO12_NC, GotPltEntryAddr); + relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr)); + relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, 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 { +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) + 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); + uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); + return !inBranchRange(type, branchAddr, dst); } uint32_t AArch64::getThunkSectionSpacing() const { // See comment in Arch/ARM.cpp for a more detailed explanation of // getThunkSectionSpacing(). For AArch64 the only branches we are permitted to // Thunk have a range of +/- 128 MiB return (128 * 1024 * 1024) - 0x30000; } -bool AArch64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { - if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26) +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) { + uint64_t range = 128 * 1024 * 1024; + if (dst > src) { // Immediate of branch is signed. - Range -= 4; - return Dst - Src <= Range; + range -= 4; + return dst - src <= range; } - return Src - Dst <= 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; - uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3); - write32le(L, (read32le(L) & ~Mask) | ImmLo | ImmHi); +static void write32AArch64Addr(uint8_t *l, uint64_t imm) { + uint32_t immLo = (imm & 0x3) << 29; + uint32_t immHi = (imm & 0x1FFFFC) << 3; + uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3); + write32le(l, (read32le(l) & ~mask) | immLo | immHi); } // Return the bits [Start, End] from Val shifted Start bits. // For instance, getBits(0xF0, 4, 8) returns 0xF. -static uint64_t getBits(uint64_t Val, int Start, int End) { - uint64_t Mask = ((uint64_t)1 << (End + 1 - Start)) - 1; - return (Val >> Start) & Mask; +static uint64_t getBits(uint64_t val, int start, int end) { + uint64_t mask = ((uint64_t)1 << (end + 1 - start)) - 1; + return (val >> start) & mask; } -static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); } +static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); } // Update the immediate field in a AARCH64 ldr, str, and add instruction. -static void or32AArch64Imm(uint8_t *L, uint64_t Imm) { - or32le(L, (Imm & 0xFFF) << 10); +static void or32AArch64Imm(uint8_t *l, uint64_t imm) { + or32le(l, (imm & 0xFFF) << 10); } -void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_AARCH64_ABS16: case R_AARCH64_PREL16: - checkIntUInt(Loc, Val, 16, Type); - write16le(Loc, Val); + checkIntUInt(loc, val, 16, type); + write16le(loc, val); break; case R_AARCH64_ABS32: case R_AARCH64_PREL32: - checkIntUInt(Loc, Val, 32, Type); - write32le(Loc, Val); + checkIntUInt(loc, val, 32, type); + write32le(loc, val); break; case R_AARCH64_ABS64: - case R_AARCH64_GLOB_DAT: case R_AARCH64_PREL64: - write64le(Loc, Val); + write64le(loc, val); break; case R_AARCH64_ADD_ABS_LO12_NC: - or32AArch64Imm(Loc, Val); + or32AArch64Imm(loc, val); break; case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_ADR_PREL_PG_HI21: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case R_AARCH64_TLSDESC_ADR_PAGE21: - checkInt(Loc, Val, 33, Type); - write32AArch64Addr(Loc, Val >> 12); + checkInt(loc, val, 33, type); + LLVM_FALLTHROUGH; + case R_AARCH64_ADR_PREL_PG_HI21_NC: + write32AArch64Addr(loc, val >> 12); break; case R_AARCH64_ADR_PREL_LO21: - checkInt(Loc, Val, 21, Type); - write32AArch64Addr(Loc, Val); + checkInt(loc, val, 21, type); + write32AArch64Addr(loc, val); break; 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); + write32le(loc, 0x14000000); LLVM_FALLTHROUGH; case R_AARCH64_CALL26: - checkInt(Loc, Val, 28, Type); - or32le(Loc, (Val & 0x0FFFFFFC) >> 2); + checkInt(loc, val, 28, type); + or32le(loc, (val & 0x0FFFFFFC) >> 2); break; case R_AARCH64_CONDBR19: case R_AARCH64_LD_PREL_LO19: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 21, Type); - or32le(Loc, (Val & 0x1FFFFC) << 3); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 21, type); + or32le(loc, (val & 0x1FFFFC) << 3); break; case R_AARCH64_LDST8_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC: - or32AArch64Imm(Loc, getBits(Val, 0, 11)); + or32AArch64Imm(loc, getBits(val, 0, 11)); break; case R_AARCH64_LDST16_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: - checkAlignment(Loc, Val, 2, Type); - or32AArch64Imm(Loc, getBits(Val, 1, 11)); + checkAlignment(loc, val, 2, type); + or32AArch64Imm(loc, getBits(val, 1, 11)); break; case R_AARCH64_LDST32_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: - checkAlignment(Loc, Val, 4, Type); - or32AArch64Imm(Loc, getBits(Val, 2, 11)); + checkAlignment(loc, val, 4, type); + or32AArch64Imm(loc, getBits(val, 2, 11)); break; case R_AARCH64_LDST64_ABS_LO12_NC: case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: case R_AARCH64_TLSDESC_LD64_LO12: - checkAlignment(Loc, Val, 8, Type); - or32AArch64Imm(Loc, getBits(Val, 3, 11)); + checkAlignment(loc, val, 8, type); + or32AArch64Imm(loc, getBits(val, 3, 11)); break; case R_AARCH64_LDST128_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: - checkAlignment(Loc, Val, 16, Type); - or32AArch64Imm(Loc, getBits(Val, 4, 11)); + checkAlignment(loc, val, 16, type); + or32AArch64Imm(loc, getBits(val, 4, 11)); break; case R_AARCH64_MOVW_UABS_G0_NC: - or32le(Loc, (Val & 0xFFFF) << 5); + or32le(loc, (val & 0xFFFF) << 5); break; case R_AARCH64_MOVW_UABS_G1_NC: - or32le(Loc, (Val & 0xFFFF0000) >> 11); + or32le(loc, (val & 0xFFFF0000) >> 11); break; case R_AARCH64_MOVW_UABS_G2_NC: - or32le(Loc, (Val & 0xFFFF00000000) >> 27); + or32le(loc, (val & 0xFFFF00000000) >> 27); break; case R_AARCH64_MOVW_UABS_G3: - or32le(Loc, (Val & 0xFFFF000000000000) >> 43); + or32le(loc, (val & 0xFFFF000000000000) >> 43); break; case R_AARCH64_TSTBR14: - checkInt(Loc, Val, 16, Type); - or32le(Loc, (Val & 0xFFFC) << 3); + checkInt(loc, val, 16, type); + or32le(loc, (val & 0xFFFC) << 3); break; case R_AARCH64_TLSLE_ADD_TPREL_HI12: - checkUInt(Loc, Val, 24, Type); - or32AArch64Imm(Loc, Val >> 12); + checkUInt(loc, val, 24, type); + or32AArch64Imm(loc, val >> 12); break; case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: case R_AARCH64_TLSDESC_ADD_LO12: - or32AArch64Imm(Loc, Val); + or32AArch64Imm(loc, val); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } -void AArch64::relaxTlsGdToLe(uint8_t *Loc, RelType 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] // add x0, x0, :tlsdesc_los:v [R_AARCH64_TLSDESC_ADD_LO12] // .tlsdesccall [R_AARCH64_TLSDESC_CALL] // blr x1 // And it can optimized to: // movz x0, #0x0, lsl #16 // movk x0, #0x10 // nop // nop - checkUInt(Loc, Val, 32, Type); + checkUInt(loc, val, 32, type); - switch (Type) { + switch (type) { case R_AARCH64_TLSDESC_ADD_LO12: case R_AARCH64_TLSDESC_CALL: - write32le(Loc, 0xd503201f); // nop + write32le(loc, 0xd503201f); // nop return; case R_AARCH64_TLSDESC_ADR_PAGE21: - write32le(Loc, 0xd2a00000 | (((Val >> 16) & 0xffff) << 5)); // movz + write32le(loc, 0xd2a00000 | (((val >> 16) & 0xffff) << 5)); // movz return; case R_AARCH64_TLSDESC_LD64_LO12: - write32le(Loc, 0xf2800000 | ((Val & 0xffff) << 5)); // movk + write32le(loc, 0xf2800000 | ((val & 0xffff) << 5)); // movk return; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } -void AArch64::relaxTlsGdToIe(uint8_t *Loc, RelType 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] // add x0, x0, :tlsdesc_los:v [R_AARCH64_TLSDESC_ADD_LO12] // .tlsdesccall [R_AARCH64_TLSDESC_CALL] // blr x1 // And it can optimized to: // adrp x0, :gottprel:v // ldr x0, [x0, :gottprel_lo12:v] // nop // nop - switch (Type) { + switch (type) { case R_AARCH64_TLSDESC_ADD_LO12: case R_AARCH64_TLSDESC_CALL: - write32le(Loc, 0xd503201f); // nop + write32le(loc, 0xd503201f); // nop break; case R_AARCH64_TLSDESC_ADR_PAGE21: - write32le(Loc, 0x90000000); // adrp - relocateOne(Loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, Val); + write32le(loc, 0x90000000); // adrp + relocateOne(loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, val); break; case R_AARCH64_TLSDESC_LD64_LO12: - write32le(Loc, 0xf9400000); // ldr - relocateOne(Loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, Val); + write32le(loc, 0xf9400000); // ldr + relocateOne(loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, val); break; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } -void AArch64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { - checkUInt(Loc, Val, 32, Type); +void AArch64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { + checkUInt(loc, val, 32, type); - if (Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) { + if (type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) { // Generate MOVZ. - uint32_t RegNo = read32le(Loc) & 0x1f; - write32le(Loc, (0xd2a00000 | RegNo) | (((Val >> 16) & 0xffff) << 5)); + uint32_t regNo = read32le(loc) & 0x1f; + write32le(loc, (0xd2a00000 | regNo) | (((val >> 16) & 0xffff) << 5)); return; } - if (Type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) { + if (type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) { // Generate MOVK. - uint32_t RegNo = read32le(Loc) & 0x1f; - write32le(Loc, (0xf2800000 | RegNo) | ((Val & 0xffff) << 5)); + uint32_t regNo = read32le(loc) & 0x1f; + write32le(loc, (0xf2800000 | regNo) | ((val & 0xffff) << 5)); return; } llvm_unreachable("invalid relocation for TLS IE to LE relaxation"); } -TargetInfo *elf::getAArch64TargetInfo() { - static AArch64 Target; - return &Target; +// AArch64 may use security features in variant PLT sequences. These are: +// Pointer Authentication (PAC), introduced in armv8.3-a and Branch Target +// Indicator (BTI) introduced in armv8.5-a. The additional instructions used +// in the variant Plt sequences are encoded in the Hint space so they can be +// deployed on older architectures, which treat the instructions as a nop. +// PAC and BTI can be combined leading to the following combinations: +// writePltHeader +// writePltHeaderBti (no PAC Header needed) +// writePlt +// writePltBti (BTI only) +// writePltPac (PAC only) +// writePltBtiPac (BTI and PAC) +// +// When PAC is enabled the dynamic loader encrypts the address that it places +// in the .got.plt using the pacia1716 instruction which encrypts the value in +// x17 using the modifier in x16. The static linker places autia1716 before the +// indirect branch to x17 to authenticate the address in x17 with the modifier +// in x16. This makes it more difficult for an attacker to modify the value in +// the .got.plt. +// +// When BTI is enabled all indirect branches must land on a bti instruction. +// The static linker must place a bti instruction at the start of any PLT entry +// that may be the target of an indirect branch. As the PLT entries call the +// lazy resolver indirectly this must have a bti instruction at start. In +// general a bti instruction is not needed for a PLT entry as indirect calls +// are resolved to the function address and not the PLT entry for the function. +// There are a small number of cases where the PLT address can escape, such as +// taking the address of a function or ifunc via a non got-generating +// relocation, and a shared library refers to that symbol. +// +// We use the bti c variant of the instruction which permits indirect branches +// (br) via x16/x17 and indirect function calls (blr) via any register. The ABI +// guarantees that all indirect branches from code requiring BTI protection +// will go via x16/x17 + +namespace { +class AArch64BtiPac final : public AArch64 { +public: + AArch64BtiPac(); + 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; + +private: + bool btiHeader; // bti instruction needed in PLT Header + bool btiEntry; // bti instruction needed in PLT Entry + bool pacEntry; // autia1716 instruction needed in PLT Entry +}; +} // namespace + +AArch64BtiPac::AArch64BtiPac() { + btiHeader = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI); + // A BTI (Branch Target Indicator) Plt Entry is only required if the + // address of the PLT entry can be taken by the program, which permits an + // indirect jump to the PLT entry. This can happen when the address + // of the PLT entry for a function is canonicalised due to the address of + // the function in an executable being taken by a shared library. + // FIXME: There is a potential optimization to omit the BTI if we detect + // that the address of the PLT entry isn't taken. + btiEntry = btiHeader && !config->shared; + pacEntry = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC); + + if (btiEntry || pacEntry) + pltEntrySize = 24; } + +void AArch64BtiPac::writePltHeader(uint8_t *buf) const { + const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c + const uint8_t pltData[] = { + 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]! + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2])) + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5 // nop + }; + const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop + + uint64_t got = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + + if (btiHeader) { + // PltHeader is called indirectly by plt[N]. Prefix pltData with a BTI C + // instruction. + memcpy(buf, btiData, sizeof(btiData)); + buf += sizeof(btiData); + plt += sizeof(btiData); + } + memcpy(buf, pltData, sizeof(pltData)); + + relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(got + 16) - getAArch64Page(plt + 8)); + relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16); + relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16); + if (!btiHeader) + // We didn't add the BTI c instruction so round out size with NOP. + memcpy(buf + sizeof(pltData), nopData, sizeof(nopData)); +} + +void AArch64BtiPac::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + // The PLT entry is of the form: + // [btiData] addrInst (pacBr | stdBr) [nopData] + const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c + const uint8_t addrInst[] = { + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))] + 0x10, 0x02, 0x00, 0x91 // add x16, x16, Offset(&(.plt.got[n])) + }; + const uint8_t pacBr[] = { + 0x9f, 0x21, 0x03, 0xd5, // autia1716 + 0x20, 0x02, 0x1f, 0xd6 // br x17 + }; + const uint8_t stdBr[] = { + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5 // nop + }; + const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop + + if (btiEntry) { + memcpy(buf, btiData, sizeof(btiData)); + buf += sizeof(btiData); + pltEntryAddr += sizeof(btiData); + } + + memcpy(buf, addrInst, sizeof(addrInst)); + relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(gotPltEntryAddr) - + getAArch64Page(pltEntryAddr)); + relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr); + relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr); + + if (pacEntry) + memcpy(buf + sizeof(addrInst), pacBr, sizeof(pacBr)); + else + memcpy(buf + sizeof(addrInst), stdBr, sizeof(stdBr)); + if (!btiEntry) + // We didn't add the BTI c instruction so round out size with NOP. + memcpy(buf + sizeof(addrInst) + sizeof(stdBr), nopData, sizeof(nopData)); +} + +static TargetInfo *getTargetInfo() { + if (config->andFeatures & (GNU_PROPERTY_AARCH64_FEATURE_1_BTI | + GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) { + static AArch64BtiPac t; + return &t; + } + static AArch64 t; + return &t; +} + +TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); } Index: vendor/lld/dist/ELF/Arch/AMDGPU.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/AMDGPU.cpp (revision 351287) +++ vendor/lld/dist/ELF/Arch/AMDGPU.cpp (revision 351288) @@ -1,105 +1,113 @@ //===- AMDGPU.cpp ---------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { class AMDGPU final : public TargetInfo { public: AMDGPU(); 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; + 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; + RelType getDynRel(RelType type) const override; }; } // namespace AMDGPU::AMDGPU() { - RelativeRel = R_AMDGPU_RELATIVE64; - GotRel = R_AMDGPU_ABS64; - NoneRel = R_AMDGPU_NONE; - GotEntrySize = 8; + relativeRel = R_AMDGPU_RELATIVE64; + gotRel = R_AMDGPU_ABS64; + noneRel = R_AMDGPU_NONE; + symbolicRel = R_AMDGPU_ABS64; } -static uint32_t getEFlags(InputFile *File) { - return cast>(File)->getObj().getHeader()->e_flags; +static uint32_t getEFlags(InputFile *file) { + return cast>(file)->getObj().getHeader()->e_flags; } uint32_t AMDGPU::calcEFlags() const { - assert(!ObjectFiles.empty()); - uint32_t Ret = getEFlags(ObjectFiles[0]); + 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)) + for (InputFile *f : makeArrayRef(objectFiles).slice(1)) { + if (ret == getEFlags(f)) continue; - error("incompatible e_flags: " + toString(F)); + error("incompatible e_flags: " + toString(f)); return 0; } - return Ret; + return ret; } -void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void AMDGPU::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_AMDGPU_ABS32: case R_AMDGPU_GOTPCREL: case R_AMDGPU_GOTPCREL32_LO: case R_AMDGPU_REL32: case R_AMDGPU_REL32_LO: - write32le(Loc, Val); + write32le(loc, val); break; case R_AMDGPU_ABS64: case R_AMDGPU_REL64: - write64le(Loc, Val); + write64le(loc, val); break; case R_AMDGPU_GOTPCREL32_HI: case R_AMDGPU_REL32_HI: - write32le(Loc, Val >> 32); + write32le(loc, val >> 32); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -RelExpr AMDGPU::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_AMDGPU_ABS32: case R_AMDGPU_ABS64: return R_ABS; case R_AMDGPU_REL32: case R_AMDGPU_REL32_LO: case R_AMDGPU_REL32_HI: case R_AMDGPU_REL64: return R_PC; case R_AMDGPU_GOTPCREL: case R_AMDGPU_GOTPCREL32_LO: case R_AMDGPU_GOTPCREL32_HI: return R_GOT_PC; default: - return R_INVALID; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } +RelType AMDGPU::getDynRel(RelType type) const { + if (type == R_AMDGPU_ABS64) + return type; + return R_AMDGPU_NONE; +} + TargetInfo *elf::getAMDGPUTargetInfo() { - static AMDGPU Target; - return &Target; + static AMDGPU target; + return ⌖ } Index: vendor/lld/dist/ELF/Arch/ARM.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/ARM.cpp (revision 351287) +++ vendor/lld/dist/ELF/Arch/ARM.cpp (revision 351288) @@ -1,612 +1,606 @@ //===- ARM.cpp ------------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #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" using namespace llvm; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { class ARM final : public TargetInfo { public: ARM(); uint32_t calcEFlags() const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) 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(InputSection &IS, uint64_t Off) const override; - void addPltHeaderSymbols(InputSection &ISD) const override; - bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) 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(InputSection &isec, uint64_t off) const override; + void addPltHeaderSymbols(InputSection &isd) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; uint32_t getThunkSectionSpacing() const override; - bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + 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 ARM::ARM() { - CopyRel = R_ARM_COPY; - RelativeRel = R_ARM_RELATIVE; - IRelativeRel = R_ARM_IRELATIVE; - GotRel = R_ARM_GLOB_DAT; - NoneRel = R_ARM_NONE; - PltRel = R_ARM_JUMP_SLOT; - TlsGotRel = R_ARM_TLS_TPOFF32; - TlsModuleIndexRel = R_ARM_TLS_DTPMOD32; - TlsOffsetRel = R_ARM_TLS_DTPOFF32; - GotBaseSymInGotPlt = false; - GotEntrySize = 4; - GotPltEntrySize = 4; - PltEntrySize = 16; - PltHeaderSize = 32; - TrapInstr = {0xd4, 0xd4, 0xd4, 0xd4}; - NeedsThunks = true; + copyRel = R_ARM_COPY; + relativeRel = R_ARM_RELATIVE; + iRelativeRel = R_ARM_IRELATIVE; + gotRel = R_ARM_GLOB_DAT; + noneRel = R_ARM_NONE; + pltRel = R_ARM_JUMP_SLOT; + symbolicRel = R_ARM_ABS32; + tlsGotRel = R_ARM_TLS_TPOFF32; + tlsModuleIndexRel = R_ARM_TLS_DTPMOD32; + tlsOffsetRel = R_ARM_TLS_DTPOFF32; + gotBaseSymInGotPlt = false; + pltEntrySize = 16; + pltHeaderSize = 32; + trapInstr = {0xd4, 0xd4, 0xd4, 0xd4}; + needsThunks = true; } uint32_t ARM::calcEFlags() const { // The ABIFloatType is used by loaders to detect the floating point calling // convention. - uint32_t ABIFloatType = 0; - if (Config->ARMVFPArgs == ARMVFPArgKind::Base || - Config->ARMVFPArgs == ARMVFPArgKind::Default) - ABIFloatType = EF_ARM_ABI_FLOAT_SOFT; - else if (Config->ARMVFPArgs == ARMVFPArgKind::VFP) - ABIFloatType = EF_ARM_ABI_FLOAT_HARD; + uint32_t abiFloatType = 0; + if (config->armVFPArgs == ARMVFPArgKind::Base || + config->armVFPArgs == ARMVFPArgKind::Default) + abiFloatType = EF_ARM_ABI_FLOAT_SOFT; + else if (config->armVFPArgs == ARMVFPArgKind::VFP) + abiFloatType = EF_ARM_ABI_FLOAT_HARD; // 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 | ABIFloatType; + return EF_ARM_EABI_VER5 | abiFloatType; } -RelExpr ARM::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr ARM::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_ARM_THM_JUMP11: return R_PC; case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_PREL31: case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: case R_ARM_THM_CALL: return R_PLT_PC; case R_ARM_GOTOFF32: // (S + A) - GOT_ORG return R_GOTREL; case R_ARM_GOT_BREL: // GOT(S) + A - GOT_ORG return R_GOT_OFF; case R_ARM_GOT_PREL: case R_ARM_TLS_IE32: // GOT(S) + A - P return R_GOT_PC; case R_ARM_SBREL32: return R_ARM_SBREL; case R_ARM_TARGET1: - return Config->Target1Rel ? R_PC : R_ABS; + return config->target1Rel ? R_PC : R_ABS; case R_ARM_TARGET2: - if (Config->Target2 == Target2Policy::Rel) + if (config->target2 == Target2Policy::Rel) return R_PC; - if (Config->Target2 == Target2Policy::Abs) + if (config->target2 == Target2Policy::Abs) return R_ABS; return R_GOT_PC; case R_ARM_TLS_GD32: return R_TLSGD_PC; case R_ARM_TLS_LDM32: return R_TLSLD_PC; case R_ARM_BASE_PREL: // B(S) + A - P // FIXME: currently B(S) assumed to be .got, this may not hold for all // platforms. return R_GOTONLY_PC; case R_ARM_MOVW_PREL_NC: case R_ARM_MOVT_PREL: case R_ARM_REL32: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: return R_PC; case R_ARM_NONE: return R_NONE; case R_ARM_TLS_LE32: return R_TLS; case R_ARM_V4BX: // V4BX is just a marker to indicate there's a "bx rN" instruction at the // given address. It can be used to implement a special linker mode which // rewrites ARMv4T inputs to ARMv4. Since we support only ARMv4 input and // not ARMv4 output, we can just ignore it. return R_HINT; default: return R_ABS; } } -RelType ARM::getDynRel(RelType Type) const { - if ((Type == R_ARM_ABS32) || (Type == R_ARM_TARGET1 && !Config->Target1Rel)) +RelType ARM::getDynRel(RelType type) const { + if ((type == R_ARM_ABS32) || (type == R_ARM_TARGET1 && !config->target1Rel)) return R_ARM_ABS32; return R_ARM_NONE; } -void ARM::writeGotPlt(uint8_t *Buf, const Symbol &) const { - write32le(Buf, In.Plt->getVA()); +void ARM::writeGotPlt(uint8_t *buf, const Symbol &) const { + write32le(buf, in.plt->getVA()); } -void ARM::writeIgotPlt(uint8_t *Buf, const Symbol &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()); + write32le(buf, s.getVA()); } // Long form PLT Header 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[] = { +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 = In.GotPlt->getVA(); - uint64_t L1 = In.Plt->getVA() + 8; - write32le(Buf + 16, GotPlt - L1 - 8); + memcpy(buf, pltData, sizeof(pltData)); + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t l1 = in.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 { +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[] = { + 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 = In.GotPlt->getVA() - In.Plt->getVA() - 4; - if (!llvm::isUInt<27>(Offset)) { + uint64_t offset = in.gotPlt->getVA() - in.plt->getVA() - 4; + if (!llvm::isUInt<27>(offset)) { // We cannot encode the Offset, use the long form. - writePltHeaderLong(Buf); + 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)); - memcpy(Buf + 16, TrapInstr.data(), 4); // Pad to 32-byte boundary - memcpy(Buf + 20, TrapInstr.data(), 4); - memcpy(Buf + 24, TrapInstr.data(), 4); - memcpy(Buf + 28, TrapInstr.data(), 4); + 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)); + memcpy(buf + 16, trapInstr.data(), 4); // Pad to 32-byte boundary + memcpy(buf + 20, trapInstr.data(), 4); + memcpy(buf + 24, trapInstr.data(), 4); + memcpy(buf + 28, trapInstr.data(), 4); } -void ARM::addPltHeaderSymbols(InputSection &IS) const { - addSyntheticLocal("$a", STT_NOTYPE, 0, 0, IS); - addSyntheticLocal("$d", STT_NOTYPE, 16, 0, IS); +void ARM::addPltHeaderSymbols(InputSection &isec) const { + addSyntheticLocal("$a", STT_NOTYPE, 0, 0, isec); + addSyntheticLocal("$d", STT_NOTYPE, 16, 0, isec); } // 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[] = { +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 0x00, 0xf0, 0x9c, 0xe5, // ldr pc, [ip] 0x00, 0x00, 0x00, 0x00, // L2: .word Offset(&(.plt.got) - L1 - 8 }; - memcpy(Buf, PltData, sizeof(PltData)); - uint64_t L1 = PltEntryAddr + 4; - write32le(Buf + 12, GotPltEntryAddr - L1 - 8); + memcpy(buf, pltData, sizeof(pltData)); + uint64_t l1 = pltEntryAddr + 4; + 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 { +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[] = { + 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)) { + 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); + 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)); - memcpy(Buf + 12, TrapInstr.data(), 4); // Pad to 16-byte boundary + write32le(buf + 0, pltData[0] | ((offset >> 20) & 0xff)); + write32le(buf + 4, pltData[1] | ((offset >> 12) & 0xff)); + write32le(buf + 8, pltData[2] | (offset & 0xfff)); + memcpy(buf + 12, trapInstr.data(), 4); // Pad to 16-byte boundary } -void ARM::addPltSymbols(InputSection &IS, uint64_t Off) const { - addSyntheticLocal("$a", STT_NOTYPE, Off, 0, IS); - addSyntheticLocal("$d", STT_NOTYPE, Off + 12, 0, IS); +void ARM::addPltSymbols(InputSection &isec, uint64_t off) const { + addSyntheticLocal("$a", STT_NOTYPE, off, 0, isec); + addSyntheticLocal("$d", STT_NOTYPE, off + 12, 0, isec); } -bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const { +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()) + 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 (Type) { + switch (type) { case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_JUMP24: // Source is ARM, all PLT entries are ARM so no interworking required. // Otherwise we need to interwork if Symbol has bit 0 set (Thumb). - if (Expr == R_PC && ((S.getVA() & 1) == 1)) + if (expr == R_PC && ((s.getVA() & 1) == 1)) return true; LLVM_FALLTHROUGH; case R_ARM_CALL: { - uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA(); - return !inBranchRange(Type, BranchAddr, Dst); + 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)) + if (expr == R_PLT_PC || ((s.getVA() & 1) == 0)) return true; LLVM_FALLTHROUGH; case R_ARM_THM_CALL: { - uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA(); - return !inBranchRange(Type, BranchAddr, Dst); + uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); + return !inBranchRange(type, branchAddr, dst); } } return false; } uint32_t ARM::getThunkSectionSpacing() const { // The placing of pre-created ThunkSections is controlled by the value - // ThunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to + // thunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to // place the ThunkSection such that all branches from the InputSections // prior to the ThunkSection can reach a Thunk placed at the end of the // ThunkSection. Graphically: - // | up to ThunkSectionSpacing .text input sections | + // | up to thunkSectionSpacing .text input sections | // | ThunkSection | - // | up to ThunkSectionSpacing .text input sections | + // | up to thunkSectionSpacing .text input sections | // | ThunkSection | // Pre-created ThunkSections are spaced roughly 16MiB apart on ARMv7. This // is to match the most common expected case of a Thumb 2 encoded BL, BLX or // B.W: // ARM B, BL, BLX range +/- 32MiB // Thumb B.W, BL, BLX range +/- 16MiB // Thumb B.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 + // 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 + // thunkSectionSpacing by is set conservatively to allow us to create 16,384 // 12 byte Thunks at any offset in a ThunkSection without risk of a branch to // one of the Thunks going out of range. - // On Arm the ThunkSectionSpacing depends on the range of the Thumb Branch + // On Arm the thunkSectionSpacing depends on the range of the Thumb Branch // range. On earlier Architectures such as ARMv4, ARMv5 and ARMv6 (except // ARMv6T2) the range is +/- 4MiB. - return (Config->ARMJ1J2BranchEncoding) ? 0x1000000 - 0x30000 + return (config->armJ1J2BranchEncoding) ? 0x1000000 - 0x30000 : 0x400000 - 0x7500; } -bool ARM::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { - uint64_t Range; - uint64_t InstrSize; +bool ARM::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + uint64_t range; + uint64_t instrSize; - switch (Type) { + switch (type) { case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_JUMP24: case R_ARM_CALL: - Range = 0x2000000; - InstrSize = 4; + range = 0x2000000; + instrSize = 4; break; case R_ARM_THM_JUMP19: - Range = 0x100000; - InstrSize = 2; + range = 0x100000; + instrSize = 2; break; case R_ARM_THM_JUMP24: case R_ARM_THM_CALL: - Range = Config->ARMJ1J2BranchEncoding ? 0x1000000 : 0x400000; - InstrSize = 2; + range = config->armJ1J2BranchEncoding ? 0x1000000 : 0x400000; + instrSize = 2; break; default: return true; } // PC at Src is 2 instructions ahead, immediate of branch is signed - if (Src > Dst) - Range -= 2 * InstrSize; + if (src > dst) + range -= 2 * instrSize; else - Range += InstrSize; + range += instrSize; - if ((Dst & 0x1) == 0) + if ((dst & 0x1) == 0) // Destination is ARM, if ARM caller then Src is already 4-byte aligned. // If Thumb Caller (BLX) the Src address has bottom 2 bits cleared to ensure // destination will be 4 byte aligned. - Src &= ~0x3; + src &= ~0x3; else // Bit 0 == 1 denotes Thumb state, it is not part of the range - Dst &= ~0x1; + dst &= ~0x1; - uint64_t Distance = (Src > Dst) ? Src - Dst : Dst - Src; - return Distance <= Range; + uint64_t distance = (src > dst) ? src - dst : dst - src; + return distance <= range; } -void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_ARM_ABS32: case R_ARM_BASE_PREL: - case R_ARM_GLOB_DAT: case R_ARM_GOTOFF32: case R_ARM_GOT_BREL: case R_ARM_GOT_PREL: case R_ARM_REL32: case R_ARM_RELATIVE: case R_ARM_SBREL32: case R_ARM_TARGET1: case R_ARM_TARGET2: case R_ARM_TLS_GD32: case R_ARM_TLS_IE32: case R_ARM_TLS_LDM32: case R_ARM_TLS_LDO32: case R_ARM_TLS_LE32: case R_ARM_TLS_TPOFF32: case R_ARM_TLS_DTPOFF32: - write32le(Loc, Val); + write32le(loc, val); break; - case R_ARM_TLS_DTPMOD32: - write32le(Loc, 1); - break; case R_ARM_PREL31: - checkInt(Loc, Val, 31, Type); - write32le(Loc, (read32le(Loc) & 0x80000000) | (Val & ~0x80000000)); + checkInt(loc, val, 31, type); + write32le(loc, (read32le(loc) & 0x80000000) | (val & ~0x80000000)); break; case R_ARM_CALL: // R_ARM_CALL is used for BL and BLX instructions, depending on the // value of bit 0 of Val, we must select a BL or BLX instruction - if (Val & 1) { + if (val & 1) { // If bit 0 of Val is 1 the target is Thumb, we must select a BLX. // The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1' - checkInt(Loc, Val, 26, Type); - write32le(Loc, 0xfa000000 | // opcode - ((Val & 2) << 23) | // H - ((Val >> 2) & 0x00ffffff)); // imm24 + checkInt(loc, val, 26, type); + write32le(loc, 0xfa000000 | // opcode + ((val & 2) << 23) | // H + ((val >> 2) & 0x00ffffff)); // imm24 break; } - if ((read32le(Loc) & 0xfe000000) == 0xfa000000) + if ((read32le(loc) & 0xfe000000) == 0xfa000000) // BLX (always unconditional) instruction to an ARM Target, select an // unconditional BL. - write32le(Loc, 0xeb000000 | (read32le(Loc) & 0x00ffffff)); + write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff)); // fall through as BL encoding is shared with B LLVM_FALLTHROUGH; case R_ARM_JUMP24: case R_ARM_PC24: case R_ARM_PLT32: - checkInt(Loc, Val, 26, Type); - write32le(Loc, (read32le(Loc) & ~0x00ffffff) | ((Val >> 2) & 0x00ffffff)); + checkInt(loc, val, 26, type); + write32le(loc, (read32le(loc) & ~0x00ffffff) | ((val >> 2) & 0x00ffffff)); break; case R_ARM_THM_JUMP11: - checkInt(Loc, Val, 12, Type); - write16le(Loc, (read32le(Loc) & 0xf800) | ((Val >> 1) & 0x07ff)); + checkInt(loc, val, 12, type); + write16le(loc, (read32le(loc) & 0xf800) | ((val >> 1) & 0x07ff)); break; case R_ARM_THM_JUMP19: // Encoding T3: Val = S:J2:J1:imm6:imm11:0 - checkInt(Loc, Val, 21, Type); - write16le(Loc, - (read16le(Loc) & 0xfbc0) | // opcode cond - ((Val >> 10) & 0x0400) | // S - ((Val >> 12) & 0x003f)); // imm6 - write16le(Loc + 2, + checkInt(loc, val, 21, type); + write16le(loc, + (read16le(loc) & 0xfbc0) | // opcode cond + ((val >> 10) & 0x0400) | // S + ((val >> 12) & 0x003f)); // imm6 + write16le(loc + 2, 0x8000 | // opcode - ((Val >> 8) & 0x0800) | // J2 - ((Val >> 5) & 0x2000) | // J1 - ((Val >> 1) & 0x07ff)); // imm11 + ((val >> 8) & 0x0800) | // J2 + ((val >> 5) & 0x2000) | // J1 + ((val >> 1) & 0x07ff)); // imm11 break; case R_ARM_THM_CALL: // R_ARM_THM_CALL is used for BL and BLX instructions, depending on the // value of bit 0 of Val, we must select a BL or BLX instruction - if ((Val & 1) == 0) { + if ((val & 1) == 0) { // Ensure BLX destination is 4-byte aligned. As BLX instruction may // only be two byte aligned. This must be done before overflow check - Val = alignTo(Val, 4); + val = alignTo(val, 4); } // Bit 12 is 0 for BLX, 1 for BL - write16le(Loc + 2, (read16le(Loc + 2) & ~0x1000) | (Val & 1) << 12); - if (!Config->ARMJ1J2BranchEncoding) { + write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | (val & 1) << 12); + if (!config->armJ1J2BranchEncoding) { // Older Arm architectures do not support R_ARM_THM_JUMP24 and have // different encoding rules and range due to J1 and J2 always being 1. - checkInt(Loc, Val, 23, Type); - write16le(Loc, + checkInt(loc, val, 23, type); + write16le(loc, 0xf000 | // opcode - ((Val >> 12) & 0x07ff)); // imm11 - write16le(Loc + 2, - (read16le(Loc + 2) & 0xd000) | // opcode + ((val >> 12) & 0x07ff)); // imm11 + write16le(loc + 2, + (read16le(loc + 2) & 0xd000) | // opcode 0x2800 | // J1 == J2 == 1 - ((Val >> 1) & 0x07ff)); // imm11 + ((val >> 1) & 0x07ff)); // imm11 break; } // Fall through as rest of encoding is the same as B.W LLVM_FALLTHROUGH; case R_ARM_THM_JUMP24: // Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0 - checkInt(Loc, Val, 25, Type); - write16le(Loc, + checkInt(loc, val, 25, type); + write16le(loc, 0xf000 | // opcode - ((Val >> 14) & 0x0400) | // S - ((Val >> 12) & 0x03ff)); // imm10 - write16le(Loc + 2, - (read16le(Loc + 2) & 0xd000) | // opcode - (((~(Val >> 10)) ^ (Val >> 11)) & 0x2000) | // J1 - (((~(Val >> 11)) ^ (Val >> 13)) & 0x0800) | // J2 - ((Val >> 1) & 0x07ff)); // imm11 + ((val >> 14) & 0x0400) | // S + ((val >> 12) & 0x03ff)); // imm10 + write16le(loc + 2, + (read16le(loc + 2) & 0xd000) | // opcode + (((~(val >> 10)) ^ (val >> 11)) & 0x2000) | // J1 + (((~(val >> 11)) ^ (val >> 13)) & 0x0800) | // J2 + ((val >> 1) & 0x07ff)); // imm11 break; case R_ARM_MOVW_ABS_NC: case R_ARM_MOVW_PREL_NC: - write32le(Loc, (read32le(Loc) & ~0x000f0fff) | ((Val & 0xf000) << 4) | - (Val & 0x0fff)); + write32le(loc, (read32le(loc) & ~0x000f0fff) | ((val & 0xf000) << 4) | + (val & 0x0fff)); break; case R_ARM_MOVT_ABS: case R_ARM_MOVT_PREL: - write32le(Loc, (read32le(Loc) & ~0x000f0fff) | - (((Val >> 16) & 0xf000) << 4) | ((Val >> 16) & 0xfff)); + write32le(loc, (read32le(loc) & ~0x000f0fff) | + (((val >> 16) & 0xf000) << 4) | ((val >> 16) & 0xfff)); break; case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVT_PREL: // Encoding T1: A = imm4:i:imm3:imm8 - write16le(Loc, + write16le(loc, 0xf2c0 | // opcode - ((Val >> 17) & 0x0400) | // i - ((Val >> 28) & 0x000f)); // imm4 - write16le(Loc + 2, - (read16le(Loc + 2) & 0x8f00) | // opcode - ((Val >> 12) & 0x7000) | // imm3 - ((Val >> 16) & 0x00ff)); // imm8 + ((val >> 17) & 0x0400) | // i + ((val >> 28) & 0x000f)); // imm4 + write16le(loc + 2, + (read16le(loc + 2) & 0x8f00) | // opcode + ((val >> 12) & 0x7000) | // imm3 + ((val >> 16) & 0x00ff)); // imm8 break; case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVW_PREL_NC: // Encoding T3: A = imm4:i:imm3:imm8 - write16le(Loc, + write16le(loc, 0xf240 | // opcode - ((Val >> 1) & 0x0400) | // i - ((Val >> 12) & 0x000f)); // imm4 - write16le(Loc + 2, - (read16le(Loc + 2) & 0x8f00) | // opcode - ((Val << 4) & 0x7000) | // imm3 - (Val & 0x00ff)); // imm8 + ((val >> 1) & 0x0400) | // i + ((val >> 12) & 0x000f)); // imm4 + write16le(loc + 2, + (read16le(loc + 2) & 0x8f00) | // opcode + ((val << 4) & 0x7000) | // imm3 + (val & 0x00ff)); // imm8 break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } -int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const { - switch (Type) { +int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { default: return 0; case R_ARM_ABS32: case R_ARM_BASE_PREL: case R_ARM_GOTOFF32: case R_ARM_GOT_BREL: case R_ARM_GOT_PREL: case R_ARM_REL32: case R_ARM_TARGET1: case R_ARM_TARGET2: case R_ARM_TLS_GD32: case R_ARM_TLS_LDM32: case R_ARM_TLS_LDO32: case R_ARM_TLS_IE32: case R_ARM_TLS_LE32: - return SignExtend64<32>(read32le(Buf)); + return SignExtend64<32>(read32le(buf)); case R_ARM_PREL31: - return SignExtend64<31>(read32le(Buf)); + return SignExtend64<31>(read32le(buf)); case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_PC24: case R_ARM_PLT32: - return SignExtend64<26>(read32le(Buf) << 2); + return SignExtend64<26>(read32le(buf) << 2); case R_ARM_THM_JUMP11: - return SignExtend64<12>(read16le(Buf) << 1); + return SignExtend64<12>(read16le(buf) << 1); case R_ARM_THM_JUMP19: { // Encoding T3: A = S:J2:J1:imm10:imm6:0 - uint16_t Hi = read16le(Buf); - uint16_t Lo = read16le(Buf + 2); - return SignExtend64<20>(((Hi & 0x0400) << 10) | // S - ((Lo & 0x0800) << 8) | // J2 - ((Lo & 0x2000) << 5) | // J1 - ((Hi & 0x003f) << 12) | // imm6 - ((Lo & 0x07ff) << 1)); // imm11:0 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<20>(((hi & 0x0400) << 10) | // S + ((lo & 0x0800) << 8) | // J2 + ((lo & 0x2000) << 5) | // J1 + ((hi & 0x003f) << 12) | // imm6 + ((lo & 0x07ff) << 1)); // imm11:0 } case R_ARM_THM_CALL: - if (!Config->ARMJ1J2BranchEncoding) { + if (!config->armJ1J2BranchEncoding) { // Older Arm architectures do not support R_ARM_THM_JUMP24 and have // different encoding rules and range due to J1 and J2 always being 1. - uint16_t Hi = read16le(Buf); - uint16_t Lo = read16le(Buf + 2); - return SignExtend64<22>(((Hi & 0x7ff) << 12) | // imm11 - ((Lo & 0x7ff) << 1)); // imm11:0 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<22>(((hi & 0x7ff) << 12) | // imm11 + ((lo & 0x7ff) << 1)); // imm11:0 break; } LLVM_FALLTHROUGH; case R_ARM_THM_JUMP24: { // Encoding B T4, BL T1, BLX T2: A = S:I1:I2:imm10:imm11:0 // I1 = NOT(J1 EOR S), I2 = NOT(J2 EOR S) - uint16_t Hi = read16le(Buf); - uint16_t Lo = read16le(Buf + 2); - return SignExtend64<24>(((Hi & 0x0400) << 14) | // S - (~((Lo ^ (Hi << 3)) << 10) & 0x00800000) | // I1 - (~((Lo ^ (Hi << 1)) << 11) & 0x00400000) | // I2 - ((Hi & 0x003ff) << 12) | // imm0 - ((Lo & 0x007ff) << 1)); // imm11:0 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<24>(((hi & 0x0400) << 14) | // S + (~((lo ^ (hi << 3)) << 10) & 0x00800000) | // I1 + (~((lo ^ (hi << 1)) << 11) & 0x00400000) | // I2 + ((hi & 0x003ff) << 12) | // imm0 + ((lo & 0x007ff) << 1)); // imm11:0 } // ELF for the ARM Architecture 4.6.1.1 the implicit addend for MOVW and // MOVT is in the range -32768 <= A < 32768 case R_ARM_MOVW_ABS_NC: case R_ARM_MOVT_ABS: case R_ARM_MOVW_PREL_NC: case R_ARM_MOVT_PREL: { - uint64_t Val = read32le(Buf) & 0x000f0fff; - return SignExtend64<16>(((Val & 0x000f0000) >> 4) | (Val & 0x00fff)); + uint64_t val = read32le(buf) & 0x000f0fff; + return SignExtend64<16>(((val & 0x000f0000) >> 4) | (val & 0x00fff)); } case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: { // Encoding T3: A = imm4:i:imm3:imm8 - uint16_t Hi = read16le(Buf); - uint16_t Lo = read16le(Buf + 2); - return SignExtend64<16>(((Hi & 0x000f) << 12) | // imm4 - ((Hi & 0x0400) << 1) | // i - ((Lo & 0x7000) >> 4) | // imm3 - (Lo & 0x00ff)); // imm8 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<16>(((hi & 0x000f) << 12) | // imm4 + ((hi & 0x0400) << 1) | // i + ((lo & 0x7000) >> 4) | // imm3 + (lo & 0x00ff)); // imm8 } } } TargetInfo *elf::getARMTargetInfo() { - static ARM Target; - return &Target; + static ARM target; + return ⌖ } Index: vendor/lld/dist/ELF/Arch/AVR.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/AVR.cpp (revision 351287) +++ vendor/lld/dist/ELF/Arch/AVR.cpp (revision 351288) @@ -1,77 +1,76 @@ //===- AVR.cpp ------------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// // // AVR is a Harvard-architecture 8-bit micrcontroller designed for small // baremetal programs. All AVR-family processors have 32 8-bit registers. // The tiniest AVR has 32 byte RAM and 1 KiB program memory, and the largest // one supports up to 2^24 data address space and 2^22 code address space. // // Since it is a baremetal programming, there's usually no loader to load // ELF files on AVRs. You are expected to link your program against address // 0 and pull out a .text section from the result using objcopy, so that you // can write the linked code to on-chip flush memory. You can do that with // the following commands: // // ld.lld -Ttext=0 -o foo foo.o // objcopy -O binary --only-section=.text foo output.bin // // Note that the current AVR support is very preliminary so you can't // link any useful program yet, though. // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { class AVR final : public TargetInfo { public: AVR(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace -AVR::AVR() { NoneRel = R_AVR_NONE; } +AVR::AVR() { noneRel = R_AVR_NONE; } -RelExpr AVR::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { +RelExpr AVR::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { return R_ABS; } -void AVR::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_AVR_CALL: { - uint16_t Hi = Val >> 17; - uint16_t Lo = Val >> 1; - write16le(Loc, read16le(Loc) | ((Hi >> 1) << 4) | (Hi & 1)); - write16le(Loc + 2, Lo); + uint16_t hi = val >> 17; + uint16_t lo = val >> 1; + write16le(loc, read16le(loc) | ((hi >> 1) << 4) | (hi & 1)); + write16le(loc + 2, lo); break; } default: - error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } TargetInfo *elf::getAVRTargetInfo() { - static AVR Target; - return &Target; + static AVR target; + return ⌖ } Index: vendor/lld/dist/ELF/Arch/Hexagon.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/Hexagon.cpp (revision 351287) +++ vendor/lld/dist/ELF/Arch/Hexagon.cpp (revision 351288) @@ -1,292 +1,291 @@ //===-- Hexagon.cpp -------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { class Hexagon final : public TargetInfo { public: Hexagon(); uint32_t calcEFlags() const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; } // namespace Hexagon::Hexagon() { - PltRel = R_HEX_JMP_SLOT; - RelativeRel = R_HEX_RELATIVE; - GotRel = R_HEX_GLOB_DAT; - GotEntrySize = 4; + pltRel = R_HEX_JMP_SLOT; + relativeRel = R_HEX_RELATIVE; + gotRel = R_HEX_GLOB_DAT; + symbolicRel = R_HEX_32; + // The zero'th GOT entry is reserved for the address of _DYNAMIC. The // next 3 are reserved for the dynamic loader. - GotPltHeaderEntriesNum = 4; - GotPltEntrySize = 4; + gotPltHeaderEntriesNum = 4; - PltEntrySize = 16; - PltHeaderSize = 32; + pltEntrySize = 16; + pltHeaderSize = 32; // Hexagon Linux uses 64K pages by default. - DefaultMaxPageSize = 0x10000; - NoneRel = R_HEX_NONE; + defaultMaxPageSize = 0x10000; + noneRel = R_HEX_NONE; } uint32_t Hexagon::calcEFlags() const { - assert(!ObjectFiles.empty()); + assert(!objectFiles.empty()); // The architecture revision must always be equal to or greater than // greatest revision in the list of inputs. - uint32_t Ret = 0; - for (InputFile *F : ObjectFiles) { - uint32_t EFlags = cast>(F)->getObj().getHeader()->e_flags; - if (EFlags > Ret) - Ret = EFlags; + uint32_t ret = 0; + for (InputFile *f : objectFiles) { + uint32_t eflags = cast>(f)->getObj().getHeader()->e_flags; + if (eflags > ret) + ret = eflags; } - return Ret; + return ret; } -static uint32_t applyMask(uint32_t Mask, uint32_t Data) { - uint32_t Result = 0; - size_t Off = 0; +static uint32_t applyMask(uint32_t mask, uint32_t data) { + uint32_t result = 0; + size_t off = 0; - for (size_t Bit = 0; Bit != 32; ++Bit) { - uint32_t ValBit = (Data >> Off) & 1; - uint32_t MaskBit = (Mask >> Bit) & 1; - if (MaskBit) { - Result |= (ValBit << Bit); - ++Off; + for (size_t bit = 0; bit != 32; ++bit) { + uint32_t valBit = (data >> off) & 1; + uint32_t maskBit = (mask >> bit) & 1; + if (maskBit) { + result |= (valBit << bit); + ++off; } } - return Result; + return result; } -RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_HEX_B9_PCREL: case R_HEX_B9_PCREL_X: case R_HEX_B13_PCREL: case R_HEX_B15_PCREL: case R_HEX_B15_PCREL_X: case R_HEX_6_PCREL_X: case R_HEX_32_PCREL: return R_PC; case R_HEX_B22_PCREL: case R_HEX_PLT_B22_PCREL: case R_HEX_B22_PCREL_X: case R_HEX_B32_PCREL_X: return R_PLT_PC; case R_HEX_GOT_11_X: case R_HEX_GOT_16_X: case R_HEX_GOT_32_6_X: return R_HEXAGON_GOT; default: return R_ABS; } } -static uint32_t findMaskR6(uint32_t Insn) { +static uint32_t findMaskR6(uint32_t insn) { // There are (arguably too) many relocation masks for the DSP's // R_HEX_6_X type. The table below is used to select the correct mask // for the given instruction. struct InstructionMask { - uint32_t CmpMask; - uint32_t RelocMask; + uint32_t cmpMask; + uint32_t relocMask; }; - static const InstructionMask R6[] = { + static const InstructionMask r6[] = { {0x38000000, 0x0000201f}, {0x39000000, 0x0000201f}, {0x3e000000, 0x00001f80}, {0x3f000000, 0x00001f80}, {0x40000000, 0x000020f8}, {0x41000000, 0x000007e0}, {0x42000000, 0x000020f8}, {0x43000000, 0x000007e0}, {0x44000000, 0x000020f8}, {0x45000000, 0x000007e0}, {0x46000000, 0x000020f8}, {0x47000000, 0x000007e0}, {0x6a000000, 0x00001f80}, {0x7c000000, 0x001f2000}, {0x9a000000, 0x00000f60}, {0x9b000000, 0x00000f60}, {0x9c000000, 0x00000f60}, {0x9d000000, 0x00000f60}, {0x9f000000, 0x001f0100}, {0xab000000, 0x0000003f}, {0xad000000, 0x0000003f}, {0xaf000000, 0x00030078}, {0xd7000000, 0x006020e0}, {0xd8000000, 0x006020e0}, {0xdb000000, 0x006020e0}, {0xdf000000, 0x006020e0}}; // Duplex forms have a fixed mask and parse bits 15:14 are always // zero. Non-duplex insns will always have at least one bit set in the // parse field. - if ((0xC000 & Insn) == 0x0) + if ((0xC000 & insn) == 0x0) return 0x03f00000; - for (InstructionMask I : R6) - if ((0xff000000 & Insn) == I.CmpMask) - return I.RelocMask; + for (InstructionMask i : r6) + if ((0xff000000 & insn) == i.cmpMask) + return i.relocMask; error("unrecognized instruction for R_HEX_6 relocation: 0x" + - utohexstr(Insn)); + utohexstr(insn)); return 0; } -static uint32_t findMaskR8(uint32_t Insn) { - if ((0xff000000 & Insn) == 0xde000000) +static uint32_t findMaskR8(uint32_t insn) { + if ((0xff000000 & insn) == 0xde000000) return 0x00e020e8; - if ((0xff000000 & Insn) == 0x3c000000) + if ((0xff000000 & insn) == 0x3c000000) return 0x0000207f; return 0x00001fe0; } -static uint32_t findMaskR11(uint32_t Insn) { - if ((0xff000000 & Insn) == 0xa1000000) +static uint32_t findMaskR11(uint32_t insn) { + if ((0xff000000 & insn) == 0xa1000000) return 0x060020ff; return 0x06003fe0; } -static uint32_t findMaskR16(uint32_t Insn) { - if ((0xff000000 & Insn) == 0x48000000) +static uint32_t findMaskR16(uint32_t insn) { + if ((0xff000000 & insn) == 0x48000000) return 0x061f20ff; - if ((0xff000000 & Insn) == 0x49000000) + if ((0xff000000 & insn) == 0x49000000) return 0x061f3fe0; - if ((0xff000000 & Insn) == 0x78000000) + if ((0xff000000 & insn) == 0x78000000) return 0x00df3fe0; - if ((0xff000000 & Insn) == 0xb0000000) + if ((0xff000000 & insn) == 0xb0000000) return 0x0fe03fe0; error("unrecognized instruction for R_HEX_16_X relocation: 0x" + - utohexstr(Insn)); + utohexstr(insn)); return 0; } -static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); } +static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); } -void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_HEX_NONE: break; case R_HEX_6_PCREL_X: case R_HEX_6_X: - or32le(Loc, applyMask(findMaskR6(read32le(Loc)), Val)); + or32le(loc, applyMask(findMaskR6(read32le(loc)), val)); break; case R_HEX_8_X: - or32le(Loc, applyMask(findMaskR8(read32le(Loc)), Val)); + or32le(loc, applyMask(findMaskR8(read32le(loc)), val)); break; case R_HEX_9_X: - or32le(Loc, applyMask(0x00003fe0, Val & 0x3f)); + or32le(loc, applyMask(0x00003fe0, val & 0x3f)); break; case R_HEX_10_X: - or32le(Loc, applyMask(0x00203fe0, Val & 0x3f)); + or32le(loc, applyMask(0x00203fe0, val & 0x3f)); break; case R_HEX_11_X: case R_HEX_GOT_11_X: - or32le(Loc, applyMask(findMaskR11(read32le(Loc)), Val & 0x3f)); + or32le(loc, applyMask(findMaskR11(read32le(loc)), val & 0x3f)); break; case R_HEX_12_X: - or32le(Loc, applyMask(0x000007e0, Val)); + or32le(loc, applyMask(0x000007e0, val)); break; case R_HEX_16_X: // These relocs only have 6 effective bits. case R_HEX_GOT_16_X: - or32le(Loc, applyMask(findMaskR16(read32le(Loc)), Val & 0x3f)); + or32le(loc, applyMask(findMaskR16(read32le(loc)), val & 0x3f)); break; case R_HEX_32: case R_HEX_32_PCREL: - or32le(Loc, Val); + or32le(loc, val); break; case R_HEX_32_6_X: case R_HEX_GOT_32_6_X: - or32le(Loc, applyMask(0x0fff3fff, Val >> 6)); + or32le(loc, applyMask(0x0fff3fff, val >> 6)); break; case R_HEX_B9_PCREL: - or32le(Loc, applyMask(0x003000fe, Val >> 2)); + or32le(loc, applyMask(0x003000fe, val >> 2)); break; case R_HEX_B9_PCREL_X: - or32le(Loc, applyMask(0x003000fe, Val & 0x3f)); + or32le(loc, applyMask(0x003000fe, val & 0x3f)); break; case R_HEX_B13_PCREL: - or32le(Loc, applyMask(0x00202ffe, Val >> 2)); + or32le(loc, applyMask(0x00202ffe, val >> 2)); break; case R_HEX_B15_PCREL: - or32le(Loc, applyMask(0x00df20fe, Val >> 2)); + or32le(loc, applyMask(0x00df20fe, val >> 2)); break; case R_HEX_B15_PCREL_X: - or32le(Loc, applyMask(0x00df20fe, Val & 0x3f)); + or32le(loc, applyMask(0x00df20fe, val & 0x3f)); break; case R_HEX_B22_PCREL: case R_HEX_PLT_B22_PCREL: - or32le(Loc, applyMask(0x1ff3ffe, Val >> 2)); + or32le(loc, applyMask(0x1ff3ffe, val >> 2)); break; case R_HEX_B22_PCREL_X: - or32le(Loc, applyMask(0x1ff3ffe, Val & 0x3f)); + or32le(loc, applyMask(0x1ff3ffe, val & 0x3f)); break; case R_HEX_B32_PCREL_X: - or32le(Loc, applyMask(0x0fff3fff, Val >> 6)); + or32le(loc, applyMask(0x0fff3fff, val >> 6)); break; case R_HEX_HI16: - or32le(Loc, applyMask(0x00c03fff, Val >> 16)); + or32le(loc, applyMask(0x00c03fff, val >> 16)); break; case R_HEX_LO16: - or32le(Loc, applyMask(0x00c03fff, Val)); + or32le(loc, applyMask(0x00c03fff, val)); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); break; } } -void Hexagon::writePltHeader(uint8_t *Buf) const { - const uint8_t PltData[] = { +void Hexagon::writePltHeader(uint8_t *buf) const { + const uint8_t pltData[] = { 0x00, 0x40, 0x00, 0x00, // { immext (#0) 0x1c, 0xc0, 0x49, 0x6a, // r28 = add (pc, ##GOT0@PCREL) } # @GOT0 0x0e, 0x42, 0x9c, 0xe2, // { r14 -= add (r28, #16) # offset of GOTn 0x4f, 0x40, 0x9c, 0x91, // r15 = memw (r28 + #8) # object ID at GOT2 0x3c, 0xc0, 0x9c, 0x91, // r28 = memw (r28 + #4) }# dynamic link at GOT1 0x0e, 0x42, 0x0e, 0x8c, // { r14 = asr (r14, #2) # index of PLTn 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 } # call dynamic linker 0x0c, 0xdb, 0x00, 0x54, // trap0(#0xdb) # bring plt0 into 16byte alignment }; - memcpy(Buf, PltData, sizeof(PltData)); + memcpy(buf, pltData, sizeof(pltData)); // Offset from PLT0 to the GOT. - uint64_t Off = In.GotPlt->getVA() - In.Plt->getVA(); - relocateOne(Buf, R_HEX_B32_PCREL_X, Off); - relocateOne(Buf + 4, R_HEX_6_PCREL_X, Off); + uint64_t off = in.gotPlt->getVA() - in.plt->getVA(); + relocateOne(buf, R_HEX_B32_PCREL_X, off); + relocateOne(buf + 4, R_HEX_6_PCREL_X, off); } -void Hexagon::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Inst[] = { +void Hexagon::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t inst[] = { 0x00, 0x40, 0x00, 0x00, // { immext (#0) 0x0e, 0xc0, 0x49, 0x6a, // r14 = add (pc, ##GOTn@PCREL) } 0x1c, 0xc0, 0x8e, 0x91, // r28 = memw (r14) 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 }; - memcpy(Buf, Inst, sizeof(Inst)); + memcpy(buf, inst, sizeof(inst)); - relocateOne(Buf, R_HEX_B32_PCREL_X, GotPltEntryAddr - PltEntryAddr); - relocateOne(Buf + 4, R_HEX_6_PCREL_X, GotPltEntryAddr - PltEntryAddr); + relocateOne(buf, R_HEX_B32_PCREL_X, gotPltEntryAddr - pltEntryAddr); + relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr); } TargetInfo *elf::getHexagonTargetInfo() { - static Hexagon Target; - return &Target; + static Hexagon target; + return ⌖ } Index: vendor/lld/dist/ELF/Arch/MSP430.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/MSP430.cpp (revision 351287) +++ vendor/lld/dist/ELF/Arch/MSP430.cpp (revision 351288) @@ -1,94 +1,93 @@ //===- MSP430.cpp ---------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// // // The MSP430 is a 16-bit microcontroller RISC architecture. The instruction set // has only 27 core instructions orthogonally augmented with a variety // of addressing modes for source and destination operands. Entire address space // of MSP430 is 64KB (the extended MSP430X architecture is not considered here). // A typical MSP430 MCU has several kilobytes of RAM and ROM, plenty // of peripherals and is generally optimized for a low power consumption. // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { class MSP430 final : public TargetInfo { public: MSP430(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace MSP430::MSP430() { // mov.b #0, r3 - TrapInstr = {0x43, 0x43, 0x43, 0x43}; + trapInstr = {0x43, 0x43, 0x43, 0x43}; } -RelExpr MSP430::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr MSP430::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_MSP430_10_PCREL: case R_MSP430_16_PCREL: case R_MSP430_16_PCREL_BYTE: case R_MSP430_2X_PCREL: case R_MSP430_RL_PCREL: case R_MSP430_SYM_DIFF: return R_PC; default: return R_ABS; } } -void MSP430::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void MSP430::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_MSP430_8: - checkIntUInt(Loc, Val, 8, Type); - *Loc = Val; + checkIntUInt(loc, val, 8, type); + *loc = val; break; case R_MSP430_16: case R_MSP430_16_PCREL: case R_MSP430_16_BYTE: case R_MSP430_16_PCREL_BYTE: - checkIntUInt(Loc, Val, 16, Type); - write16le(Loc, Val); + checkIntUInt(loc, val, 16, type); + write16le(loc, val); break; case R_MSP430_32: - checkIntUInt(Loc, Val, 32, Type); - write32le(Loc, Val); + checkIntUInt(loc, val, 32, type); + write32le(loc, val); break; case R_MSP430_10_PCREL: { - int16_t Offset = ((int16_t)Val >> 1) - 1; - checkInt(Loc, Offset, 10, Type); - write16le(Loc, (read16le(Loc) & 0xFC00) | (Offset & 0x3FF)); + int16_t offset = ((int16_t)val >> 1) - 1; + checkInt(loc, offset, 10, type); + write16le(loc, (read16le(loc) & 0xFC00) | (offset & 0x3FF)); break; } default: - error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } TargetInfo *elf::getMSP430TargetInfo() { - static MSP430 Target; - return &Target; + static MSP430 target; + return ⌖ } Index: vendor/lld/dist/ELF/Arch/Mips.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/Mips.cpp (revision 351287) +++ vendor/lld/dist/ELF/Arch/Mips.cpp (revision 351288) @@ -1,676 +1,741 @@ //===- MIPS.cpp -----------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #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" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { template class MIPS final : public TargetInfo { public: MIPS(); 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, 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, 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; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + int64_t getImplicitAddend(const uint8_t *buf, 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, 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 template MIPS::MIPS() { - GotPltHeaderEntriesNum = 2; - DefaultMaxPageSize = 65536; - GotEntrySize = sizeof(typename ELFT::uint); - GotPltEntrySize = sizeof(typename ELFT::uint); - GotBaseSymInGotPlt = false; - PltEntrySize = 16; - PltHeaderSize = 32; - CopyRel = R_MIPS_COPY; - NoneRel = R_MIPS_NONE; - PltRel = R_MIPS_JUMP_SLOT; - NeedsThunks = true; + gotPltHeaderEntriesNum = 2; + defaultMaxPageSize = 65536; + gotBaseSymInGotPlt = false; + pltEntrySize = 16; + pltHeaderSize = 32; + copyRel = R_MIPS_COPY; + noneRel = R_MIPS_NONE; + pltRel = R_MIPS_JUMP_SLOT; + needsThunks = true; // Set `sigrie 1` as a trap instruction. - write32(TrapInstr.data(), 0x04170001); + write32(trapInstr.data(), 0x04170001); if (ELFT::Is64Bits) { - RelativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32; - TlsGotRel = R_MIPS_TLS_TPREL64; - TlsModuleIndexRel = R_MIPS_TLS_DTPMOD64; - TlsOffsetRel = R_MIPS_TLS_DTPREL64; + relativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32; + symbolicRel = R_MIPS_64; + tlsGotRel = R_MIPS_TLS_TPREL64; + tlsModuleIndexRel = R_MIPS_TLS_DTPMOD64; + tlsOffsetRel = R_MIPS_TLS_DTPREL64; } else { - RelativeRel = R_MIPS_REL32; - TlsGotRel = R_MIPS_TLS_TPREL32; - TlsModuleIndexRel = R_MIPS_TLS_DTPMOD32; - TlsOffsetRel = R_MIPS_TLS_DTPREL32; + relativeRel = R_MIPS_REL32; + symbolicRel = R_MIPS_32; + tlsGotRel = R_MIPS_TLS_TPREL32; + tlsModuleIndexRel = R_MIPS_TLS_DTPMOD32; + tlsOffsetRel = R_MIPS_TLS_DTPREL32; } } template uint32_t MIPS::calcEFlags() const { return calcMipsEFlags(); } template -RelExpr MIPS::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { +RelExpr MIPS::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { // See comment in the calculateMipsRelChain. - if (ELFT::Is64Bits || Config->MipsN32Abi) - Type &= 0xff; + if (ELFT::Is64Bits || config->mipsN32Abi) + type &= 0xff; - switch (Type) { + switch (type) { 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: // 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 // relocations as relative. - if (&S == ElfSym::MipsGpDisp) + if (&s == ElfSym::mipsGpDisp) return R_MIPS_GOT_GP_PC; - if (&S == ElfSym::MipsLocalGp) + 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_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: case R_MIPS_PC19_S2: case R_MIPS_PC21_S2: 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()) + 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_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: 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; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } -template RelType MIPS::getDynRel(RelType Type) const { - if (Type == R_MIPS_32 || Type == R_MIPS_64) - return RelativeRel; +template RelType MIPS::getDynRel(RelType type) const { + if (type == symbolicRel) + return type; return R_MIPS_NONE; } template -void MIPS::writeGotPlt(uint8_t *Buf, const Symbol &) const { - uint64_t VA = In.Plt->getVA(); +void MIPS::writeGotPlt(uint8_t *buf, const Symbol &) const { + uint64_t va = in.plt->getVA(); if (isMicroMips()) - VA |= 1; - write32(Buf, VA); + va |= 1; + write32(buf, va); } -template static uint32_t readShuffle(const uint8_t *Loc) { +template 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(Loc); + uint32_t v = read32(loc); if (E == support::little) - return (V << 16) | (V >> 16); - return V; + return (v << 16) | (v >> 16); + return v; } template -static void writeValue(uint8_t *Loc, uint64_t V, uint8_t BitsSize, - uint8_t Shift) { - uint32_t Instr = read32(Loc); - uint32_t Mask = 0xffffffff >> (32 - BitsSize); - uint32_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask); - write32(Loc, Data); +static void writeValue(uint8_t *loc, uint64_t v, uint8_t bitsSize, + uint8_t shift) { + uint32_t instr = read32(loc); + uint32_t mask = 0xffffffff >> (32 - bitsSize); + uint32_t data = (instr & ~mask) | ((v >> shift) & mask); + write32(loc, data); } template -static void writeShuffleValue(uint8_t *Loc, uint64_t V, uint8_t BitsSize, - uint8_t Shift) { +static void writeShuffleValue(uint8_t *loc, uint64_t v, uint8_t bitsSize, + uint8_t shift) { // See comments in readShuffle for purpose of this code. - uint16_t *Words = (uint16_t *)Loc; + uint16_t *words = (uint16_t *)loc; if (E == support::little) - std::swap(Words[0], Words[1]); + std::swap(words[0], words[1]); - writeValue(Loc, V, BitsSize, Shift); + writeValue(loc, v, bitsSize, shift); if (E == support::little) - std::swap(Words[0], Words[1]); + std::swap(words[0], words[1]); } template -static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize, - uint8_t Shift) { - uint16_t Instr = read16(Loc); - uint16_t Mask = 0xffff >> (16 - BitsSize); - uint16_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask); - write16(Loc, Data); +static void writeMicroRelocation16(uint8_t *loc, uint64_t v, uint8_t bitsSize, + uint8_t shift) { + uint16_t instr = read16(loc); + uint16_t mask = 0xffff >> (16 - bitsSize); + uint16_t data = (instr & ~mask) | ((v >> shift) & mask); + write16(loc, data); } -template void MIPS::writePltHeader(uint8_t *Buf) const { - const endianness E = ELFT::TargetEndianness; +template void MIPS::writePltHeader(uint8_t *buf) const { + const endianness e = ELFT::TargetEndianness; if (isMicroMips()) { - uint64_t GotPlt = In.GotPlt->getVA(); - uint64_t Plt = In.Plt->getVA(); + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); // Overwrite trap instructions written by Writer::writeTrapInstr. - memset(Buf, 0, PltHeaderSize); + memset(buf, 0, pltHeaderSize); - write16(Buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - . - write16(Buf + 4, 0xff23); // lw $25, 0($3) - write16(Buf + 8, 0x0535); // subu16 $2, $2, $3 - write16(Buf + 10, 0x2525); // srl16 $2, $2, 2 - write16(Buf + 12, 0x3302); // addiu $24, $2, -2 - write16(Buf + 14, 0xfffe); - write16(Buf + 16, 0x0dff); // move $15, $31 + write16(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - . + write16(buf + 4, 0xff23); // lw $25, 0($3) + write16(buf + 8, 0x0535); // subu16 $2, $2, $3 + write16(buf + 10, 0x2525); // srl16 $2, $2, 2 + write16(buf + 12, 0x3302); // addiu $24, $2, -2 + write16(buf + 14, 0xfffe); + write16(buf + 16, 0x0dff); // move $15, $31 if (isMipsR6()) { - write16(Buf + 18, 0x0f83); // move $28, $3 - write16(Buf + 20, 0x472b); // jalrc $25 - write16(Buf + 22, 0x0c00); // nop - relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPlt - Plt); + write16(buf + 18, 0x0f83); // move $28, $3 + write16(buf + 20, 0x472b); // jalrc $25 + write16(buf + 22, 0x0c00); // nop + relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt); } else { - write16(Buf + 18, 0x45f9); // jalrc $25 - write16(Buf + 20, 0x0f83); // move $28, $3 - write16(Buf + 22, 0x0c00); // nop - relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPlt - Plt); + write16(buf + 18, 0x45f9); // jalrc $25 + write16(buf + 20, 0x0f83); // move $28, $3 + write16(buf + 22, 0x0c00); // nop + relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt); } return; } - if (Config->MipsN32Abi) { - write32(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) - write32(Buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14) - write32(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) - write32(Buf + 12, 0x030ec023); // subu $24, $24, $14 - write32(Buf + 16, 0x03e07825); // move $15, $31 - write32(Buf + 20, 0x0018c082); // srl $24, $24, 2 + if (config->mipsN32Abi) { + write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14) + write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32(buf + 12, 0x030ec023); // subu $24, $24, $14 + write32(buf + 16, 0x03e07825); // move $15, $31 + write32(buf + 20, 0x0018c082); // srl $24, $24, 2 } else if (ELFT::Is64Bits) { - write32(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) - write32(Buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14) - write32(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) - write32(Buf + 12, 0x030ec023); // subu $24, $24, $14 - write32(Buf + 16, 0x03e07825); // move $15, $31 - write32(Buf + 20, 0x0018c0c2); // srl $24, $24, 3 + write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14) + write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32(buf + 12, 0x030ec023); // subu $24, $24, $14 + write32(buf + 16, 0x03e07825); // move $15, $31 + write32(buf + 20, 0x0018c0c2); // srl $24, $24, 3 } else { - write32(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) - write32(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) - write32(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) - write32(Buf + 12, 0x031cc023); // subu $24, $24, $28 - write32(Buf + 16, 0x03e07825); // move $15, $31 - write32(Buf + 20, 0x0018c082); // srl $24, $24, 2 + write32(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) + write32(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) + write32(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) + write32(buf + 12, 0x031cc023); // subu $24, $24, $28 + write32(buf + 16, 0x03e07825); // move $15, $31 + write32(buf + 20, 0x0018c082); // srl $24, $24, 2 } - uint32_t JalrInst = Config->ZHazardplt ? 0x0320fc09 : 0x0320f809; - write32(Buf + 24, JalrInst); // jalr.hb $25 or jalr $25 - write32(Buf + 28, 0x2718fffe); // subu $24, $24, 2 + uint32_t jalrInst = config->zHazardplt ? 0x0320fc09 : 0x0320f809; + write32(buf + 24, jalrInst); // jalr.hb $25 or jalr $25 + write32(buf + 28, 0x2718fffe); // subu $24, $24, 2 - uint64_t GotPlt = In.GotPlt->getVA(); - writeValue(Buf, GotPlt + 0x8000, 16, 16); - writeValue(Buf + 4, GotPlt, 16, 0); - writeValue(Buf + 8, GotPlt, 16, 0); + uint64_t gotPlt = in.gotPlt->getVA(); + writeValue(buf, gotPlt + 0x8000, 16, 16); + writeValue(buf + 4, gotPlt, 16, 0); + writeValue(buf + 8, gotPlt, 16, 0); } template -void MIPS::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const endianness E = ELFT::TargetEndianness; +void MIPS::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); + memset(buf, 0, pltEntrySize); if (isMipsR6()) { - write16(Buf, 0x7840); // addiupc $2, (GOTPLT) - . - write16(Buf + 4, 0xff22); // lw $25, 0($2) - write16(Buf + 8, 0x0f02); // move $24, $2 - write16(Buf + 10, 0x4723); // jrc $25 / jr16 $25 - relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPltEntryAddr - PltEntryAddr); + write16(buf, 0x7840); // addiupc $2, (GOTPLT) - . + write16(buf + 4, 0xff22); // lw $25, 0($2) + write16(buf + 8, 0x0f02); // move $24, $2 + write16(buf + 10, 0x4723); // jrc $25 / jr16 $25 + relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr); } else { - write16(Buf, 0x7900); // addiupc $2, (GOTPLT) - . - write16(Buf + 4, 0xff22); // lw $25, 0($2) - write16(Buf + 8, 0x4599); // jrc $25 / jr16 $25 - write16(Buf + 10, 0x0f02); // move $24, $2 - relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPltEntryAddr - PltEntryAddr); + write16(buf, 0x7900); // addiupc $2, (GOTPLT) - . + write16(buf + 4, 0xff22); // lw $25, 0($2) + write16(buf + 8, 0x4599); // jrc $25 / jr16 $25 + write16(buf + 10, 0x0f02); // move $24, $2 + relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr); } return; } - uint32_t JrInst = isMipsR6() ? (Config->ZHazardplt ? 0x03200409 : 0x03200009) - : (Config->ZHazardplt ? 0x03200408 : 0x03200008); + uint32_t loadInst = ELFT::Is64Bits ? 0xddf90000 : 0x8df90000; + uint32_t jrInst = isMipsR6() ? (config->zHazardplt ? 0x03200409 : 0x03200009) + : (config->zHazardplt ? 0x03200408 : 0x03200008); + uint32_t addInst = ELFT::Is64Bits ? 0x65f80000 : 0x25f80000; - write32(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry) - write32(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15) - write32(Buf + 8, JrInst); // jr $25 / jr.hb $25 - write32(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry) - writeValue(Buf, GotPltEntryAddr + 0x8000, 16, 16); - writeValue(Buf + 4, GotPltEntryAddr, 16, 0); - writeValue(Buf + 12, GotPltEntryAddr, 16, 0); + write32(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry) + write32(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15) + write32(buf + 8, jrInst); // jr $25 / jr.hb $25 + write32(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry) + writeValue(buf, gotPltEntryAddr + 0x8000, 16, 16); + writeValue(buf + 4, gotPltEntryAddr, 16, 0); + writeValue(buf + 12, gotPltEntryAddr, 16, 0); } template -bool MIPS::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const { +bool MIPS::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 && Type != R_MICROMIPS_26_S1 && - Type != R_MICROMIPS_PC26_S1) + if (type != R_MIPS_26 && type != R_MIPS_PC26_S2 && + type != R_MICROMIPS_26_S1 && type != R_MICROMIPS_PC26_S1) return false; - auto *F = dyn_cast_or_null>(File); - if (!F) + auto *f = dyn_cast_or_null>(file); + if (!f) return false; // If current file has PIC code, LA25 stub is not required. - if (F->getObj().getHeader()->e_flags & EF_MIPS_PIC) + if (f->getObj().getHeader()->e_flags & EF_MIPS_PIC) return false; - auto *D = dyn_cast(&S); + auto *d = dyn_cast(&s); // LA25 is required if target file has PIC code // or target symbol is a PIC symbol. - return D && isMipsPIC(D); + return d && isMipsPIC(d); } template -int64_t MIPS::getImplicitAddend(const uint8_t *Buf, RelType Type) const { - const endianness E = ELFT::TargetEndianness; - switch (Type) { +int64_t MIPS::getImplicitAddend(const uint8_t *buf, RelType type) const { + const endianness e = ELFT::TargetEndianness; + switch (type) { case R_MIPS_32: case R_MIPS_GPREL32: case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_TPREL32: - return SignExtend64<32>(read32(Buf)); + return SignExtend64<32>(read32(buf)); case R_MIPS_26: // 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(Buf) << 2); + return SignExtend64<28>(read32(buf) << 2); case R_MIPS_GOT16: case R_MIPS_HI16: case R_MIPS_PCHI16: - return SignExtend64<16>(read32(Buf)) << 16; + return SignExtend64<16>(read32(buf)) << 16; case R_MIPS_GPREL16: case R_MIPS_LO16: case R_MIPS_PCLO16: case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_DTPREL_LO16: case R_MIPS_TLS_TPREL_HI16: case R_MIPS_TLS_TPREL_LO16: - return SignExtend64<16>(read32(Buf)); + return SignExtend64<16>(read32(buf)); case R_MICROMIPS_GOT16: case R_MICROMIPS_HI16: - return SignExtend64<16>(readShuffle(Buf)) << 16; + return SignExtend64<16>(readShuffle(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(Buf)); + return SignExtend64<16>(readShuffle(buf)); case R_MICROMIPS_GPREL7_S2: - return SignExtend64<9>(readShuffle(Buf) << 2); + return SignExtend64<9>(readShuffle(buf) << 2); case R_MIPS_PC16: - return SignExtend64<18>(read32(Buf) << 2); + return SignExtend64<18>(read32(buf) << 2); case R_MIPS_PC19_S2: - return SignExtend64<21>(read32(Buf) << 2); + return SignExtend64<21>(read32(buf) << 2); case R_MIPS_PC21_S2: - return SignExtend64<23>(read32(Buf) << 2); + return SignExtend64<23>(read32(buf) << 2); case R_MIPS_PC26_S2: - return SignExtend64<28>(read32(Buf) << 2); + return SignExtend64<28>(read32(buf) << 2); case R_MIPS_PC32: - return SignExtend64<32>(read32(Buf)); + return SignExtend64<32>(read32(buf)); case R_MICROMIPS_26_S1: - return SignExtend64<27>(readShuffle(Buf) << 1); + return SignExtend64<27>(readShuffle(buf) << 1); case R_MICROMIPS_PC7_S1: - return SignExtend64<8>(read16(Buf) << 1); + return SignExtend64<8>(read16(buf) << 1); case R_MICROMIPS_PC10_S1: - return SignExtend64<11>(read16(Buf) << 1); + return SignExtend64<11>(read16(buf) << 1); case R_MICROMIPS_PC16_S1: - return SignExtend64<17>(readShuffle(Buf) << 1); + return SignExtend64<17>(readShuffle(buf) << 1); case R_MICROMIPS_PC18_S3: - return SignExtend64<21>(readShuffle(Buf) << 3); + return SignExtend64<21>(readShuffle(buf) << 3); case R_MICROMIPS_PC19_S2: - return SignExtend64<21>(readShuffle(Buf) << 2); + return SignExtend64<21>(readShuffle(buf) << 2); case R_MICROMIPS_PC21_S1: - return SignExtend64<22>(readShuffle(Buf) << 1); + return SignExtend64<22>(readShuffle(buf) << 1); case R_MICROMIPS_PC23_S2: - return SignExtend64<25>(readShuffle(Buf) << 2); + return SignExtend64<25>(readShuffle(buf) << 2); case R_MICROMIPS_PC26_S1: - return SignExtend64<27>(readShuffle(Buf) << 1); + return SignExtend64<27>(readShuffle(buf) << 1); default: return 0; } } static std::pair -calculateMipsRelChain(uint8_t *Loc, RelType 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, // we support two of them. That is allow to pass at least all LLVM // test suite cases. // / R_MIPS_SUB / R_MIPS_HI16 | R_MIPS_LO16 // / R_MIPS_64 / R_MIPS_NONE // The first relocation is a 'real' relocation which is calculated // using the corresponding symbol's value. The second and the third // 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 - 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); - error(getErrorLocation(Loc) + "unsupported relocations combination " + - Twine(Type)); - return std::make_pair(Type & 0xff, Val); + 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); + error(getErrorLocation(loc) + "unsupported relocations combination " + + Twine(type)); + return std::make_pair(type & 0xff, val); } +static bool isBranchReloc(RelType type) { + return type == R_MIPS_26 || type == R_MIPS_PC26_S2 || + type == R_MIPS_PC21_S2 || type == R_MIPS_PC16; +} + +static bool isMicroBranchReloc(RelType type) { + return type == R_MICROMIPS_26_S1 || type == R_MICROMIPS_PC16_S1 || + type == R_MICROMIPS_PC10_S1 || type == R_MICROMIPS_PC7_S1; +} + template -void MIPS::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - const endianness E = ELFT::TargetEndianness; +static uint64_t fixupCrossModeJump(uint8_t *loc, RelType type, uint64_t val) { + // Here we need to detect jump/branch from regular MIPS code + // to a microMIPS target and vice versa. In that cases jump + // instructions need to be replaced by their "cross-mode" + // equivalents. + const endianness e = ELFT::TargetEndianness; + bool isMicroTgt = val & 0x1; + bool isCrossJump = (isMicroTgt && isBranchReloc(type)) || + (!isMicroTgt && isMicroBranchReloc(type)); + if (!isCrossJump) + return val; - if (ELFT::Is64Bits || Config->MipsN32Abi) - std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val); + switch (type) { + case R_MIPS_26: { + uint32_t inst = read32(loc) >> 26; + if (inst == 0x3 || inst == 0x1d) { // JAL or JALX + writeValue(loc, 0x1d << 26, 32, 0); + return val; + } + break; + } + case R_MICROMIPS_26_S1: { + uint32_t inst = readShuffle(loc) >> 26; + if (inst == 0x3d || inst == 0x3c) { // JAL32 or JALX32 + val >>= 1; + writeShuffleValue(loc, 0x3c << 26, 32, 0); + return val; + } + break; + } + case R_MIPS_PC26_S2: + case R_MIPS_PC21_S2: + case R_MIPS_PC16: + case R_MICROMIPS_PC16_S1: + case R_MICROMIPS_PC10_S1: + case R_MICROMIPS_PC7_S1: + // FIXME (simon): Support valid branch relocations. + break; + default: + llvm_unreachable("unexpected jump/branch relocation"); + } + error(getErrorLocation(loc) + + "unsupported jump/branch instruction between ISA modes referenced by " + + toString(type) + " relocation"); + return val; +} + +template +void MIPS::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + const endianness e = ELFT::TargetEndianness; + + if (ELFT::Is64Bits || config->mipsN32Abi) + std::tie(type, val) = calculateMipsRelChain(loc, type, val); + + // Detect cross-mode jump/branch and fix instruction. + val = fixupCrossModeJump(loc, type, val); + // Thread pointer and DRP offsets from the start of TLS data area. // https://www.linux-mips.org/wiki/NPTL - if (Type == R_MIPS_TLS_DTPREL_HI16 || Type == R_MIPS_TLS_DTPREL_LO16 || - 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 || - Type == R_MICROMIPS_TLS_TPREL_HI16 || - Type == R_MICROMIPS_TLS_TPREL_LO16) { - Val -= 0x7000; + 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_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 || + type == R_MICROMIPS_TLS_TPREL_HI16 || + type == R_MICROMIPS_TLS_TPREL_LO16) { + val -= 0x7000; } - switch (Type) { + switch (type) { case R_MIPS_32: case R_MIPS_GPREL32: case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_TPREL32: - write32(Loc, Val); + write32(loc, val); break; case R_MIPS_64: case R_MIPS_TLS_DTPREL64: case R_MIPS_TLS_TPREL64: - write64(Loc, Val); + write64(loc, val); break; case R_MIPS_26: - writeValue(Loc, Val, 26, 2); + writeValue(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) { - writeValue(Loc, Val + 0x8000, 16, 16); + if (config->relocatable) { + writeValue(loc, val + 0x8000, 16, 16); } else { - checkInt(Loc, Val, 16, Type); - writeValue(Loc, Val, 16, 0); + checkInt(loc, val, 16, type); + writeValue(loc, val, 16, 0); } break; case R_MICROMIPS_GOT16: - if (Config->Relocatable) { - writeShuffleValue(Loc, Val + 0x8000, 16, 16); + if (config->relocatable) { + writeShuffleValue(loc, val + 0x8000, 16, 16); } else { - checkInt(Loc, Val, 16, Type); - writeShuffleValue(Loc, Val, 16, 0); + checkInt(loc, val, 16, type); + writeShuffleValue(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(Loc, Val, 16, Type); + checkInt(loc, val, 16, type); LLVM_FALLTHROUGH; 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_TPREL_LO16: - writeValue(Loc, Val, 16, 0); + writeValue(loc, val, 16, 0); break; case R_MICROMIPS_GPREL16: case R_MICROMIPS_TLS_GD: case R_MICROMIPS_TLS_LDM: - checkInt(Loc, Val, 16, Type); - writeShuffleValue(Loc, Val, 16, 0); + checkInt(loc, val, 16, type); + writeShuffleValue(loc, val, 16, 0); break; case R_MICROMIPS_CALL16: case R_MICROMIPS_CALL_LO16: case R_MICROMIPS_LO16: case R_MICROMIPS_TLS_DTPREL_LO16: case R_MICROMIPS_TLS_GOTTPREL: case R_MICROMIPS_TLS_TPREL_LO16: - writeShuffleValue(Loc, Val, 16, 0); + writeShuffleValue(loc, val, 16, 0); break; case R_MICROMIPS_GPREL7_S2: - checkInt(Loc, Val, 7, Type); - writeShuffleValue(Loc, Val, 7, 2); + checkInt(loc, val, 7, type); + writeShuffleValue(loc, val, 7, 2); break; case R_MIPS_CALL_HI16: case R_MIPS_GOT_HI16: case R_MIPS_HI16: case R_MIPS_PCHI16: case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_TPREL_HI16: - writeValue(Loc, Val + 0x8000, 16, 16); + writeValue(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: - writeShuffleValue(Loc, Val + 0x8000, 16, 16); + writeShuffleValue(loc, val + 0x8000, 16, 16); break; case R_MIPS_HIGHER: - writeValue(Loc, Val + 0x80008000, 16, 32); + writeValue(loc, val + 0x80008000, 16, 32); break; case R_MIPS_HIGHEST: - writeValue(Loc, Val + 0x800080008000, 16, 48); + writeValue(loc, val + 0x800080008000, 16, 48); break; case R_MIPS_JALR: case R_MICROMIPS_JALR: // Ignore this optimization relocation for now break; case R_MIPS_PC16: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 18, Type); - writeValue(Loc, Val, 16, 2); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 18, type); + writeValue(loc, val, 16, 2); break; case R_MIPS_PC19_S2: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 21, Type); - writeValue(Loc, Val, 19, 2); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 21, type); + writeValue(loc, val, 19, 2); break; case R_MIPS_PC21_S2: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 23, Type); - writeValue(Loc, Val, 21, 2); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 23, type); + writeValue(loc, val, 21, 2); break; case R_MIPS_PC26_S2: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 28, Type); - writeValue(Loc, Val, 26, 2); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 28, type); + writeValue(loc, val, 26, 2); break; case R_MIPS_PC32: - writeValue(Loc, Val, 32, 0); + writeValue(loc, val, 32, 0); break; case R_MICROMIPS_26_S1: case R_MICROMIPS_PC26_S1: - checkInt(Loc, Val, 27, Type); - writeShuffleValue(Loc, Val, 26, 1); + checkInt(loc, val, 27, type); + writeShuffleValue(loc, val, 26, 1); break; case R_MICROMIPS_PC7_S1: - checkInt(Loc, Val, 8, Type); - writeMicroRelocation16(Loc, Val, 7, 1); + checkInt(loc, val, 8, type); + writeMicroRelocation16(loc, val, 7, 1); break; case R_MICROMIPS_PC10_S1: - checkInt(Loc, Val, 11, Type); - writeMicroRelocation16(Loc, Val, 10, 1); + checkInt(loc, val, 11, type); + writeMicroRelocation16(loc, val, 10, 1); break; case R_MICROMIPS_PC16_S1: - checkInt(Loc, Val, 17, Type); - writeShuffleValue(Loc, Val, 16, 1); + checkInt(loc, val, 17, type); + writeShuffleValue(loc, val, 16, 1); break; case R_MICROMIPS_PC18_S3: - checkInt(Loc, Val, 21, Type); - writeShuffleValue(Loc, Val, 18, 3); + checkInt(loc, val, 21, type); + writeShuffleValue(loc, val, 18, 3); break; case R_MICROMIPS_PC19_S2: - checkInt(Loc, Val, 21, Type); - writeShuffleValue(Loc, Val, 19, 2); + checkInt(loc, val, 21, type); + writeShuffleValue(loc, val, 19, 2); break; case R_MICROMIPS_PC21_S1: - checkInt(Loc, Val, 22, Type); - writeShuffleValue(Loc, Val, 21, 1); + checkInt(loc, val, 22, type); + writeShuffleValue(loc, val, 21, 1); break; case R_MICROMIPS_PC23_S2: - checkInt(Loc, Val, 25, Type); - writeShuffleValue(Loc, Val, 23, 2); + checkInt(loc, val, 25, type); + writeShuffleValue(loc, val, 23, 2); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -template bool MIPS::usesOnlyLowPageBits(RelType Type) const { - return Type == R_MIPS_LO16 || Type == R_MIPS_GOT_OFST || - Type == R_MICROMIPS_LO16; +template bool MIPS::usesOnlyLowPageBits(RelType type) const { + return type == R_MIPS_LO16 || type == R_MIPS_GOT_OFST || + type == R_MICROMIPS_LO16; } // Return true if the symbol is a PIC function. -template bool elf::isMipsPIC(const Defined *Sym) { - if (!Sym->isFunc()) +template bool elf::isMipsPIC(const Defined *sym) { + if (!sym->isFunc()) return false; - if (Sym->StOther & STO_MIPS_PIC) + if (sym->stOther & STO_MIPS_PIC) return true; - if (!Sym->Section) + if (!sym->section) return false; - ObjFile *File = - cast(Sym->Section)->template getFile(); - if (!File) + ObjFile *file = + cast(sym->section)->template getFile(); + if (!file) return false; - return File->getObj().getHeader()->e_flags & EF_MIPS_PIC; + return file->getObj().getHeader()->e_flags & EF_MIPS_PIC; } template TargetInfo *elf::getMipsTargetInfo() { - static MIPS Target; - return &Target; + static MIPS target; + return ⌖ } template TargetInfo *elf::getMipsTargetInfo(); template TargetInfo *elf::getMipsTargetInfo(); template TargetInfo *elf::getMipsTargetInfo(); template TargetInfo *elf::getMipsTargetInfo(); template bool elf::isMipsPIC(const Defined *); template bool elf::isMipsPIC(const Defined *); template bool elf::isMipsPIC(const Defined *); template bool elf::isMipsPIC(const Defined *); Index: vendor/lld/dist/ELF/Arch/MipsArchTree.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/MipsArchTree.cpp (revision 351287) +++ vendor/lld/dist/ELF/Arch/MipsArchTree.cpp (revision 351288) @@ -1,390 +1,389 @@ //===- MipsArchTree.cpp --------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===---------------------------------------------------------------------===// // // This file contains a helper function for the Writer. // //===---------------------------------------------------------------------===// #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" using namespace llvm; using namespace llvm::object; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { struct ArchTreeEdge { - uint32_t Child; - uint32_t Parent; + uint32_t child; + uint32_t parent; }; struct FileFlags { - InputFile *File; - uint32_t Flags; + InputFile *file; + uint32_t flags; }; } // namespace -static StringRef getAbiName(uint32_t Flags) { - switch (Flags) { +static StringRef getAbiName(uint32_t flags) { + switch (flags) { case 0: return "n64"; case EF_MIPS_ABI2: return "n32"; case EF_MIPS_ABI_O32: return "o32"; case EF_MIPS_ABI_O64: return "o64"; case EF_MIPS_ABI_EABI32: return "eabi32"; case EF_MIPS_ABI_EABI64: return "eabi64"; default: return "unknown"; } } -static StringRef getNanName(bool IsNan2008) { - return IsNan2008 ? "2008" : "legacy"; +static StringRef getNanName(bool isNan2008) { + return isNan2008 ? "2008" : "legacy"; } -static StringRef getFpName(bool IsFp64) { return IsFp64 ? "64" : "32"; } +static StringRef getFpName(bool isFp64) { return isFp64 ? "64" : "32"; } -static void checkFlags(ArrayRef Files) { - assert(!Files.empty() && "expected non-empty file list"); +static void checkFlags(ArrayRef files) { + assert(!files.empty() && "expected non-empty file list"); - uint32_t ABI = Files[0].Flags & (EF_MIPS_ABI | EF_MIPS_ABI2); - bool Nan = Files[0].Flags & EF_MIPS_NAN2008; - bool Fp = Files[0].Flags & EF_MIPS_FP64; + uint32_t abi = files[0].flags & (EF_MIPS_ABI | EF_MIPS_ABI2); + bool nan = files[0].flags & EF_MIPS_NAN2008; + bool fp = files[0].flags & EF_MIPS_FP64; - for (const FileFlags &F : Files) { - if (Config->Is64 && F.Flags & EF_MIPS_MICROMIPS) - error(toString(F.File) + ": microMIPS 64-bit is not supported"); + for (const FileFlags &f : files) { + if (config->is64 && f.flags & EF_MIPS_MICROMIPS) + error(toString(f.file) + ": microMIPS 64-bit is not supported"); - uint32_t ABI2 = F.Flags & (EF_MIPS_ABI | EF_MIPS_ABI2); - if (ABI != ABI2) - error(toString(F.File) + ": ABI '" + getAbiName(ABI2) + - "' is incompatible with target ABI '" + getAbiName(ABI) + "'"); + uint32_t abi2 = f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2); + if (abi != abi2) + error(toString(f.file) + ": ABI '" + getAbiName(abi2) + + "' is incompatible with target ABI '" + getAbiName(abi) + "'"); - bool Nan2 = F.Flags & EF_MIPS_NAN2008; - if (Nan != Nan2) - error(toString(F.File) + ": -mnan=" + getNanName(Nan2) + - " is incompatible with target -mnan=" + getNanName(Nan)); + bool nan2 = f.flags & EF_MIPS_NAN2008; + if (nan != nan2) + error(toString(f.file) + ": -mnan=" + getNanName(nan2) + + " is incompatible with target -mnan=" + getNanName(nan)); - bool Fp2 = F.Flags & EF_MIPS_FP64; - if (Fp != Fp2) - error(toString(F.File) + ": -mfp" + getFpName(Fp2) + - " is incompatible with target -mfp" + getFpName(Fp)); + bool fp2 = f.flags & EF_MIPS_FP64; + if (fp != fp2) + error(toString(f.file) + ": -mfp" + getFpName(fp2) + + " is incompatible with target -mfp" + getFpName(fp)); } } -static uint32_t getMiscFlags(ArrayRef Files) { - uint32_t Ret = 0; - for (const FileFlags &F : Files) - Ret |= F.Flags & +static uint32_t getMiscFlags(ArrayRef files) { + uint32_t ret = 0; + for (const FileFlags &f : files) + ret |= f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2 | EF_MIPS_ARCH_ASE | EF_MIPS_NOREORDER | EF_MIPS_MICROMIPS | EF_MIPS_NAN2008 | EF_MIPS_32BITMODE); - return Ret; + return ret; } -static uint32_t getPicFlags(ArrayRef Files) { +static uint32_t getPicFlags(ArrayRef files) { // Check PIC/non-PIC compatibility. - bool IsPic = Files[0].Flags & (EF_MIPS_PIC | EF_MIPS_CPIC); - for (const FileFlags &F : Files.slice(1)) { - bool IsPic2 = F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC); - if (IsPic && !IsPic2) - warn(toString(F.File) + + bool isPic = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + for (const FileFlags &f : files.slice(1)) { + bool isPic2 = f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + if (isPic && !isPic2) + warn(toString(f.file) + ": linking non-abicalls code with abicalls code " + - toString(Files[0].File)); - if (!IsPic && IsPic2) - warn(toString(F.File) + + toString(files[0].file)); + if (!isPic && isPic2) + warn(toString(f.file) + ": linking abicalls code with non-abicalls code " + - toString(Files[0].File)); + toString(files[0].file)); } // Compute the result PIC/non-PIC flag. - uint32_t Ret = Files[0].Flags & (EF_MIPS_PIC | EF_MIPS_CPIC); - for (const FileFlags &F : Files.slice(1)) - Ret &= F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + uint32_t ret = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + for (const FileFlags &f : files.slice(1)) + ret &= f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC); // PIC code is inherently CPIC and may not set CPIC flag explicitly. - if (Ret & EF_MIPS_PIC) - Ret |= EF_MIPS_CPIC; - return Ret; + if (ret & EF_MIPS_PIC) + ret |= EF_MIPS_CPIC; + return ret; } -static ArchTreeEdge ArchTree[] = { +static ArchTreeEdge archTree[] = { // MIPS32R6 and MIPS64R6 are not compatible with other extensions // MIPS64R2 extensions. {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, EF_MIPS_ARCH_64R2}, {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON2, EF_MIPS_ARCH_64R2}, {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, EF_MIPS_ARCH_64R2}, {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_LS3A, EF_MIPS_ARCH_64R2}, // MIPS64 extensions. {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, EF_MIPS_ARCH_64}, {EF_MIPS_ARCH_64 | EF_MIPS_MACH_XLR, EF_MIPS_ARCH_64}, {EF_MIPS_ARCH_64R2, EF_MIPS_ARCH_64}, // MIPS V extensions. {EF_MIPS_ARCH_64, EF_MIPS_ARCH_5}, // R5000 extensions. {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5500, EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400}, // MIPS IV extensions. {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400, EF_MIPS_ARCH_4}, {EF_MIPS_ARCH_4 | EF_MIPS_MACH_9000, EF_MIPS_ARCH_4}, {EF_MIPS_ARCH_5, EF_MIPS_ARCH_4}, // VR4100 extensions. {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4111, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100}, {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4120, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100}, // MIPS III extensions. {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4010, EF_MIPS_ARCH_3}, {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100, EF_MIPS_ARCH_3}, {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4650, EF_MIPS_ARCH_3}, {EF_MIPS_ARCH_3 | EF_MIPS_MACH_5900, EF_MIPS_ARCH_3}, {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2E, EF_MIPS_ARCH_3}, {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2F, EF_MIPS_ARCH_3}, {EF_MIPS_ARCH_4, EF_MIPS_ARCH_3}, // MIPS32 extensions. {EF_MIPS_ARCH_32R2, EF_MIPS_ARCH_32}, // MIPS II extensions. {EF_MIPS_ARCH_3, EF_MIPS_ARCH_2}, {EF_MIPS_ARCH_32, EF_MIPS_ARCH_2}, // MIPS I extensions. {EF_MIPS_ARCH_1 | EF_MIPS_MACH_3900, EF_MIPS_ARCH_1}, {EF_MIPS_ARCH_2, EF_MIPS_ARCH_1}, }; -static bool isArchMatched(uint32_t New, uint32_t Res) { - if (New == Res) +static bool isArchMatched(uint32_t New, uint32_t res) { + if (New == res) return true; - if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, Res)) + if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res)) return true; - if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, Res)) + if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res)) return true; - for (const auto &Edge : ArchTree) { - if (Res == Edge.Child) { - Res = Edge.Parent; - if (Res == New) + for (const auto &edge : archTree) { + if (res == edge.child) { + res = edge.parent; + if (res == New) return true; } } return false; } -static StringRef getMachName(uint32_t Flags) { - switch (Flags & EF_MIPS_MACH) { +static StringRef getMachName(uint32_t flags) { + switch (flags & EF_MIPS_MACH) { case EF_MIPS_MACH_NONE: return ""; case EF_MIPS_MACH_3900: return "r3900"; case EF_MIPS_MACH_4010: return "r4010"; case EF_MIPS_MACH_4100: return "r4100"; case EF_MIPS_MACH_4650: return "r4650"; case EF_MIPS_MACH_4120: return "r4120"; case EF_MIPS_MACH_4111: return "r4111"; case EF_MIPS_MACH_5400: return "vr5400"; case EF_MIPS_MACH_5900: return "vr5900"; case EF_MIPS_MACH_5500: return "vr5500"; case EF_MIPS_MACH_9000: return "rm9000"; case EF_MIPS_MACH_LS2E: return "loongson2e"; case EF_MIPS_MACH_LS2F: return "loongson2f"; case EF_MIPS_MACH_LS3A: return "loongson3a"; case EF_MIPS_MACH_OCTEON: return "octeon"; case EF_MIPS_MACH_OCTEON2: return "octeon2"; case EF_MIPS_MACH_OCTEON3: return "octeon3"; case EF_MIPS_MACH_SB1: return "sb1"; case EF_MIPS_MACH_XLR: return "xlr"; default: return "unknown machine"; } } -static StringRef getArchName(uint32_t Flags) { - switch (Flags & EF_MIPS_ARCH) { +static StringRef getArchName(uint32_t flags) { + switch (flags & EF_MIPS_ARCH) { case EF_MIPS_ARCH_1: return "mips1"; case EF_MIPS_ARCH_2: return "mips2"; case EF_MIPS_ARCH_3: return "mips3"; case EF_MIPS_ARCH_4: return "mips4"; case EF_MIPS_ARCH_5: return "mips5"; case EF_MIPS_ARCH_32: return "mips32"; case EF_MIPS_ARCH_64: return "mips64"; case EF_MIPS_ARCH_32R2: return "mips32r2"; case EF_MIPS_ARCH_64R2: return "mips64r2"; case EF_MIPS_ARCH_32R6: return "mips32r6"; case EF_MIPS_ARCH_64R6: return "mips64r6"; default: return "unknown arch"; } } -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(); +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, // these input files are compatible. In that case we need to return "highest" // ISA. If there are incompatible input files, we show an error. // For example, mips1 is a "parent" of mips2 and such files are compatible. // Output file gets EF_MIPS_ARCH_2 flag. From the other side mips3 and mips32 // are incompatible because nor mips3 is a parent for misp32, nor mips32 // is a parent for mips3. -static uint32_t getArchFlags(ArrayRef Files) { - uint32_t Ret = Files[0].Flags & (EF_MIPS_ARCH | EF_MIPS_MACH); +static uint32_t getArchFlags(ArrayRef files) { + uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH); - for (const FileFlags &F : Files.slice(1)) { - uint32_t New = F.Flags & (EF_MIPS_ARCH | EF_MIPS_MACH); + for (const FileFlags &f : files.slice(1)) { + uint32_t New = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH); // Check ISA compatibility. - if (isArchMatched(New, Ret)) + if (isArchMatched(New, ret)) continue; - if (!isArchMatched(Ret, New)) { - error("incompatible target ISA:\n>>> " + toString(Files[0].File) + ": " + - getFullArchName(Ret) + "\n>>> " + toString(F.File) + ": " + + if (!isArchMatched(ret, New)) { + error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " + + getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " + getFullArchName(New)); return 0; } - Ret = New; + ret = New; } - return Ret; + return ret; } template uint32_t elf::calcMipsEFlags() { - std::vector V; - for (InputFile *F : ObjectFiles) - V.push_back({F, cast>(F)->getObj().getHeader()->e_flags}); - if (V.empty()) + std::vector v; + for (InputFile *f : objectFiles) + v.push_back({f, cast>(f)->getObj().getHeader()->e_flags}); + if (v.empty()) return 0; - checkFlags(V); - return getMiscFlags(V) | getPicFlags(V) | getArchFlags(V); + checkFlags(v); + return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v); } -static int compareMipsFpAbi(uint8_t FpA, uint8_t FpB) { - if (FpA == FpB) +static int compareMipsFpAbi(uint8_t fpA, uint8_t fpB) { + if (fpA == fpB) return 0; - if (FpB == Mips::Val_GNU_MIPS_ABI_FP_ANY) + if (fpB == Mips::Val_GNU_MIPS_ABI_FP_ANY) return 1; - if (FpB == Mips::Val_GNU_MIPS_ABI_FP_64A && - FpA == Mips::Val_GNU_MIPS_ABI_FP_64) + if (fpB == Mips::Val_GNU_MIPS_ABI_FP_64A && + fpA == Mips::Val_GNU_MIPS_ABI_FP_64) return 1; - if (FpB != Mips::Val_GNU_MIPS_ABI_FP_XX) + if (fpB != Mips::Val_GNU_MIPS_ABI_FP_XX) return -1; - if (FpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE || - FpA == Mips::Val_GNU_MIPS_ABI_FP_64 || - FpA == Mips::Val_GNU_MIPS_ABI_FP_64A) + if (fpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE || + fpA == Mips::Val_GNU_MIPS_ABI_FP_64 || + fpA == Mips::Val_GNU_MIPS_ABI_FP_64A) return 1; return -1; } -static StringRef getMipsFpAbiName(uint8_t FpAbi) { - switch (FpAbi) { +static StringRef getMipsFpAbiName(uint8_t fpAbi) { + switch (fpAbi) { case Mips::Val_GNU_MIPS_ABI_FP_ANY: return "any"; case Mips::Val_GNU_MIPS_ABI_FP_DOUBLE: return "-mdouble-float"; case Mips::Val_GNU_MIPS_ABI_FP_SINGLE: return "-msingle-float"; case Mips::Val_GNU_MIPS_ABI_FP_SOFT: return "-msoft-float"; case Mips::Val_GNU_MIPS_ABI_FP_OLD_64: return "-mgp32 -mfp64 (old)"; case Mips::Val_GNU_MIPS_ABI_FP_XX: return "-mfpxx"; case Mips::Val_GNU_MIPS_ABI_FP_64: return "-mgp32 -mfp64"; case Mips::Val_GNU_MIPS_ABI_FP_64A: return "-mgp32 -mfp64 -mno-odd-spreg"; default: return "unknown"; } } -uint8_t elf::getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag, - StringRef FileName) { - if (compareMipsFpAbi(NewFlag, OldFlag) >= 0) - return NewFlag; - if (compareMipsFpAbi(OldFlag, NewFlag) < 0) - error(FileName + ": floating point ABI '" + getMipsFpAbiName(NewFlag) + +uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, + StringRef fileName) { + if (compareMipsFpAbi(newFlag, oldFlag) >= 0) + return newFlag; + if (compareMipsFpAbi(oldFlag, newFlag) < 0) + error(fileName + ": floating point ABI '" + getMipsFpAbiName(newFlag) + "' is incompatible with target floating point ABI '" + - getMipsFpAbiName(OldFlag) + "'"); - return OldFlag; + getMipsFpAbiName(oldFlag) + "'"); + return oldFlag; } -template static bool isN32Abi(const InputFile *F) { - if (auto *EF = dyn_cast>(F)) - return EF->getObj().getHeader()->e_flags & EF_MIPS_ABI2; +template static bool isN32Abi(const InputFile *f) { + if (auto *ef = dyn_cast(f)) + return ef->template getObj().getHeader()->e_flags & EF_MIPS_ABI2; return false; } -bool elf::isMipsN32Abi(const InputFile *F) { - switch (Config->EKind) { +bool elf::isMipsN32Abi(const InputFile *f) { + switch (config->ekind) { case ELF32LEKind: - return isN32Abi(F); + return isN32Abi(f); case ELF32BEKind: - return isN32Abi(F); + return isN32Abi(f); case ELF64LEKind: - return isN32Abi(F); + return isN32Abi(f); case ELF64BEKind: - return isN32Abi(F); + return isN32Abi(f); default: llvm_unreachable("unknown Config->EKind"); } } -bool elf::isMicroMips() { return Config->EFlags & EF_MIPS_MICROMIPS; } +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; + uint32_t arch = config->eflags & EF_MIPS_ARCH; + return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6; } template uint32_t elf::calcMipsEFlags(); template uint32_t elf::calcMipsEFlags(); template uint32_t elf::calcMipsEFlags(); template uint32_t elf::calcMipsEFlags(); Index: vendor/lld/dist/ELF/Arch/PPC.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/PPC.cpp (revision 351287) +++ vendor/lld/dist/ELF/Arch/PPC.cpp (revision 351288) @@ -1,81 +1,432 @@ //===- PPC.cpp ------------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// +#include "OutputSections.h" #include "Symbols.h" +#include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { class PPC final : public TargetInfo { public: PPC(); - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writeGotHeader(uint8_t *buf) const override; + void writePltHeader(uint8_t *buf) const override { + llvm_unreachable("should call writePPC32GlinkSection() instead"); + } + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override { + llvm_unreachable("should call writePPC32GlinkSection() instead"); + } + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + bool needsThunk(RelExpr expr, RelType relocType, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; + uint32_t getThunkSectionSpacing() const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + int getTlsGdRelaxSkip(RelType type) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace +static uint16_t lo(uint32_t v) { return v; } +static uint16_t ha(uint32_t v) { return (v + 0x8000) >> 16; } + +static uint32_t readFromHalf16(const uint8_t *loc) { + return read32(config->isLE ? loc : loc - 2); +} + +static void writeFromHalf16(uint8_t *loc, uint32_t insn) { + write32(config->isLE ? loc : loc - 2, insn); +} + +void elf::writePPC32GlinkSection(uint8_t *buf, size_t numEntries) { + // On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an + // absolute address from a specific .plt slot (usually called .got.plt on + // other targets) and jumps there. + // + // a) With immediate binding (BIND_NOW), the .plt entry is resolved at load + // time. The .glink section is not used. + // b) With lazy binding, the .plt entry points to a `b PLTresolve` + // instruction in .glink, filled in by PPC::writeGotPlt(). + + // Write N `b PLTresolve` first. + for (size_t i = 0; i != numEntries; ++i) + write32(buf + 4 * i, 0x48000000 | 4 * (numEntries - i)); + buf += 4 * numEntries; + + // Then write PLTresolve(), which has two forms: PIC and non-PIC. PLTresolve() + // computes the PLT index (by computing the distance from the landing b to + // itself) and calls _dl_runtime_resolve() (in glibc). + uint32_t got = in.got->getVA(); + uint32_t glink = in.plt->getVA(); // VA of .glink + const uint8_t *end = buf + 64; + if (config->isPic) { + uint32_t afterBcl = in.plt->getSize() - target->pltHeaderSize + 12; + uint32_t gotBcl = got + 4 - (glink + afterBcl); + write32(buf + 0, 0x3d6b0000 | ha(afterBcl)); // addis r11,r11,1f-glink@ha + write32(buf + 4, 0x7c0802a6); // mflr r0 + write32(buf + 8, 0x429f0005); // bcl 20,30,.+4 + write32(buf + 12, 0x396b0000 | lo(afterBcl)); // 1: addi r11,r11,1b-.glink@l + write32(buf + 16, 0x7d8802a6); // mflr r12 + write32(buf + 20, 0x7c0803a6); // mtlr r0 + write32(buf + 24, 0x7d6c5850); // sub r11,r11,r12 + write32(buf + 28, 0x3d8c0000 | ha(gotBcl)); // addis 12,12,GOT+4-1b@ha + if (ha(gotBcl) == ha(gotBcl + 4)) { + write32(buf + 32, 0x800c0000 | lo(gotBcl)); // lwz r0,r12,GOT+4-1b@l(r12) + write32(buf + 36, + 0x818c0000 | lo(gotBcl + 4)); // lwz r12,r12,GOT+8-1b@l(r12) + } else { + write32(buf + 32, 0x840c0000 | lo(gotBcl)); // lwzu r0,r12,GOT+4-1b@l(r12) + write32(buf + 36, 0x818c0000 | 4); // lwz r12,r12,4(r12) + } + write32(buf + 40, 0x7c0903a6); // mtctr 0 + write32(buf + 44, 0x7c0b5a14); // add r0,11,11 + write32(buf + 48, 0x7d605a14); // add r11,0,11 + write32(buf + 52, 0x4e800420); // bctr + buf += 56; + } else { + write32(buf + 0, 0x3d800000 | ha(got + 4)); // lis r12,GOT+4@ha + write32(buf + 4, 0x3d6b0000 | ha(-glink)); // addis r11,r11,-Glink@ha + if (ha(got + 4) == ha(got + 8)) + write32(buf + 8, 0x800c0000 | lo(got + 4)); // lwz r0,GOT+4@l(r12) + else + write32(buf + 8, 0x840c0000 | lo(got + 4)); // lwzu r0,GOT+4@l(r12) + write32(buf + 12, 0x396b0000 | lo(-glink)); // addi r11,r11,-Glink@l + write32(buf + 16, 0x7c0903a6); // mtctr r0 + write32(buf + 20, 0x7c0b5a14); // add r0,r11,r11 + if (ha(got + 4) == ha(got + 8)) + write32(buf + 24, 0x818c0000 | lo(got + 8)); // lwz r12,GOT+8@ha(r12) + else + write32(buf + 24, 0x818c0000 | 4); // lwz r12,4(r12) + write32(buf + 28, 0x7d605a14); // add r11,r0,r11 + write32(buf + 32, 0x4e800420); // bctr + buf += 36; + } + + // Pad with nop. They should not be executed. + for (; buf < end; buf += 4) + write32(buf, 0x60000000); +} + PPC::PPC() { - NoneRel = R_PPC_NONE; - GotBaseSymOff = 0x8000; - GotBaseSymInGotPlt = false; + gotRel = R_PPC_GLOB_DAT; + noneRel = R_PPC_NONE; + pltRel = R_PPC_JMP_SLOT; + relativeRel = R_PPC_RELATIVE; + iRelativeRel = R_PPC_IRELATIVE; + symbolicRel = R_PPC_ADDR32; + gotBaseSymInGotPlt = false; + gotHeaderEntriesNum = 3; + gotPltHeaderEntriesNum = 0; + pltHeaderSize = 64; // size of PLTresolve in .glink + pltEntrySize = 4; + + needsThunks = true; + + tlsModuleIndexRel = R_PPC_DTPMOD32; + tlsOffsetRel = R_PPC_DTPREL32; + tlsGotRel = R_PPC_TPREL32; + + defaultMaxPageSize = 65536; + defaultImageBase = 0x10000000; + + write32(trapInstr.data(), 0x7fe00008); } -RelExpr PPC::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +void PPC::writeGotHeader(uint8_t *buf) const { + // _GLOBAL_OFFSET_TABLE_[0] = _DYNAMIC + // glibc stores _dl_runtime_resolve in _GLOBAL_OFFSET_TABLE_[1], + // link_map in _GLOBAL_OFFSET_TABLE_[2]. + write32(buf, mainPart->dynamic->getVA()); +} + +void PPC::writeGotPlt(uint8_t *buf, const Symbol &s) const { + // Address of the symbol resolver stub in .glink . + write32(buf, in.plt->getVA() + 4 * s.pltIndex); +} + +bool PPC::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { + if (type != R_PPC_REL24 && type != R_PPC_PLTREL24) + return false; + if (s.isInPlt()) + return true; + if (s.isUndefWeak()) + return false; + return !(expr == R_PC && PPC::inBranchRange(type, branchAddr, s.getVA())); +} + +uint32_t PPC::getThunkSectionSpacing() const { return 0x2000000; } + +bool PPC::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + uint64_t offset = dst - src; + if (type == R_PPC_REL24 || type == R_PPC_PLTREL24) + return isInt<26>(offset); + llvm_unreachable("unsupported relocation type used in branch"); +} + +RelExpr PPC::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_HA: + case R_PPC_DTPREL16_HI: + case R_PPC_DTPREL16_LO: + case R_PPC_DTPREL32: + return R_DTPREL; case R_PPC_REL14: - case R_PPC_REL24: case R_PPC_REL32: + case R_PPC_LOCAL24PC: + case R_PPC_REL16_LO: + case R_PPC_REL16_HI: + case R_PPC_REL16_HA: return R_PC; - case R_PPC_PLTREL24: + case R_PPC_GOT16: + return R_GOT_OFF; + case R_PPC_REL24: return R_PLT_PC; + case R_PPC_PLTREL24: + return R_PPC32_PLTREL; + case R_PPC_GOT_TLSGD16: + return R_TLSGD_GOT; + case R_PPC_GOT_TLSLD16: + return R_TLSLD_GOT; + case R_PPC_GOT_TPREL16: + return R_GOT_OFF; + case R_PPC_TLS: + return R_TLSIE_HINT; + case R_PPC_TLSGD: + return R_TLSDESC_CALL; + case R_PPC_TLSLD: + return R_TLSLD_HINT; + case R_PPC_TPREL16: + case R_PPC_TPREL16_HA: + case R_PPC_TPREL16_LO: + case R_PPC_TPREL16_HI: + return R_TLS; default: return R_ABS; } } -void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +RelType PPC::getDynRel(RelType type) const { + if (type == R_PPC_ADDR32) + return type; + return R_PPC_NONE; +} + +static std::pair fromDTPREL(RelType type, uint64_t val) { + uint64_t dtpBiasedVal = val - 0x8000; + switch (type) { + case R_PPC_DTPREL16: + return {R_PPC64_ADDR16, dtpBiasedVal}; + case R_PPC_DTPREL16_HA: + return {R_PPC_ADDR16_HA, dtpBiasedVal}; + case R_PPC_DTPREL16_HI: + return {R_PPC_ADDR16_HI, dtpBiasedVal}; + case R_PPC_DTPREL16_LO: + return {R_PPC_ADDR16_LO, dtpBiasedVal}; + case R_PPC_DTPREL32: + return {R_PPC_ADDR32, dtpBiasedVal}; + default: + return {type, val}; + } +} + +void PPC::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + RelType newType; + std::tie(newType, val) = fromDTPREL(type, val); + switch (newType) { + case R_PPC_ADDR16: + checkIntUInt(loc, val, 16, type); + write16(loc, val); + break; + case R_PPC_GOT16: + case R_PPC_GOT_TLSGD16: + case R_PPC_GOT_TLSLD16: + case R_PPC_GOT_TPREL16: + case R_PPC_TPREL16: + checkInt(loc, val, 16, type); + write16(loc, val); + break; case R_PPC_ADDR16_HA: - write16be(Loc, (Val + 0x8000) >> 16); + case R_PPC_DTPREL16_HA: + case R_PPC_GOT_TLSGD16_HA: + case R_PPC_GOT_TLSLD16_HA: + case R_PPC_GOT_TPREL16_HA: + case R_PPC_REL16_HA: + case R_PPC_TPREL16_HA: + write16(loc, ha(val)); break; case R_PPC_ADDR16_HI: - write16be(Loc, Val >> 16); + case R_PPC_DTPREL16_HI: + case R_PPC_GOT_TLSGD16_HI: + case R_PPC_GOT_TLSLD16_HI: + case R_PPC_GOT_TPREL16_HI: + case R_PPC_REL16_HI: + case R_PPC_TPREL16_HI: + write16(loc, val >> 16); break; case R_PPC_ADDR16_LO: - write16be(Loc, Val); + case R_PPC_DTPREL16_LO: + case R_PPC_GOT_TLSGD16_LO: + case R_PPC_GOT_TLSLD16_LO: + case R_PPC_GOT_TPREL16_LO: + case R_PPC_REL16_LO: + case R_PPC_TPREL16_LO: + write16(loc, val); break; case R_PPC_ADDR32: case R_PPC_REL32: - write32be(Loc, Val); + write32(loc, val); break; - case R_PPC_REL14: - write32be(Loc, read32be(Loc) | (Val & 0xFFFC)); + case R_PPC_REL14: { + uint32_t mask = 0x0000FFFC; + checkInt(loc, val, 16, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); break; - case R_PPC_PLTREL24: + } case R_PPC_REL24: - write32be(Loc, read32be(Loc) | (Val & 0x3FFFFFC)); + case R_PPC_LOCAL24PC: + case R_PPC_PLTREL24: { + uint32_t mask = 0x03FFFFFC; + checkInt(loc, val, 26, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); break; + } default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } +RelExpr PPC::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + if (expr == R_RELAX_TLS_GD_TO_IE) + return R_RELAX_TLS_GD_TO_IE_GOT_OFF; + if (expr == R_RELAX_TLS_LD_TO_LE) + return R_RELAX_TLS_LD_TO_LE_ABS; + return expr; +} + +int PPC::getTlsGdRelaxSkip(RelType type) const { + // A __tls_get_addr call instruction is marked with 2 relocations: + // + // R_PPC_TLSGD / R_PPC_TLSLD: marker relocation + // R_PPC_REL24: __tls_get_addr + // + // After the relaxation we no longer call __tls_get_addr and should skip both + // relocations to not create a false dependence on __tls_get_addr being + // defined. + if (type == R_PPC_TLSGD || type == R_PPC_TLSLD) + return 2; + return 1; +} + +void PPC::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TLSGD16: { + // addi rT, rA, x@got@tlsgd --> lwz rT, x@got@tprel(rA) + uint32_t insn = readFromHalf16(loc); + writeFromHalf16(loc, 0x80000000 | (insn & 0x03ff0000)); + relocateOne(loc, R_PPC_GOT_TPREL16, val); + break; + } + case R_PPC_TLSGD: + // bl __tls_get_addr(x@tldgd) --> add r3, r3, r2 + write32(loc, 0x7c631214); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); + } +} + +void PPC::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TLSGD16: + // addi r3, r31, x@got@tlsgd --> addis r3, r2, x@tprel@ha + writeFromHalf16(loc, 0x3c620000 | ha(val)); + break; + case R_PPC_TLSGD: + // bl __tls_get_addr(x@tldgd) --> add r3, r3, x@tprel@l + write32(loc, 0x38630000 | lo(val)); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); + } +} + +void PPC::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TLSLD16: + // addi r3, rA, x@got@tlsgd --> addis r3, r2, 0 + writeFromHalf16(loc, 0x3c620000); + break; + case R_PPC_TLSLD: + // r3+x@dtprel computes r3+x-0x8000, while we want it to compute r3+x@tprel + // = r3+x-0x7000, so add 4096 to r3. + // bl __tls_get_addr(x@tlsld) --> addi r3, r3, 4096 + write32(loc, 0x38631000); + break; + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_HA: + case R_PPC_DTPREL16_HI: + case R_PPC_DTPREL16_LO: + relocateOne(loc, type, val); + break; + default: + llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); + } +} + +void PPC::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TPREL16: { + // lwz rT, x@got@tprel(rA) --> addis rT, r2, x@tprel@ha + uint32_t rt = readFromHalf16(loc) & 0x03e00000; + writeFromHalf16(loc, 0x3c020000 | rt | ha(val)); + break; + } + case R_PPC_TLS: { + uint32_t insn = read32(loc); + if (insn >> 26 != 31) + error("unrecognized instruction for IE to LE R_PPC_TLS"); + // addi rT, rT, x@tls --> addi rT, rT, x@tprel@l + uint32_t dFormOp = getPPCDFormOp((read32(loc) & 0x000007fe) >> 1); + if (dFormOp == 0) + error("unrecognized instruction for IE to LE R_PPC_TLS"); + write32(loc, (dFormOp << 26) | (insn & 0x03ff0000) | lo(val)); + break; + } + default: + llvm_unreachable("unsupported relocation for TLS IE to LE relaxation"); + } +} + TargetInfo *elf::getPPCTargetInfo() { - static PPC Target; - return &Target; + static PPC target; + return ⌖ } Index: vendor/lld/dist/ELF/Arch/PPC64.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/PPC64.cpp (revision 351287) +++ vendor/lld/dist/ELF/Arch/PPC64.cpp (revision 351288) @@ -1,931 +1,1077 @@ //===- PPC64.cpp ----------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; -static uint64_t PPC64TocOffset = 0x8000; -static uint64_t DynamicThreadPointerOffset = 0x8000; +static uint64_t ppc64TocOffset = 0x8000; +static uint64_t dynamicThreadPointerOffset = 0x8000; // The instruction encoding of bits 21-30 from the ISA for the Xform and Dform // instructions that can be used as part of the initial exec TLS sequence. enum XFormOpcd { LBZX = 87, LHZX = 279, LWZX = 23, LDX = 21, STBX = 215, STHX = 407, STWX = 151, STDX = 149, ADD = 266, }; enum DFormOpcd { LBZ = 34, LBZU = 35, LHZ = 40, LHZU = 41, LHAU = 43, LWZ = 32, LWZU = 33, LFSU = 49, LD = 58, LFDU = 51, STB = 38, STBU = 39, STH = 44, STHU = 45, STW = 36, STWU = 37, STFSU = 53, STFDU = 55, STD = 62, ADDI = 14 }; uint64_t elf::getPPC64TocBase() { // The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The // TOC starts where the first of these sections starts. We always create a // .got when we see a relocation that uses it, so for us the start is always // the .got. - uint64_t TocVA = In.Got->getVA(); + uint64_t tocVA = in.got->getVA(); // Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000 // thus permitting a full 64 Kbytes segment. Note that the glibc startup // code (crt1.o) assumes that you can get from the TOC base to the // start of the .toc section with only a single (signed) 16-bit relocation. - return TocVA + PPC64TocOffset; + return tocVA + ppc64TocOffset; } -unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther) { +unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) { // The offset is encoded into the 3 most significant bits of the st_other // field, with some special values described in section 3.4.1 of the ABI: // 0 --> Zero offset between the GEP and LEP, and the function does NOT use // the TOC pointer (r2). r2 will hold the same value on returning from // the function as it did on entering the function. // 1 --> Zero offset between the GEP and LEP, and r2 should be treated as a // caller-saved register for all callers. // 2-6 --> The binary logarithm of the offset eg: // 2 --> 2^2 = 4 bytes --> 1 instruction. // 6 --> 2^6 = 64 bytes --> 16 instructions. // 7 --> Reserved. - uint8_t GepToLep = (StOther >> 5) & 7; - if (GepToLep < 2) + uint8_t gepToLep = (stOther >> 5) & 7; + if (gepToLep < 2) return 0; // The value encoded in the st_other bits is the // log-base-2(offset). - if (GepToLep < 7) - return 1 << GepToLep; + if (gepToLep < 7) + return 1 << gepToLep; error("reserved value of 7 in the 3 most-significant-bits of st_other"); return 0; } +bool elf::isPPC64SmallCodeModelTocReloc(RelType type) { + // The only small code model relocations that access the .toc section. + return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS; +} + +// Find the R_PPC64_ADDR64 in .rela.toc with matching offset. +template +static std::pair +getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) { + if (tocSec->numRelocations == 0) + return {}; + + // .rela.toc contains exclusively R_PPC64_ADDR64 relocations sorted by + // r_offset: 0, 8, 16, etc. For a given Offset, Offset / 8 gives us the + // relocation index in most cases. + // + // In rare cases a TOC entry may store a constant that doesn't need an + // R_PPC64_ADDR64, the corresponding r_offset is therefore missing. Offset / 8 + // points to a relocation with larger r_offset. Do a linear probe then. + // Constants are extremely uncommon in .toc and the extra number of array + // accesses can be seen as a small constant. + ArrayRef relas = tocSec->template relas(); + uint64_t index = std::min(offset / 8, relas.size() - 1); + for (;;) { + if (relas[index].r_offset == offset) { + Symbol &sym = tocSec->getFile()->getRelocTargetSym(relas[index]); + return {dyn_cast(&sym), getAddend(relas[index])}; + } + if (relas[index].r_offset < offset || index == 0) + break; + --index; + } + return {}; +} + +// When accessing a symbol defined in another translation unit, compilers +// reserve a .toc entry, allocate a local label and generate toc-indirect +// instuctions: +// +// addis 3, 2, .LC0@toc@ha # R_PPC64_TOC16_HA +// ld 3, .LC0@toc@l(3) # R_PPC64_TOC16_LO_DS, load the address from a .toc entry +// ld/lwa 3, 0(3) # load the value from the address +// +// .section .toc,"aw",@progbits +// .LC0: .tc var[TC],var +// +// If var is defined, non-preemptable and addressable with a 32-bit signed +// offset from the toc base, the address of var can be computed by adding an +// offset to the toc base, saving a load. +// +// addis 3,2,var@toc@ha # this may be relaxed to a nop, +// addi 3,3,var@toc@l # then this becomes addi 3,2,var@toc +// ld/lwa 3, 0(3) # load the value from the address +// +// Returns true if the relaxation is performed. +bool elf::tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel, + uint8_t *bufLoc) { + assert(config->tocOptimize); + if (rel.addend < 0) + return false; + + // If the symbol is not the .toc section, this isn't a toc-indirection. + Defined *defSym = dyn_cast(rel.sym); + if (!defSym || !defSym->isSection() || defSym->section->name != ".toc") + return false; + + Defined *d; + int64_t addend; + auto *tocISB = cast(defSym->section); + std::tie(d, addend) = + config->isLE ? getRelaTocSymAndAddend(tocISB, rel.addend) + : getRelaTocSymAndAddend(tocISB, rel.addend); + + // Only non-preemptable defined symbols can be relaxed. + if (!d || d->isPreemptible) + return false; + + // Two instructions can materialize a 32-bit signed offset from the toc base. + uint64_t tocRelative = d->getVA(addend) - getPPC64TocBase(); + if (!isInt<32>(tocRelative)) + return false; + + // Add PPC64TocOffset that will be subtracted by relocateOne(). + target->relaxGot(bufLoc, type, tocRelative + ppc64TocOffset); + return true; +} + namespace { class PPC64 final : public TargetInfo { public: PPC64(); + int getTlsGdRelaxSkip(RelType type) const override; uint32_t calcEFlags() const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void writeGotHeader(uint8_t *Buf) const override; - bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const override; - bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; - RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const override; - void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void writeGotHeader(uint8_t *buf) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; + uint32_t getThunkSectionSpacing() const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxGot(uint8_t *loc, RelType 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 relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; - bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End, - uint8_t StOther) const override; + bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const override; }; } // namespace // Relocation masks following the #lo(value), #hi(value), #ha(value), // #higher(value), #highera(value), #highest(value), and #highesta(value) // macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi // document. -static uint16_t lo(uint64_t V) { return V; } -static uint16_t hi(uint64_t V) { return V >> 16; } -static uint16_t ha(uint64_t V) { return (V + 0x8000) >> 16; } -static uint16_t higher(uint64_t V) { return V >> 32; } -static uint16_t highera(uint64_t V) { return (V + 0x8000) >> 32; } -static uint16_t highest(uint64_t V) { return V >> 48; } -static uint16_t highesta(uint64_t V) { return (V + 0x8000) >> 48; } +static uint16_t lo(uint64_t v) { return v; } +static uint16_t hi(uint64_t v) { return v >> 16; } +static uint16_t ha(uint64_t v) { return (v + 0x8000) >> 16; } +static uint16_t higher(uint64_t v) { return v >> 32; } +static uint16_t highera(uint64_t v) { return (v + 0x8000) >> 32; } +static uint16_t highest(uint64_t v) { return v >> 48; } +static uint16_t highesta(uint64_t v) { return (v + 0x8000) >> 48; } // Extracts the 'PO' field of an instruction encoding. -static uint8_t getPrimaryOpCode(uint32_t Encoding) { return (Encoding >> 26); } +static uint8_t getPrimaryOpCode(uint32_t encoding) { return (encoding >> 26); } -static bool isDQFormInstruction(uint32_t Encoding) { - switch (getPrimaryOpCode(Encoding)) { +static bool isDQFormInstruction(uint32_t encoding) { + switch (getPrimaryOpCode(encoding)) { default: return false; case 56: // The only instruction with a primary opcode of 56 is `lq`. return true; case 61: // There are both DS and DQ instruction forms with this primary opcode. // Namely `lxv` and `stxv` are the DQ-forms that use it. // The DS 'XO' bits being set to 01 is restricted to DQ form. - return (Encoding & 3) == 0x1; + return (encoding & 3) == 0x1; } } -static bool isInstructionUpdateForm(uint32_t Encoding) { - switch (getPrimaryOpCode(Encoding)) { +static bool isInstructionUpdateForm(uint32_t encoding) { + switch (getPrimaryOpCode(encoding)) { default: return false; case LBZU: case LHAU: case LHZU: case LWZU: case LFSU: case LFDU: case STBU: case STHU: case STWU: case STFSU: case STFDU: return true; // LWA has the same opcode as LD, and the DS bits is what differentiates // between LD/LDU/LWA case LD: case STD: - return (Encoding & 3) == 1; + return (encoding & 3) == 1; } } // There are a number of places when we either want to read or write an // instruction when handling a half16 relocation type. On big-endian the buffer // pointer is pointing into the middle of the word we want to extract, and on // little-endian it is pointing to the start of the word. These 2 helpers are to // simplify reading and writing in that context. -static void writeInstrFromHalf16(uint8_t *Loc, uint32_t Instr) { - write32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0), Instr); +static void writeFromHalf16(uint8_t *loc, uint32_t insn) { + write32(config->isLE ? loc : loc - 2, insn); } -static uint32_t readInstrFromHalf16(const uint8_t *Loc) { - return read32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0)); +static uint32_t readFromHalf16(const uint8_t *loc) { + return read32(config->isLE ? loc : loc - 2); } PPC64::PPC64() { - GotRel = R_PPC64_GLOB_DAT; - NoneRel = R_PPC64_NONE; - PltRel = R_PPC64_JMP_SLOT; - RelativeRel = R_PPC64_RELATIVE; - IRelativeRel = R_PPC64_IRELATIVE; - GotEntrySize = 8; - PltEntrySize = 4; - GotPltEntrySize = 8; - GotBaseSymInGotPlt = false; - GotBaseSymOff = 0x8000; - GotHeaderEntriesNum = 1; - GotPltHeaderEntriesNum = 2; - PltHeaderSize = 60; - NeedsThunks = true; + gotRel = R_PPC64_GLOB_DAT; + noneRel = R_PPC64_NONE; + pltRel = R_PPC64_JMP_SLOT; + relativeRel = R_PPC64_RELATIVE; + iRelativeRel = R_PPC64_IRELATIVE; + symbolicRel = R_PPC64_ADDR64; + pltEntrySize = 4; + gotBaseSymInGotPlt = false; + gotHeaderEntriesNum = 1; + gotPltHeaderEntriesNum = 2; + pltHeaderSize = 60; + needsThunks = true; - TlsModuleIndexRel = R_PPC64_DTPMOD64; - TlsOffsetRel = R_PPC64_DTPREL64; + tlsModuleIndexRel = R_PPC64_DTPMOD64; + tlsOffsetRel = R_PPC64_DTPREL64; - TlsGotRel = R_PPC64_TPREL64; + tlsGotRel = R_PPC64_TPREL64; - NeedsMoreStackNonSplit = false; + needsMoreStackNonSplit = false; // We need 64K pages (at least under glibc/Linux, the loader won't // set different permissions on a finer granularity than that). - DefaultMaxPageSize = 65536; + defaultMaxPageSize = 65536; // The PPC64 ELF ABI v1 spec, says: // // It is normally desirable to put segments with different characteristics // in separate 256 Mbyte portions of the address space, to give the // operating system full paging flexibility in the 64-bit address space. // // And because the lowest non-zero 256M boundary is 0x10000000, PPC64 linkers // use 0x10000000 as the starting address. - DefaultImageBase = 0x10000000; + defaultImageBase = 0x10000000; - write32(TrapInstr.data(), 0x7fe00008); + write32(trapInstr.data(), 0x7fe00008); } -static uint32_t getEFlags(InputFile *File) { - if (Config->EKind == ELF64BEKind) - return cast>(File)->getObj().getHeader()->e_flags; - return cast>(File)->getObj().getHeader()->e_flags; +int PPC64::getTlsGdRelaxSkip(RelType type) const { + // A __tls_get_addr call instruction is marked with 2 relocations: + // + // R_PPC64_TLSGD / R_PPC64_TLSLD: marker relocation + // R_PPC64_REL24: __tls_get_addr + // + // After the relaxation we no longer call __tls_get_addr and should skip both + // relocations to not create a false dependence on __tls_get_addr being + // defined. + if (type == R_PPC64_TLSGD || type == R_PPC64_TLSLD) + return 2; + return 1; } +static uint32_t getEFlags(InputFile *file) { + if (config->ekind == ELF64BEKind) + return cast>(file)->getObj().getHeader()->e_flags; + return cast>(file)->getObj().getHeader()->e_flags; +} + // This file implements v2 ABI. This function makes sure that all // object files have v2 or an unspecified version as an ABI version. uint32_t PPC64::calcEFlags() const { - for (InputFile *F : ObjectFiles) { - uint32_t Flag = getEFlags(F); - if (Flag == 1) - error(toString(F) + ": ABI version 1 is not supported"); - else if (Flag > 2) - error(toString(F) + ": unrecognized e_flags: " + Twine(Flag)); + for (InputFile *f : objectFiles) { + uint32_t flag = getEFlags(f); + if (flag == 1) + error(toString(f) + ": ABI version 1 is not supported"); + else if (flag > 2) + error(toString(f) + ": unrecognized e_flags: " + Twine(flag)); } return 2; } -void PPC64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC64_TOC16_HA: + // Convert "addis reg, 2, .LC0@toc@h" to "addis reg, 2, var@toc@h" or "nop". + relocateOne(loc, type, val); + break; + case R_PPC64_TOC16_LO_DS: { + // Convert "ld reg, .LC0@toc@l(reg)" to "addi reg, reg, var@toc@l" or + // "addi reg, 2, var@toc". + uint32_t insn = readFromHalf16(loc); + if (getPrimaryOpCode(insn) != LD) + error("expected a 'ld' for got-indirect to toc-relative relaxing"); + writeFromHalf16(loc, (insn & 0x03ffffff) | 0x38000000); + relocateOne(loc, R_PPC64_TOC16_LO, val); + break; + } + default: + llvm_unreachable("unexpected relocation type"); + } +} + +void PPC64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // Reference: 3.7.4.2 of the 64-bit ELF V2 abi supplement. // The general dynamic code sequence for a global `x` will look like: // Instruction Relocation Symbol // addis r3, r2, x@got@tlsgd@ha R_PPC64_GOT_TLSGD16_HA x // addi r3, r3, x@got@tlsgd@l R_PPC64_GOT_TLSGD16_LO x // bl __tls_get_addr(x@tlsgd) R_PPC64_TLSGD x // R_PPC64_REL24 __tls_get_addr // nop None None // Relaxing to local exec entails converting: // addis r3, r2, x@got@tlsgd@ha into nop // addi r3, r3, x@got@tlsgd@l into addis r3, r13, x@tprel@ha // bl __tls_get_addr(x@tlsgd) into nop // nop into addi r3, r3, x@tprel@l - switch (Type) { + switch (type) { case R_PPC64_GOT_TLSGD16_HA: - writeInstrFromHalf16(Loc, 0x60000000); // nop + writeFromHalf16(loc, 0x60000000); // nop break; case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: - writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13 - relocateOne(Loc, R_PPC64_TPREL16_HA, Val); + writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13 + relocateOne(loc, R_PPC64_TPREL16_HA, val); break; case R_PPC64_TLSGD: - write32(Loc, 0x60000000); // nop - write32(Loc + 4, 0x38630000); // addi r3, r3 + write32(loc, 0x60000000); // nop + write32(loc + 4, 0x38630000); // addi r3, r3 // Since we are relocating a half16 type relocation and Loc + 4 points to // the start of an instruction we need to advance the buffer by an extra // 2 bytes on BE. - relocateOne(Loc + 4 + (Config->EKind == ELF64BEKind ? 2 : 0), - R_PPC64_TPREL16_LO, Val); + relocateOne(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0), + R_PPC64_TPREL16_LO, val); break; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } -void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { // Reference: 3.7.4.3 of the 64-bit ELF V2 abi supplement. // The local dynamic code sequence for a global `x` will look like: // Instruction Relocation Symbol // addis r3, r2, x@got@tlsld@ha R_PPC64_GOT_TLSLD16_HA x // addi r3, r3, x@got@tlsld@l R_PPC64_GOT_TLSLD16_LO x // bl __tls_get_addr(x@tlsgd) R_PPC64_TLSLD x // R_PPC64_REL24 __tls_get_addr // nop None None // Relaxing to local exec entails converting: // addis r3, r2, x@got@tlsld@ha into nop // addi r3, r3, x@got@tlsld@l into addis r3, r13, 0 // bl __tls_get_addr(x@tlsgd) into nop // nop into addi r3, r3, 4096 - switch (Type) { + switch (type) { case R_PPC64_GOT_TLSLD16_HA: - writeInstrFromHalf16(Loc, 0x60000000); // nop + writeFromHalf16(loc, 0x60000000); // nop break; case R_PPC64_GOT_TLSLD16_LO: - writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13, 0 + writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13, 0 break; case R_PPC64_TLSLD: - write32(Loc, 0x60000000); // nop - write32(Loc + 4, 0x38631000); // addi r3, r3, 4096 + write32(loc, 0x60000000); // nop + write32(loc + 4, 0x38631000); // addi r3, r3, 4096 break; case R_PPC64_DTPREL16: case R_PPC64_DTPREL16_HA: case R_PPC64_DTPREL16_HI: case R_PPC64_DTPREL16_DS: case R_PPC64_DTPREL16_LO: case R_PPC64_DTPREL16_LO_DS: - case R_PPC64_GOT_DTPREL16_HA: - case R_PPC64_GOT_DTPREL16_LO_DS: - case R_PPC64_GOT_DTPREL16_DS: - case R_PPC64_GOT_DTPREL16_HI: - relocateOne(Loc, Type, Val); + relocateOne(loc, type, val); break; default: llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); } } -static unsigned getDFormOp(unsigned SecondaryOp) { - switch (SecondaryOp) { +unsigned elf::getPPCDFormOp(unsigned secondaryOp) { + switch (secondaryOp) { case LBZX: return LBZ; case LHZX: return LHZ; case LWZX: return LWZ; case LDX: return LD; case STBX: return STB; case STHX: return STH; case STWX: return STW; case STDX: return STD; case ADD: return ADDI; default: - error("unrecognized instruction for IE to LE R_PPC64_TLS"); return 0; } } -void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { // The initial exec code sequence for a global `x` will look like: // Instruction Relocation Symbol // addis r9, r2, x@got@tprel@ha R_PPC64_GOT_TPREL16_HA x // ld r9, x@got@tprel@l(r9) R_PPC64_GOT_TPREL16_LO_DS x // add r9, r9, x@tls R_PPC64_TLS x // Relaxing to local exec entails converting: // addis r9, r2, x@got@tprel@ha into nop // ld r9, x@got@tprel@l(r9) into addis r9, r13, x@tprel@ha // add r9, r9, x@tls into addi r9, r9, x@tprel@l // x@tls R_PPC64_TLS is a relocation which does not compute anything, // it is replaced with r13 (thread pointer). // The add instruction in the initial exec sequence has multiple variations // that need to be handled. If we are building an address it will use an add // instruction, if we are accessing memory it will use any of the X-form // indexed load or store instructions. - unsigned Offset = (Config->EKind == ELF64BEKind) ? 2 : 0; - switch (Type) { + unsigned offset = (config->ekind == ELF64BEKind) ? 2 : 0; + switch (type) { case R_PPC64_GOT_TPREL16_HA: - write32(Loc - Offset, 0x60000000); // nop + write32(loc - offset, 0x60000000); // nop break; case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_TPREL16_DS: { - uint32_t RegNo = read32(Loc - Offset) & 0x03E00000; // bits 6-10 - write32(Loc - Offset, 0x3C0D0000 | RegNo); // addis RegNo, r13 - relocateOne(Loc, R_PPC64_TPREL16_HA, Val); + uint32_t regNo = read32(loc - offset) & 0x03E00000; // bits 6-10 + write32(loc - offset, 0x3C0D0000 | regNo); // addis RegNo, r13 + relocateOne(loc, R_PPC64_TPREL16_HA, val); break; } case R_PPC64_TLS: { - uint32_t PrimaryOp = getPrimaryOpCode(read32(Loc)); - if (PrimaryOp != 31) + uint32_t primaryOp = getPrimaryOpCode(read32(loc)); + if (primaryOp != 31) error("unrecognized instruction for IE to LE R_PPC64_TLS"); - uint32_t SecondaryOp = (read32(Loc) & 0x000007FE) >> 1; // bits 21-30 - uint32_t DFormOp = getDFormOp(SecondaryOp); - write32(Loc, ((DFormOp << 26) | (read32(Loc) & 0x03FFFFFF))); - relocateOne(Loc + Offset, R_PPC64_TPREL16_LO, Val); + uint32_t secondaryOp = (read32(loc) & 0x000007FE) >> 1; // bits 21-30 + uint32_t dFormOp = getPPCDFormOp(secondaryOp); + if (dFormOp == 0) + error("unrecognized instruction for IE to LE R_PPC64_TLS"); + write32(loc, ((dFormOp << 26) | (read32(loc) & 0x03FFFFFF))); + relocateOne(loc + offset, R_PPC64_TPREL16_LO, val); break; } default: llvm_unreachable("unknown relocation for IE to LE"); break; } } -RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr PPC64::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_PPC64_GOT16: case R_PPC64_GOT16_DS: case R_PPC64_GOT16_HA: case R_PPC64_GOT16_HI: case R_PPC64_GOT16_LO: case R_PPC64_GOT16_LO_DS: return R_GOT_OFF; case R_PPC64_TOC16: case R_PPC64_TOC16_DS: - case R_PPC64_TOC16_HA: case R_PPC64_TOC16_HI: case R_PPC64_TOC16_LO: - case R_PPC64_TOC16_LO_DS: return R_GOTREL; + case R_PPC64_TOC16_HA: + case R_PPC64_TOC16_LO_DS: + return config->tocOptimize ? R_PPC64_RELAX_TOC : R_GOTREL; case R_PPC64_TOC: - return R_PPC_TOC; + return R_PPC64_TOCBASE; case R_PPC64_REL14: case R_PPC64_REL24: - return R_PPC_CALL_PLT; + return R_PPC64_CALL_PLT; case R_PPC64_REL16_LO: case R_PPC64_REL16_HA: case R_PPC64_REL32: case R_PPC64_REL64: return R_PC; case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_HA: case R_PPC64_GOT_TLSGD16_HI: case R_PPC64_GOT_TLSGD16_LO: return R_TLSGD_GOT; case R_PPC64_GOT_TLSLD16: case R_PPC64_GOT_TLSLD16_HA: case R_PPC64_GOT_TLSLD16_HI: case R_PPC64_GOT_TLSLD16_LO: return R_TLSLD_GOT; case R_PPC64_GOT_TPREL16_HA: case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_TPREL16_DS: case R_PPC64_GOT_TPREL16_HI: return R_GOT_OFF; case R_PPC64_GOT_DTPREL16_HA: case R_PPC64_GOT_DTPREL16_LO_DS: case R_PPC64_GOT_DTPREL16_DS: case R_PPC64_GOT_DTPREL16_HI: return R_TLSLD_GOT_OFF; case R_PPC64_TPREL16: case R_PPC64_TPREL16_HA: case R_PPC64_TPREL16_LO: case R_PPC64_TPREL16_HI: case R_PPC64_TPREL16_DS: case R_PPC64_TPREL16_LO_DS: case R_PPC64_TPREL16_HIGHER: case R_PPC64_TPREL16_HIGHERA: case R_PPC64_TPREL16_HIGHEST: case R_PPC64_TPREL16_HIGHESTA: return R_TLS; case R_PPC64_DTPREL16: case R_PPC64_DTPREL16_DS: case R_PPC64_DTPREL16_HA: case R_PPC64_DTPREL16_HI: case R_PPC64_DTPREL16_HIGHER: case R_PPC64_DTPREL16_HIGHERA: case R_PPC64_DTPREL16_HIGHEST: case R_PPC64_DTPREL16_HIGHESTA: case R_PPC64_DTPREL16_LO: case R_PPC64_DTPREL16_LO_DS: case R_PPC64_DTPREL64: - return R_ABS; + return R_DTPREL; case R_PPC64_TLSGD: return R_TLSDESC_CALL; case R_PPC64_TLSLD: return R_TLSLD_HINT; case R_PPC64_TLS: return R_TLSIE_HINT; default: return R_ABS; } } -void PPC64::writeGotHeader(uint8_t *Buf) const { - write64(Buf, getPPC64TocBase()); +RelType PPC64::getDynRel(RelType type) const { + if (type == R_PPC64_ADDR64 || type == R_PPC64_TOC) + return R_PPC64_ADDR64; + return R_PPC64_NONE; } -void PPC64::writePltHeader(uint8_t *Buf) const { +void PPC64::writeGotHeader(uint8_t *buf) const { + write64(buf, getPPC64TocBase()); +} + +void PPC64::writePltHeader(uint8_t *buf) const { // The generic resolver stub goes first. - write32(Buf + 0, 0x7c0802a6); // mflr r0 - write32(Buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8> - write32(Buf + 8, 0x7d6802a6); // mflr r11 - write32(Buf + 12, 0x7c0803a6); // mtlr r0 - write32(Buf + 16, 0x7d8b6050); // subf r12, r11, r12 - write32(Buf + 20, 0x380cffcc); // subi r0,r12,52 - write32(Buf + 24, 0x7800f082); // srdi r0,r0,62,2 - write32(Buf + 28, 0xe98b002c); // ld r12,44(r11) - write32(Buf + 32, 0x7d6c5a14); // add r11,r12,r11 - write32(Buf + 36, 0xe98b0000); // ld r12,0(r11) - write32(Buf + 40, 0xe96b0008); // ld r11,8(r11) - write32(Buf + 44, 0x7d8903a6); // mtctr r12 - write32(Buf + 48, 0x4e800420); // bctr + write32(buf + 0, 0x7c0802a6); // mflr r0 + write32(buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8> + write32(buf + 8, 0x7d6802a6); // mflr r11 + write32(buf + 12, 0x7c0803a6); // mtlr r0 + write32(buf + 16, 0x7d8b6050); // subf r12, r11, r12 + write32(buf + 20, 0x380cffcc); // subi r0,r12,52 + write32(buf + 24, 0x7800f082); // srdi r0,r0,62,2 + write32(buf + 28, 0xe98b002c); // ld r12,44(r11) + write32(buf + 32, 0x7d6c5a14); // add r11,r12,r11 + write32(buf + 36, 0xe98b0000); // ld r12,0(r11) + write32(buf + 40, 0xe96b0008); // ld r11,8(r11) + write32(buf + 44, 0x7d8903a6); // mtctr r12 + write32(buf + 48, 0x4e800420); // bctr // The 'bcl' instruction will set the link register to the address of the // following instruction ('mflr r11'). Here we store the offset from that // instruction to the first entry in the GotPlt section. - int64_t GotPltOffset = In.GotPlt->getVA() - (In.Plt->getVA() + 8); - write64(Buf + 52, GotPltOffset); + int64_t gotPltOffset = in.gotPlt->getVA() - (in.plt->getVA() + 8); + write64(buf + 52, gotPltOffset); } -void PPC64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - int32_t Offset = PltHeaderSize + Index * PltEntrySize; +void PPC64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + int32_t offset = pltHeaderSize + index * pltEntrySize; // bl __glink_PLTresolve - write32(Buf, 0x48000000 | ((-Offset) & 0x03FFFFFc)); + write32(buf, 0x48000000 | ((-offset) & 0x03FFFFFc)); } -static std::pair toAddr16Rel(RelType Type, uint64_t Val) { +static std::pair toAddr16Rel(RelType type, uint64_t val) { // Relocations relative to the toc-base need to be adjusted by the Toc offset. - uint64_t TocBiasedVal = Val - PPC64TocOffset; + uint64_t tocBiasedVal = val - ppc64TocOffset; // Relocations relative to dtv[dtpmod] need to be adjusted by the DTP offset. - uint64_t DTPBiasedVal = Val - DynamicThreadPointerOffset; + uint64_t dtpBiasedVal = val - dynamicThreadPointerOffset; - switch (Type) { + switch (type) { // TOC biased relocation. case R_PPC64_GOT16: case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSLD16: case R_PPC64_TOC16: - return {R_PPC64_ADDR16, TocBiasedVal}; + return {R_PPC64_ADDR16, tocBiasedVal}; case R_PPC64_GOT16_DS: case R_PPC64_TOC16_DS: case R_PPC64_GOT_TPREL16_DS: case R_PPC64_GOT_DTPREL16_DS: - return {R_PPC64_ADDR16_DS, TocBiasedVal}; + return {R_PPC64_ADDR16_DS, tocBiasedVal}; case R_PPC64_GOT16_HA: case R_PPC64_GOT_TLSGD16_HA: case R_PPC64_GOT_TLSLD16_HA: case R_PPC64_GOT_TPREL16_HA: case R_PPC64_GOT_DTPREL16_HA: case R_PPC64_TOC16_HA: - return {R_PPC64_ADDR16_HA, TocBiasedVal}; + return {R_PPC64_ADDR16_HA, tocBiasedVal}; case R_PPC64_GOT16_HI: case R_PPC64_GOT_TLSGD16_HI: case R_PPC64_GOT_TLSLD16_HI: case R_PPC64_GOT_TPREL16_HI: case R_PPC64_GOT_DTPREL16_HI: case R_PPC64_TOC16_HI: - return {R_PPC64_ADDR16_HI, TocBiasedVal}; + return {R_PPC64_ADDR16_HI, tocBiasedVal}; case R_PPC64_GOT16_LO: case R_PPC64_GOT_TLSGD16_LO: case R_PPC64_GOT_TLSLD16_LO: case R_PPC64_TOC16_LO: - return {R_PPC64_ADDR16_LO, TocBiasedVal}; + return {R_PPC64_ADDR16_LO, tocBiasedVal}; case R_PPC64_GOT16_LO_DS: case R_PPC64_TOC16_LO_DS: case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_DTPREL16_LO_DS: - return {R_PPC64_ADDR16_LO_DS, TocBiasedVal}; + return {R_PPC64_ADDR16_LO_DS, tocBiasedVal}; // Dynamic Thread pointer biased relocation types. case R_PPC64_DTPREL16: - return {R_PPC64_ADDR16, DTPBiasedVal}; + return {R_PPC64_ADDR16, dtpBiasedVal}; case R_PPC64_DTPREL16_DS: - return {R_PPC64_ADDR16_DS, DTPBiasedVal}; + return {R_PPC64_ADDR16_DS, dtpBiasedVal}; case R_PPC64_DTPREL16_HA: - return {R_PPC64_ADDR16_HA, DTPBiasedVal}; + return {R_PPC64_ADDR16_HA, dtpBiasedVal}; case R_PPC64_DTPREL16_HI: - return {R_PPC64_ADDR16_HI, DTPBiasedVal}; + return {R_PPC64_ADDR16_HI, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHER: - return {R_PPC64_ADDR16_HIGHER, DTPBiasedVal}; + return {R_PPC64_ADDR16_HIGHER, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHERA: - return {R_PPC64_ADDR16_HIGHERA, DTPBiasedVal}; + return {R_PPC64_ADDR16_HIGHERA, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHEST: - return {R_PPC64_ADDR16_HIGHEST, DTPBiasedVal}; + return {R_PPC64_ADDR16_HIGHEST, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHESTA: - return {R_PPC64_ADDR16_HIGHESTA, DTPBiasedVal}; + return {R_PPC64_ADDR16_HIGHESTA, dtpBiasedVal}; case R_PPC64_DTPREL16_LO: - return {R_PPC64_ADDR16_LO, DTPBiasedVal}; + return {R_PPC64_ADDR16_LO, dtpBiasedVal}; case R_PPC64_DTPREL16_LO_DS: - return {R_PPC64_ADDR16_LO_DS, DTPBiasedVal}; + return {R_PPC64_ADDR16_LO_DS, dtpBiasedVal}; case R_PPC64_DTPREL64: - return {R_PPC64_ADDR64, DTPBiasedVal}; + return {R_PPC64_ADDR64, dtpBiasedVal}; default: - return {Type, Val}; + return {type, val}; } } -static bool isTocOptType(RelType Type) { - switch (Type) { +static bool isTocOptType(RelType type) { + switch (type) { case R_PPC64_GOT16_HA: case R_PPC64_GOT16_LO_DS: case R_PPC64_TOC16_HA: case R_PPC64_TOC16_LO_DS: case R_PPC64_TOC16_LO: return true; default: return false; } } -void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { // We need to save the original relocation type to use in diagnostics, and // use the original type to determine if we should toc-optimize the // instructions being relocated. - RelType OriginalType = Type; - bool ShouldTocOptimize = isTocOptType(Type); + RelType originalType = type; + bool shouldTocOptimize = isTocOptType(type); // For dynamic thread pointer relative, toc-relative, and got-indirect // relocations, proceed in terms of the corresponding ADDR16 relocation type. - std::tie(Type, Val) = toAddr16Rel(Type, Val); + std::tie(type, val) = toAddr16Rel(type, val); - switch (Type) { + switch (type) { case R_PPC64_ADDR14: { - checkAlignment(Loc, Val, 4, Type); + checkAlignment(loc, val, 4, type); // Preserve the AA/LK bits in the branch instruction - uint8_t AALK = Loc[3]; - write16(Loc + 2, (AALK & 3) | (Val & 0xfffc)); + uint8_t aalk = loc[3]; + write16(loc + 2, (aalk & 3) | (val & 0xfffc)); break; } case R_PPC64_ADDR16: - case R_PPC64_TPREL16: - checkInt(Loc, Val, 16, OriginalType); - write16(Loc, Val); + checkIntUInt(loc, val, 16, originalType); + write16(loc, val); break; + case R_PPC64_ADDR32: + checkIntUInt(loc, val, 32, originalType); + write32(loc, val); + break; case R_PPC64_ADDR16_DS: case R_PPC64_TPREL16_DS: { - checkInt(Loc, Val, 16, OriginalType); + checkInt(loc, val, 16, originalType); // DQ-form instructions use bits 28-31 as part of the instruction encoding // DS-form instructions only use bits 30-31. - uint16_t Mask = isDQFormInstruction(readInstrFromHalf16(Loc)) ? 0xF : 0x3; - checkAlignment(Loc, lo(Val), Mask + 1, OriginalType); - write16(Loc, (read16(Loc) & Mask) | lo(Val)); + uint16_t mask = isDQFormInstruction(readFromHalf16(loc)) ? 0xf : 0x3; + checkAlignment(loc, lo(val), mask + 1, originalType); + write16(loc, (read16(loc) & mask) | lo(val)); } break; case R_PPC64_ADDR16_HA: case R_PPC64_REL16_HA: case R_PPC64_TPREL16_HA: - if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) - writeInstrFromHalf16(Loc, 0x60000000); + if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) + writeFromHalf16(loc, 0x60000000); else - write16(Loc, ha(Val)); + write16(loc, ha(val)); break; case R_PPC64_ADDR16_HI: case R_PPC64_REL16_HI: case R_PPC64_TPREL16_HI: - write16(Loc, hi(Val)); + write16(loc, hi(val)); break; case R_PPC64_ADDR16_HIGHER: case R_PPC64_TPREL16_HIGHER: - write16(Loc, higher(Val)); + write16(loc, higher(val)); break; case R_PPC64_ADDR16_HIGHERA: case R_PPC64_TPREL16_HIGHERA: - write16(Loc, highera(Val)); + write16(loc, highera(val)); break; case R_PPC64_ADDR16_HIGHEST: case R_PPC64_TPREL16_HIGHEST: - write16(Loc, highest(Val)); + write16(loc, highest(val)); break; case R_PPC64_ADDR16_HIGHESTA: case R_PPC64_TPREL16_HIGHESTA: - write16(Loc, highesta(Val)); + write16(loc, highesta(val)); break; case R_PPC64_ADDR16_LO: case R_PPC64_REL16_LO: case R_PPC64_TPREL16_LO: // When the high-adjusted part of a toc relocation evalutes to 0, it is // changed into a nop. The lo part then needs to be updated to use the // toc-pointer register r2, as the base register. - if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) { - uint32_t Instr = readInstrFromHalf16(Loc); - if (isInstructionUpdateForm(Instr)) - error(getErrorLocation(Loc) + + if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) { + uint32_t insn = readFromHalf16(loc); + if (isInstructionUpdateForm(insn)) + error(getErrorLocation(loc) + "can't toc-optimize an update instruction: 0x" + - utohexstr(Instr)); - Instr = (Instr & 0xFFE00000) | 0x00020000; - writeInstrFromHalf16(Loc, Instr); + utohexstr(insn)); + writeFromHalf16(loc, (insn & 0xffe00000) | 0x00020000 | lo(val)); + } else { + write16(loc, lo(val)); } - write16(Loc, lo(Val)); break; case R_PPC64_ADDR16_LO_DS: case R_PPC64_TPREL16_LO_DS: { // DQ-form instructions use bits 28-31 as part of the instruction encoding // DS-form instructions only use bits 30-31. - uint32_t Inst = readInstrFromHalf16(Loc); - uint16_t Mask = isDQFormInstruction(Inst) ? 0xF : 0x3; - checkAlignment(Loc, lo(Val), Mask + 1, OriginalType); - if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) { + uint32_t insn = readFromHalf16(loc); + uint16_t mask = isDQFormInstruction(insn) ? 0xf : 0x3; + checkAlignment(loc, lo(val), mask + 1, originalType); + if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) { // When the high-adjusted part of a toc relocation evalutes to 0, it is // changed into a nop. The lo part then needs to be updated to use the toc // pointer register r2, as the base register. - if (isInstructionUpdateForm(Inst)) - error(getErrorLocation(Loc) + + if (isInstructionUpdateForm(insn)) + error(getErrorLocation(loc) + "Can't toc-optimize an update instruction: 0x" + - Twine::utohexstr(Inst)); - Inst = (Inst & 0xFFE0000F) | 0x00020000; - writeInstrFromHalf16(Loc, Inst); + Twine::utohexstr(insn)); + insn &= 0xffe00000 | mask; + writeFromHalf16(loc, insn | 0x00020000 | lo(val)); + } else { + write16(loc, (read16(loc) & mask) | lo(val)); } - write16(Loc, (read16(Loc) & Mask) | lo(Val)); } break; - case R_PPC64_ADDR32: + case R_PPC64_TPREL16: + checkInt(loc, val, 16, originalType); + write16(loc, val); + break; case R_PPC64_REL32: - checkInt(Loc, Val, 32, Type); - write32(Loc, Val); + checkInt(loc, val, 32, type); + write32(loc, val); break; case R_PPC64_ADDR64: case R_PPC64_REL64: case R_PPC64_TOC: - write64(Loc, Val); + write64(loc, val); break; case R_PPC64_REL14: { - uint32_t Mask = 0x0000FFFC; - checkInt(Loc, Val, 16, Type); - checkAlignment(Loc, Val, 4, Type); - write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask)); + uint32_t mask = 0x0000FFFC; + checkInt(loc, val, 16, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); break; } case R_PPC64_REL24: { - uint32_t Mask = 0x03FFFFFC; - checkInt(Loc, Val, 26, Type); - checkAlignment(Loc, Val, 4, Type); - write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask)); + uint32_t mask = 0x03FFFFFC; + checkInt(loc, val, 26, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); break; } case R_PPC64_DTPREL64: - write64(Loc, Val - DynamicThreadPointerOffset); + write64(loc, val - dynamicThreadPointerOffset); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } -bool PPC64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const { - if (Type != R_PPC64_REL14 && Type != R_PPC64_REL24) +bool PPC64::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { + if (type != R_PPC64_REL14 && type != R_PPC64_REL24) return false; // If a function is in the Plt it needs to be called with a call-stub. - if (S.isInPlt()) + if (s.isInPlt()) return true; // If a symbol is a weak undefined and we are compiling an executable // it doesn't need a range-extending thunk since it can't be called. - if (S.isUndefWeak() && !Config->Shared) + if (s.isUndefWeak() && !config->shared) return false; // If the offset exceeds the range of the branch type then it will need // a range-extending thunk. - return !inBranchRange(Type, BranchAddr, S.getVA()); + // See the comment in getRelocTargetVA() about R_PPC64_CALL. + return !inBranchRange(type, branchAddr, + s.getVA() + + getPPC64GlobalEntryToLocalEntryOffset(s.stOther)); } -bool PPC64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { - int64_t Offset = Dst - Src; - if (Type == R_PPC64_REL14) - return isInt<16>(Offset); - if (Type == R_PPC64_REL24) - return isInt<26>(Offset); +uint32_t PPC64::getThunkSectionSpacing() const { + // See comment in Arch/ARM.cpp for a more detailed explanation of + // getThunkSectionSpacing(). For PPC64 we pick the constant here based on + // R_PPC64_REL24, which is used by unconditional branch instructions. + // 0x2000000 = (1 << 24-1) * 4 + return 0x2000000; +} + +bool PPC64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + int64_t offset = dst - src; + if (type == R_PPC64_REL14) + return isInt<16>(offset); + if (type == R_PPC64_REL24) + return isInt<26>(offset); llvm_unreachable("unsupported relocation type used in branch"); } -RelExpr PPC64::adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const { - if (Expr == R_RELAX_TLS_GD_TO_IE) +RelExpr PPC64::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + if (expr == R_RELAX_TLS_GD_TO_IE) return R_RELAX_TLS_GD_TO_IE_GOT_OFF; - if (Expr == R_RELAX_TLS_LD_TO_LE) + if (expr == R_RELAX_TLS_LD_TO_LE) return R_RELAX_TLS_LD_TO_LE_ABS; - return Expr; + return expr; } // Reference: 3.7.4.1 of the 64-bit ELF V2 abi supplement. // The general dynamic code sequence for a global `x` uses 4 instructions. // Instruction Relocation Symbol // addis r3, r2, x@got@tlsgd@ha R_PPC64_GOT_TLSGD16_HA x // addi r3, r3, x@got@tlsgd@l R_PPC64_GOT_TLSGD16_LO x // bl __tls_get_addr(x@tlsgd) R_PPC64_TLSGD x // R_PPC64_REL24 __tls_get_addr // nop None None // // Relaxing to initial-exec entails: // 1) Convert the addis/addi pair that builds the address of the tls_index // struct for 'x' to an addis/ld pair that loads an offset from a got-entry. // 2) Convert the call to __tls_get_addr to a nop. // 3) Convert the nop following the call to an add of the loaded offset to the // thread pointer. // Since the nop must directly follow the call, the R_PPC64_TLSGD relocation is // used as the relaxation hint for both steps 2 and 3. -void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void PPC64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_PPC64_GOT_TLSGD16_HA: // This is relaxed from addis rT, r2, sym@got@tlsgd@ha to // addis rT, r2, sym@got@tprel@ha. - relocateOne(Loc, R_PPC64_GOT_TPREL16_HA, Val); + relocateOne(loc, R_PPC64_GOT_TPREL16_HA, val); return; + case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: { // Relax from addi r3, rA, sym@got@tlsgd@l to // ld r3, sym@got@tprel@l(rA) - uint32_t InputRegister = (readInstrFromHalf16(Loc) & (0x1f << 16)); - writeInstrFromHalf16(Loc, 0xE8600000 | InputRegister); - relocateOne(Loc, R_PPC64_GOT_TPREL16_LO_DS, Val); + uint32_t ra = (readFromHalf16(loc) & (0x1f << 16)); + writeFromHalf16(loc, 0xe8600000 | ra); + relocateOne(loc, R_PPC64_GOT_TPREL16_LO_DS, val); return; } case R_PPC64_TLSGD: - write32(Loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop - write32(Loc + 4, 0x7c636A14); // nop --> add r3, r3, r13 + write32(loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop + write32(loc + 4, 0x7c636A14); // nop --> add r3, r3, r13 return; default: llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); } } // The prologue for a split-stack function is expected to look roughly // like this: // .Lglobal_entry_point: // # TOC pointer initalization. // ... // .Llocal_entry_point: // # load the __private_ss member of the threads tcbhead. // ld r0,-0x7000-64(r13) // # subtract the functions stack size from the stack pointer. // addis r12, r1, ha(-stack-frame size) // addi r12, r12, l(-stack-frame size) // # compare needed to actual and branch to allocate_more_stack if more // # space is needed, otherwise fallthrough to 'normal' function body. // cmpld cr7,r12,r0 // blt- cr7, .Lallocate_more_stack // // -) The allocate_more_stack block might be placed after the split-stack // prologue and the `blt-` replaced with a `bge+ .Lnormal_func_body` // instead. // -) If either the addis or addi is not needed due to the stack size being // smaller then 32K or a multiple of 64K they will be replaced with a nop, // but there will always be 2 instructions the linker can overwrite for the // adjusted stack size. // // The linkers job here is to increase the stack size used in the addis/addi // pair by split-stack-size-adjust. // addis r12, r1, ha(-stack-frame size - split-stack-adjust-size) // addi r12, r12, l(-stack-frame size - split-stack-adjust-size) -bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End, - uint8_t StOther) const { +bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const { // If the caller has a global entry point adjust the buffer past it. The start // of the split-stack prologue will be at the local entry point. - Loc += getPPC64GlobalEntryToLocalEntryOffset(StOther); + loc += getPPC64GlobalEntryToLocalEntryOffset(stOther); // At the very least we expect to see a load of some split-stack data from the // tcb, and 2 instructions that calculate the ending stack address this // function will require. If there is not enough room for at least 3 // instructions it can't be a split-stack prologue. - if (Loc + 12 >= End) + if (loc + 12 >= end) return false; // First instruction must be `ld r0, -0x7000-64(r13)` - if (read32(Loc) != 0xe80d8fc0) + if (read32(loc) != 0xe80d8fc0) return false; - int16_t HiImm = 0; - int16_t LoImm = 0; + int16_t hiImm = 0; + int16_t loImm = 0; // First instruction can be either an addis if the frame size is larger then // 32K, or an addi if the size is less then 32K. - int32_t FirstInstr = read32(Loc + 4); - if (getPrimaryOpCode(FirstInstr) == 15) { - HiImm = FirstInstr & 0xFFFF; - } else if (getPrimaryOpCode(FirstInstr) == 14) { - LoImm = FirstInstr & 0xFFFF; + int32_t firstInstr = read32(loc + 4); + if (getPrimaryOpCode(firstInstr) == 15) { + hiImm = firstInstr & 0xFFFF; + } else if (getPrimaryOpCode(firstInstr) == 14) { + loImm = firstInstr & 0xFFFF; } else { return false; } // Second instruction is either an addi or a nop. If the first instruction was // an addi then LoImm is set and the second instruction must be a nop. - uint32_t SecondInstr = read32(Loc + 8); - if (!LoImm && getPrimaryOpCode(SecondInstr) == 14) { - LoImm = SecondInstr & 0xFFFF; - } else if (SecondInstr != 0x60000000) { + uint32_t secondInstr = read32(loc + 8); + if (!loImm && getPrimaryOpCode(secondInstr) == 14) { + loImm = secondInstr & 0xFFFF; + } else if (secondInstr != 0x60000000) { return false; } // The register operands of the first instruction should be the stack-pointer // (r1) as the input (RA) and r12 as the output (RT). If the second // instruction is not a nop, then it should use r12 as both input and output. - auto CheckRegOperands = [](uint32_t Instr, uint8_t ExpectedRT, - uint8_t ExpectedRA) { - return ((Instr & 0x3E00000) >> 21 == ExpectedRT) && - ((Instr & 0x1F0000) >> 16 == ExpectedRA); + auto checkRegOperands = [](uint32_t instr, uint8_t expectedRT, + uint8_t expectedRA) { + return ((instr & 0x3E00000) >> 21 == expectedRT) && + ((instr & 0x1F0000) >> 16 == expectedRA); }; - if (!CheckRegOperands(FirstInstr, 12, 1)) + if (!checkRegOperands(firstInstr, 12, 1)) return false; - if (SecondInstr != 0x60000000 && !CheckRegOperands(SecondInstr, 12, 12)) + if (secondInstr != 0x60000000 && !checkRegOperands(secondInstr, 12, 12)) return false; - int32_t StackFrameSize = (HiImm * 65536) + LoImm; + int32_t stackFrameSize = (hiImm * 65536) + loImm; // Check that the adjusted size doesn't overflow what we can represent with 2 // instructions. - if (StackFrameSize < Config->SplitStackAdjustSize + INT32_MIN) { - error(getErrorLocation(Loc) + "split-stack prologue adjustment overflows"); + if (stackFrameSize < config->splitStackAdjustSize + INT32_MIN) { + error(getErrorLocation(loc) + "split-stack prologue adjustment overflows"); return false; } - int32_t AdjustedStackFrameSize = - StackFrameSize - Config->SplitStackAdjustSize; + int32_t adjustedStackFrameSize = + stackFrameSize - config->splitStackAdjustSize; - LoImm = AdjustedStackFrameSize & 0xFFFF; - HiImm = (AdjustedStackFrameSize + 0x8000) >> 16; - if (HiImm) { - write32(Loc + 4, 0x3D810000 | (uint16_t)HiImm); + loImm = adjustedStackFrameSize & 0xFFFF; + hiImm = (adjustedStackFrameSize + 0x8000) >> 16; + if (hiImm) { + write32(loc + 4, 0x3D810000 | (uint16_t)hiImm); // If the low immediate is zero the second instruction will be a nop. - SecondInstr = LoImm ? 0x398C0000 | (uint16_t)LoImm : 0x60000000; - write32(Loc + 8, SecondInstr); + secondInstr = loImm ? 0x398C0000 | (uint16_t)loImm : 0x60000000; + write32(loc + 8, secondInstr); } else { // addi r12, r1, imm - write32(Loc + 4, (0x39810000) | (uint16_t)LoImm); - write32(Loc + 8, 0x60000000); + write32(loc + 4, (0x39810000) | (uint16_t)loImm); + write32(loc + 8, 0x60000000); } return true; } TargetInfo *elf::getPPC64TargetInfo() { - static PPC64 Target; - return &Target; + static PPC64 target; + return ⌖ } Index: vendor/lld/dist/ELF/Arch/RISCV.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/RISCV.cpp (revision 351287) +++ vendor/lld/dist/ELF/Arch/RISCV.cpp (revision 351288) @@ -1,279 +1,442 @@ //===- RISCV.cpp ----------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #include "InputFiles.h" +#include "SyntheticSections.h" #include "Target.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { class RISCV final : public TargetInfo { public: RISCV(); uint32_t calcEFlags() const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void writeGotHeader(uint8_t *buf) 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; + RelType getDynRel(RelType type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // end anonymous namespace -RISCV::RISCV() { NoneRel = R_RISCV_NONE; } +const uint64_t dtpOffset = 0x800; -static uint32_t getEFlags(InputFile *F) { - if (Config->Is64) - return cast>(F)->getObj().getHeader()->e_flags; - return cast>(F)->getObj().getHeader()->e_flags; +enum Op { + ADDI = 0x13, + AUIPC = 0x17, + JALR = 0x67, + LD = 0x3003, + LW = 0x2003, + SRLI = 0x5013, + SUB = 0x40000033, +}; + +enum Reg { + X_RA = 1, + X_T0 = 5, + X_T1 = 6, + X_T2 = 7, + X_T3 = 28, +}; + +static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; } +static uint32_t lo12(uint32_t val) { return val & 4095; } + +static uint32_t itype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t imm) { + return op | (rd << 7) | (rs1 << 15) | (imm << 20); } +static uint32_t rtype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t rs2) { + return op | (rd << 7) | (rs1 << 15) | (rs2 << 20); +} +static uint32_t utype(uint32_t op, uint32_t rd, uint32_t imm) { + return op | (rd << 7) | (imm << 12); +} +RISCV::RISCV() { + copyRel = R_RISCV_COPY; + noneRel = R_RISCV_NONE; + pltRel = R_RISCV_JUMP_SLOT; + relativeRel = R_RISCV_RELATIVE; + if (config->is64) { + symbolicRel = R_RISCV_64; + tlsModuleIndexRel = R_RISCV_TLS_DTPMOD64; + tlsOffsetRel = R_RISCV_TLS_DTPREL64; + tlsGotRel = R_RISCV_TLS_TPREL64; + } else { + symbolicRel = R_RISCV_32; + tlsModuleIndexRel = R_RISCV_TLS_DTPMOD32; + tlsOffsetRel = R_RISCV_TLS_DTPREL32; + tlsGotRel = R_RISCV_TLS_TPREL32; + } + gotRel = symbolicRel; + + // .got[0] = _DYNAMIC + gotBaseSymInGotPlt = false; + gotHeaderEntriesNum = 1; + + // .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map + gotPltHeaderEntriesNum = 2; + + pltEntrySize = 16; + pltHeaderSize = 32; +} + +static uint32_t getEFlags(InputFile *f) { + if (config->is64) + return cast>(f)->getObj().getHeader()->e_flags; + return cast>(f)->getObj().getHeader()->e_flags; +} + uint32_t RISCV::calcEFlags() const { - assert(!ObjectFiles.empty()); + assert(!objectFiles.empty()); - uint32_t Target = getEFlags(ObjectFiles.front()); + uint32_t target = getEFlags(objectFiles.front()); - for (InputFile *F : ObjectFiles) { - uint32_t EFlags = getEFlags(F); - if (EFlags & EF_RISCV_RVC) - Target |= EF_RISCV_RVC; + for (InputFile *f : objectFiles) { + uint32_t eflags = getEFlags(f); + if (eflags & EF_RISCV_RVC) + target |= EF_RISCV_RVC; - if ((EFlags & EF_RISCV_FLOAT_ABI) != (Target & EF_RISCV_FLOAT_ABI)) - error(toString(F) + + if ((eflags & EF_RISCV_FLOAT_ABI) != (target & EF_RISCV_FLOAT_ABI)) + error(toString(f) + ": cannot link object files with different floating-point ABI"); - if ((EFlags & EF_RISCV_RVE) != (Target & EF_RISCV_RVE)) - error(toString(F) + + if ((eflags & EF_RISCV_RVE) != (target & EF_RISCV_RVE)) + error(toString(f) + ": cannot link object files with different EF_RISCV_RVE"); } - return Target; + return target; } -RelExpr RISCV::getRelExpr(const RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +void RISCV::writeGotHeader(uint8_t *buf) const { + if (config->is64) + write64le(buf, mainPart->dynamic->getVA()); + else + write32le(buf, mainPart->dynamic->getVA()); +} + +void RISCV::writeGotPlt(uint8_t *buf, const Symbol &s) const { + if (config->is64) + write64le(buf, in.plt->getVA()); + else + write32le(buf, in.plt->getVA()); +} + +void RISCV::writePltHeader(uint8_t *buf) const { + // 1: auipc t2, %pcrel_hi(.got.plt) + // sub t1, t1, t3 + // l[wd] t3, %pcrel_lo(1b)(t2); t3 = _dl_runtime_resolve + // addi t1, t1, -pltHeaderSize-12; t1 = &.plt[i] - &.plt[0] + // addi t0, t2, %pcrel_lo(1b) + // srli t1, t1, (rv64?1:2); t1 = &.got.plt[i] - &.got.plt[0] + // l[wd] t0, Wordsize(t0); t0 = link_map + // jr t3 + uint32_t offset = in.gotPlt->getVA() - in.plt->getVA(); + uint32_t load = config->is64 ? LD : LW; + write32le(buf + 0, utype(AUIPC, X_T2, hi20(offset))); + write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T3)); + write32le(buf + 8, itype(load, X_T3, X_T2, lo12(offset))); + write32le(buf + 12, itype(ADDI, X_T1, X_T1, -target->pltHeaderSize - 12)); + write32le(buf + 16, itype(ADDI, X_T0, X_T2, lo12(offset))); + write32le(buf + 20, itype(SRLI, X_T1, X_T1, config->is64 ? 1 : 2)); + write32le(buf + 24, itype(load, X_T0, X_T0, config->wordsize)); + write32le(buf + 28, itype(JALR, 0, X_T3, 0)); +} + +void RISCV::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + // 1: auipc t3, %pcrel_hi(f@.got.plt) + // l[wd] t3, %pcrel_lo(1b)(t3) + // jalr t1, t3 + // nop + uint32_t offset = gotPltEntryAddr - pltEntryAddr; + write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset))); + write32le(buf + 4, itype(config->is64 ? LD : LW, X_T3, X_T3, lo12(offset))); + write32le(buf + 8, itype(JALR, X_T1, X_T3, 0)); + write32le(buf + 12, itype(ADDI, 0, 0, 0)); +} + +RelType RISCV::getDynRel(RelType type) const { + return type == target->symbolicRel ? type + : static_cast(R_RISCV_NONE); +} + +RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_RISCV_ADD8: + case R_RISCV_ADD16: + case R_RISCV_ADD32: + case R_RISCV_ADD64: + case R_RISCV_SET6: + case R_RISCV_SET8: + case R_RISCV_SET16: + case R_RISCV_SET32: + case R_RISCV_SUB6: + case R_RISCV_SUB8: + case R_RISCV_SUB16: + case R_RISCV_SUB32: + case R_RISCV_SUB64: + return R_RISCV_ADD; case R_RISCV_JAL: case R_RISCV_BRANCH: - case R_RISCV_CALL: case R_RISCV_PCREL_HI20: case R_RISCV_RVC_BRANCH: case R_RISCV_RVC_JUMP: case R_RISCV_32_PCREL: return R_PC; + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: + return R_PLT_PC; + case R_RISCV_GOT_HI20: + return R_GOT_PC; case R_RISCV_PCREL_LO12_I: case R_RISCV_PCREL_LO12_S: return R_RISCV_PC_INDIRECT; + case R_RISCV_TLS_GD_HI20: + return R_TLSGD_PC; + case R_RISCV_TLS_GOT_HI20: + config->hasStaticTlsModel = true; + return R_GOT_PC; + case R_RISCV_TPREL_HI20: + case R_RISCV_TPREL_LO12_I: + case R_RISCV_TPREL_LO12_S: + return R_TLS; case R_RISCV_RELAX: case R_RISCV_ALIGN: + case R_RISCV_TPREL_ADD: return R_HINT; default: return R_ABS; } } // Extract bits V[Begin:End], where range is inclusive, and Begin must be < 63. -static uint32_t extractBits(uint64_t V, uint32_t Begin, uint32_t End) { - return (V & ((1ULL << (Begin + 1)) - 1)) >> End; +static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) { + return (v & ((1ULL << (begin + 1)) - 1)) >> end; } -void RISCV::relocateOne(uint8_t *Loc, const RelType Type, - const uint64_t Val) const { - switch (Type) { +void RISCV::relocateOne(uint8_t *loc, const RelType type, + const uint64_t val) const { + const unsigned bits = config->wordsize * 8; + + switch (type) { case R_RISCV_32: - write32le(Loc, Val); + write32le(loc, val); return; case R_RISCV_64: - write64le(Loc, Val); + write64le(loc, val); return; case R_RISCV_RVC_BRANCH: { - checkInt(Loc, static_cast(Val) >> 1, 8, Type); - checkAlignment(Loc, Val, 2, Type); - uint16_t Insn = read16le(Loc) & 0xE383; - uint16_t Imm8 = extractBits(Val, 8, 8) << 12; - uint16_t Imm4_3 = extractBits(Val, 4, 3) << 10; - uint16_t Imm7_6 = extractBits(Val, 7, 6) << 5; - uint16_t Imm2_1 = extractBits(Val, 2, 1) << 3; - uint16_t Imm5 = extractBits(Val, 5, 5) << 2; - Insn |= Imm8 | Imm4_3 | Imm7_6 | Imm2_1 | Imm5; + checkInt(loc, static_cast(val) >> 1, 8, type); + checkAlignment(loc, val, 2, type); + uint16_t insn = read16le(loc) & 0xE383; + uint16_t imm8 = extractBits(val, 8, 8) << 12; + uint16_t imm4_3 = extractBits(val, 4, 3) << 10; + uint16_t imm7_6 = extractBits(val, 7, 6) << 5; + uint16_t imm2_1 = extractBits(val, 2, 1) << 3; + uint16_t imm5 = extractBits(val, 5, 5) << 2; + insn |= imm8 | imm4_3 | imm7_6 | imm2_1 | imm5; - write16le(Loc, Insn); + write16le(loc, insn); return; } case R_RISCV_RVC_JUMP: { - checkInt(Loc, static_cast(Val) >> 1, 11, Type); - checkAlignment(Loc, Val, 2, Type); - uint16_t Insn = read16le(Loc) & 0xE003; - uint16_t Imm11 = extractBits(Val, 11, 11) << 12; - uint16_t Imm4 = extractBits(Val, 4, 4) << 11; - uint16_t Imm9_8 = extractBits(Val, 9, 8) << 9; - uint16_t Imm10 = extractBits(Val, 10, 10) << 8; - uint16_t Imm6 = extractBits(Val, 6, 6) << 7; - uint16_t Imm7 = extractBits(Val, 7, 7) << 6; - uint16_t Imm3_1 = extractBits(Val, 3, 1) << 3; - uint16_t Imm5 = extractBits(Val, 5, 5) << 2; - Insn |= Imm11 | Imm4 | Imm9_8 | Imm10 | Imm6 | Imm7 | Imm3_1 | Imm5; + checkInt(loc, static_cast(val) >> 1, 11, type); + checkAlignment(loc, val, 2, type); + uint16_t insn = read16le(loc) & 0xE003; + uint16_t imm11 = extractBits(val, 11, 11) << 12; + uint16_t imm4 = extractBits(val, 4, 4) << 11; + uint16_t imm9_8 = extractBits(val, 9, 8) << 9; + uint16_t imm10 = extractBits(val, 10, 10) << 8; + uint16_t imm6 = extractBits(val, 6, 6) << 7; + uint16_t imm7 = extractBits(val, 7, 7) << 6; + uint16_t imm3_1 = extractBits(val, 3, 1) << 3; + uint16_t imm5 = extractBits(val, 5, 5) << 2; + insn |= imm11 | imm4 | imm9_8 | imm10 | imm6 | imm7 | imm3_1 | imm5; - write16le(Loc, Insn); + write16le(loc, insn); return; } case R_RISCV_RVC_LUI: { - int32_t Imm = ((Val + 0x800) >> 12); - checkUInt(Loc, Imm, 6, Type); - if (Imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0` - write16le(Loc, (read16le(Loc) & 0x0F83) | 0x4000); + int64_t imm = SignExtend64(val + 0x800, bits) >> 12; + checkInt(loc, imm, 6, type); + if (imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0` + write16le(loc, (read16le(loc) & 0x0F83) | 0x4000); } else { - uint16_t Imm17 = extractBits(Val + 0x800, 17, 17) << 12; - uint16_t Imm16_12 = extractBits(Val + 0x800, 16, 12) << 2; - write16le(Loc, (read16le(Loc) & 0xEF83) | Imm17 | Imm16_12); + uint16_t imm17 = extractBits(val + 0x800, 17, 17) << 12; + uint16_t imm16_12 = extractBits(val + 0x800, 16, 12) << 2; + write16le(loc, (read16le(loc) & 0xEF83) | imm17 | imm16_12); } return; } case R_RISCV_JAL: { - checkInt(Loc, static_cast(Val) >> 1, 20, Type); - checkAlignment(Loc, Val, 2, Type); + checkInt(loc, static_cast(val) >> 1, 20, type); + checkAlignment(loc, val, 2, type); - uint32_t Insn = read32le(Loc) & 0xFFF; - uint32_t Imm20 = extractBits(Val, 20, 20) << 31; - uint32_t Imm10_1 = extractBits(Val, 10, 1) << 21; - uint32_t Imm11 = extractBits(Val, 11, 11) << 20; - uint32_t Imm19_12 = extractBits(Val, 19, 12) << 12; - Insn |= Imm20 | Imm10_1 | Imm11 | Imm19_12; + uint32_t insn = read32le(loc) & 0xFFF; + uint32_t imm20 = extractBits(val, 20, 20) << 31; + uint32_t imm10_1 = extractBits(val, 10, 1) << 21; + uint32_t imm11 = extractBits(val, 11, 11) << 20; + uint32_t imm19_12 = extractBits(val, 19, 12) << 12; + insn |= imm20 | imm10_1 | imm11 | imm19_12; - write32le(Loc, Insn); + write32le(loc, insn); return; } case R_RISCV_BRANCH: { - checkInt(Loc, static_cast(Val) >> 1, 12, Type); - checkAlignment(Loc, Val, 2, Type); + checkInt(loc, static_cast(val) >> 1, 12, type); + checkAlignment(loc, val, 2, type); - uint32_t Insn = read32le(Loc) & 0x1FFF07F; - uint32_t Imm12 = extractBits(Val, 12, 12) << 31; - uint32_t Imm10_5 = extractBits(Val, 10, 5) << 25; - uint32_t Imm4_1 = extractBits(Val, 4, 1) << 8; - uint32_t Imm11 = extractBits(Val, 11, 11) << 7; - Insn |= Imm12 | Imm10_5 | Imm4_1 | Imm11; + uint32_t insn = read32le(loc) & 0x1FFF07F; + uint32_t imm12 = extractBits(val, 12, 12) << 31; + uint32_t imm10_5 = extractBits(val, 10, 5) << 25; + uint32_t imm4_1 = extractBits(val, 4, 1) << 8; + uint32_t imm11 = extractBits(val, 11, 11) << 7; + insn |= imm12 | imm10_5 | imm4_1 | imm11; - write32le(Loc, Insn); + write32le(loc, insn); return; } // auipc + jalr pair - case R_RISCV_CALL: { - checkInt(Loc, Val, 32, Type); - if (isInt<32>(Val)) { - relocateOne(Loc, R_RISCV_PCREL_HI20, Val); - relocateOne(Loc + 4, R_RISCV_PCREL_LO12_I, Val); + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: { + int64_t hi = SignExtend64(val + 0x800, bits) >> 12; + checkInt(loc, hi, 20, type); + if (isInt<20>(hi)) { + relocateOne(loc, R_RISCV_PCREL_HI20, val); + relocateOne(loc + 4, R_RISCV_PCREL_LO12_I, val); } return; } + case R_RISCV_GOT_HI20: case R_RISCV_PCREL_HI20: + case R_RISCV_TLS_GD_HI20: + case R_RISCV_TLS_GOT_HI20: + case R_RISCV_TPREL_HI20: case R_RISCV_HI20: { - checkInt(Loc, Val, 32, Type); - uint32_t Hi = Val + 0x800; - write32le(Loc, (read32le(Loc) & 0xFFF) | (Hi & 0xFFFFF000)); + uint64_t hi = val + 0x800; + checkInt(loc, SignExtend64(hi, bits) >> 12, 20, type); + write32le(loc, (read32le(loc) & 0xFFF) | (hi & 0xFFFFF000)); return; } case R_RISCV_PCREL_LO12_I: + case R_RISCV_TPREL_LO12_I: case R_RISCV_LO12_I: { - checkInt(Loc, Val, 32, Type); - uint32_t Hi = Val + 0x800; - uint32_t Lo = Val - (Hi & 0xFFFFF000); - write32le(Loc, (read32le(Loc) & 0xFFFFF) | ((Lo & 0xFFF) << 20)); + uint64_t hi = (val + 0x800) >> 12; + uint64_t lo = val - (hi << 12); + write32le(loc, (read32le(loc) & 0xFFFFF) | ((lo & 0xFFF) << 20)); return; } case R_RISCV_PCREL_LO12_S: + case R_RISCV_TPREL_LO12_S: case R_RISCV_LO12_S: { - checkInt(Loc, Val, 32, Type); - uint32_t Hi = Val + 0x800; - uint32_t Lo = Val - (Hi & 0xFFFFF000); - uint32_t Imm11_5 = extractBits(Lo, 11, 5) << 25; - uint32_t Imm4_0 = extractBits(Lo, 4, 0) << 7; - write32le(Loc, (read32le(Loc) & 0x1FFF07F) | Imm11_5 | Imm4_0); + uint64_t hi = (val + 0x800) >> 12; + uint64_t lo = val - (hi << 12); + uint32_t imm11_5 = extractBits(lo, 11, 5) << 25; + uint32_t imm4_0 = extractBits(lo, 4, 0) << 7; + write32le(loc, (read32le(loc) & 0x1FFF07F) | imm11_5 | imm4_0); return; } case R_RISCV_ADD8: - *Loc += Val; + *loc += val; return; case R_RISCV_ADD16: - write16le(Loc, read16le(Loc) + Val); + write16le(loc, read16le(loc) + val); return; case R_RISCV_ADD32: - write32le(Loc, read32le(Loc) + Val); + write32le(loc, read32le(loc) + val); return; case R_RISCV_ADD64: - write64le(Loc, read64le(Loc) + Val); + write64le(loc, read64le(loc) + val); return; case R_RISCV_SUB6: - *Loc = (*Loc & 0xc0) | (((*Loc & 0x3f) - Val) & 0x3f); + *loc = (*loc & 0xc0) | (((*loc & 0x3f) - val) & 0x3f); return; case R_RISCV_SUB8: - *Loc -= Val; + *loc -= val; return; case R_RISCV_SUB16: - write16le(Loc, read16le(Loc) - Val); + write16le(loc, read16le(loc) - val); return; case R_RISCV_SUB32: - write32le(Loc, read32le(Loc) - Val); + write32le(loc, read32le(loc) - val); return; case R_RISCV_SUB64: - write64le(Loc, read64le(Loc) - Val); + write64le(loc, read64le(loc) - val); return; case R_RISCV_SET6: - *Loc = (*Loc & 0xc0) | (Val & 0x3f); + *loc = (*loc & 0xc0) | (val & 0x3f); return; case R_RISCV_SET8: - *Loc = Val; + *loc = val; return; case R_RISCV_SET16: - write16le(Loc, Val); + write16le(loc, val); return; case R_RISCV_SET32: case R_RISCV_32_PCREL: - write32le(Loc, Val); + write32le(loc, val); return; + case R_RISCV_TLS_DTPREL32: + write32le(loc, val - dtpOffset); + break; + case R_RISCV_TLS_DTPREL64: + write64le(loc, val - dtpOffset); + break; + case R_RISCV_ALIGN: case R_RISCV_RELAX: return; // Ignored (for now) case R_RISCV_NONE: return; // Do nothing // These are handled by the dynamic linker case R_RISCV_RELATIVE: case R_RISCV_COPY: case R_RISCV_JUMP_SLOT: // GP-relative relocations are only produced after relaxation, which // we don't support for now case R_RISCV_GPREL_I: case R_RISCV_GPREL_S: default: - error(getErrorLocation(Loc) + - "unimplemented relocation: " + toString(Type)); + error(getErrorLocation(loc) + + "unimplemented relocation: " + toString(type)); return; } } TargetInfo *elf::getRISCVTargetInfo() { - static RISCV Target; - return &Target; + static RISCV target; + return ⌖ } Index: vendor/lld/dist/ELF/Arch/SPARCV9.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/SPARCV9.cpp (revision 351287) +++ vendor/lld/dist/ELF/Arch/SPARCV9.cpp (revision 351288) @@ -1,149 +1,149 @@ //===- SPARCV9.cpp --------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #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; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { class SPARCV9 final : public TargetInfo { public: SPARCV9(); - 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, RelType Type, uint64_t Val) const override; + 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, RelType type, uint64_t val) const override; }; } // namespace SPARCV9::SPARCV9() { - CopyRel = R_SPARC_COPY; - GotRel = R_SPARC_GLOB_DAT; - NoneRel = R_SPARC_NONE; - PltRel = R_SPARC_JMP_SLOT; - RelativeRel = R_SPARC_RELATIVE; - GotEntrySize = 8; - PltEntrySize = 32; - PltHeaderSize = 4 * PltEntrySize; + copyRel = R_SPARC_COPY; + gotRel = R_SPARC_GLOB_DAT; + noneRel = R_SPARC_NONE; + pltRel = R_SPARC_JMP_SLOT; + relativeRel = R_SPARC_RELATIVE; + symbolicRel = R_SPARC_64; + pltEntrySize = 32; + pltHeaderSize = 4 * pltEntrySize; - PageSize = 8192; - DefaultMaxPageSize = 0x100000; - DefaultImageBase = 0x100000; + defaultCommonPageSize = 8192; + defaultMaxPageSize = 0x100000; + defaultImageBase = 0x100000; } -RelExpr SPARCV9::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_SPARC_32: case R_SPARC_UA32: case R_SPARC_64: case R_SPARC_UA64: return R_ABS; case R_SPARC_PC10: case R_SPARC_PC22: case R_SPARC_DISP32: case R_SPARC_WDISP30: return R_PC; case R_SPARC_GOT10: return R_GOT_OFF; case R_SPARC_GOT22: return R_GOT_OFF; case R_SPARC_WPLT30: return R_PLT_PC; case R_SPARC_NONE: return R_NONE; default: - return R_INVALID; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } -void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void SPARCV9::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_SPARC_32: case R_SPARC_UA32: // V-word32 - checkUInt(Loc, Val, 32, Type); - write32be(Loc, Val); + checkUInt(loc, val, 32, type); + write32be(loc, val); break; case R_SPARC_DISP32: // V-disp32 - checkInt(Loc, Val, 32, Type); - write32be(Loc, Val); + checkInt(loc, val, 32, type); + write32be(loc, val); break; case R_SPARC_WDISP30: case R_SPARC_WPLT30: // V-disp30 - checkInt(Loc, Val, 32, Type); - write32be(Loc, (read32be(Loc) & ~0x3fffffff) | ((Val >> 2) & 0x3fffffff)); + checkInt(loc, val, 32, type); + write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff)); break; case R_SPARC_22: // V-imm22 - checkUInt(Loc, Val, 22, Type); - write32be(Loc, (read32be(Loc) & ~0x003fffff) | (Val & 0x003fffff)); + checkUInt(loc, val, 22, type); + write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff)); break; case R_SPARC_GOT22: case R_SPARC_PC22: // T-imm22 - write32be(Loc, (read32be(Loc) & ~0x003fffff) | ((Val >> 10) & 0x003fffff)); + write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff)); break; case R_SPARC_WDISP19: // V-disp19 - checkInt(Loc, Val, 21, Type); - write32be(Loc, (read32be(Loc) & ~0x0007ffff) | ((Val >> 2) & 0x0007ffff)); + checkInt(loc, val, 21, type); + write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff)); break; case R_SPARC_GOT10: case R_SPARC_PC10: // T-simm10 - write32be(Loc, (read32be(Loc) & ~0x000003ff) | (Val & 0x000003ff)); + write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff)); break; case R_SPARC_64: case R_SPARC_UA64: - case R_SPARC_GLOB_DAT: // V-xword64 - write64be(Loc, Val); + write64be(loc, val); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -void SPARCV9::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t PltData[] = { +void SPARCV9::writePlt(uint8_t *buf, uint64_t gotEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t pltData[] = { 0x03, 0x00, 0x00, 0x00, // sethi (. - .PLT0), %g1 0x30, 0x68, 0x00, 0x00, // ba,a %xcc, .PLT1 0x01, 0x00, 0x00, 0x00, // nop 0x01, 0x00, 0x00, 0x00, // nop 0x01, 0x00, 0x00, 0x00, // nop 0x01, 0x00, 0x00, 0x00, // nop 0x01, 0x00, 0x00, 0x00, // nop 0x01, 0x00, 0x00, 0x00 // nop }; - memcpy(Buf, PltData, sizeof(PltData)); + memcpy(buf, pltData, sizeof(pltData)); - uint64_t Off = getPltEntryOffset(Index); - relocateOne(Buf, R_SPARC_22, Off); - relocateOne(Buf + 4, R_SPARC_WDISP19, -(Off + 4 - PltEntrySize)); + uint64_t off = pltHeaderSize + pltEntrySize * index; + relocateOne(buf, R_SPARC_22, off); + relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize)); } TargetInfo *elf::getSPARCV9TargetInfo() { - static SPARCV9 Target; - return &Target; + static SPARCV9 target; + return ⌖ } Index: vendor/lld/dist/ELF/Arch/X86.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/X86.cpp (revision 351287) +++ vendor/lld/dist/ELF/Arch/X86.cpp (revision 351288) @@ -1,554 +1,554 @@ //===- X86.cpp ------------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #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; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { class X86 : public TargetInfo { public: X86(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override; - void writeGotPltHeader(uint8_t *Buf) 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, RelType Type, uint64_t Val) const override; + int getTlsGdRelaxSkip(RelType type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; + void writeGotPltHeader(uint8_t *buf) 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, RelType type, uint64_t val) const override; - RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const override; - void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace X86::X86() { - CopyRel = R_386_COPY; - GotRel = R_386_GLOB_DAT; - NoneRel = R_386_NONE; - PltRel = R_386_JUMP_SLOT; - IRelativeRel = R_386_IRELATIVE; - RelativeRel = R_386_RELATIVE; - TlsGotRel = R_386_TLS_TPOFF; - TlsModuleIndexRel = R_386_TLS_DTPMOD32; - TlsOffsetRel = R_386_TLS_DTPOFF32; - GotEntrySize = 4; - GotPltEntrySize = 4; - PltEntrySize = 16; - PltHeaderSize = 16; - TlsGdRelaxSkip = 2; - TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 + copyRel = R_386_COPY; + gotRel = R_386_GLOB_DAT; + noneRel = R_386_NONE; + pltRel = R_386_JUMP_SLOT; + iRelativeRel = R_386_IRELATIVE; + relativeRel = R_386_RELATIVE; + symbolicRel = R_386_32; + tlsGotRel = R_386_TLS_TPOFF; + tlsModuleIndexRel = R_386_TLS_DTPMOD32; + tlsOffsetRel = R_386_TLS_DTPOFF32; + pltEntrySize = 16; + pltHeaderSize = 16; + trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 // Align to the non-PAE large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. - DefaultImageBase = 0x400000; + defaultImageBase = 0x400000; } -static bool hasBaseReg(uint8_t ModRM) { return (ModRM & 0xc7) != 0x5; } +int X86::getTlsGdRelaxSkip(RelType type) const { + return 2; +} -RelExpr X86::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr X86::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + // There are 4 different TLS variable models with varying degrees of + // flexibility and performance. LocalExec and InitialExec models are fast but + // less-flexible models. If they are in use, we set DF_STATIC_TLS flag in the + // dynamic section to let runtime know about that. + if (type == R_386_TLS_LE || type == R_386_TLS_LE_32 || type == R_386_TLS_IE || + type == R_386_TLS_GOTIE) + config->hasStaticTlsModel = true; + + switch (type) { case R_386_8: case R_386_16: case R_386_32: - case R_386_TLS_LDO_32: return R_ABS; + case R_386_TLS_LDO_32: + return R_DTPREL; case R_386_TLS_GD: - return R_TLSGD_GOT_FROM_END; + return R_TLSGD_GOTPLT; case R_386_TLS_LDM: - return R_TLSLD_GOT_FROM_END; + return R_TLSLD_GOTPLT; case R_386_PLT32: return R_PLT_PC; case R_386_PC8: case R_386_PC16: case R_386_PC32: return R_PC; case R_386_GOTPC: - return R_GOTONLY_PC_FROM_END; + return R_GOTPLTONLY_PC; case R_386_TLS_IE: return R_GOT; case R_386_GOT32: case R_386_GOT32X: // 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. // // 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(%ebx). // // 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@GOT(%ebx) 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 + // 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; + // the byte, we can determine whether the instruction uses the operand as an + // absolute address (R_GOT) or a register-relative address (R_GOTPLT). + return (loc[-1] & 0xc7) == 0x5 ? R_GOT : R_GOTPLT; case R_386_TLS_GOTIE: - return R_GOT_FROM_END; + return R_GOTPLT; case R_386_GOTOFF: - return R_GOTREL_FROM_END; + return R_GOTPLTREL; case R_386_TLS_LE: return R_TLS; case R_386_TLS_LE_32: return R_NEG_TLS; case R_386_NONE: return R_NONE; default: - return R_INVALID; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } -RelExpr X86::adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const { - switch (Expr) { +RelExpr X86::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + switch (expr) { default: - return Expr; + return expr; case R_RELAX_TLS_GD_TO_IE: - return R_RELAX_TLS_GD_TO_IE_END; + return R_RELAX_TLS_GD_TO_IE_GOTPLT; case R_RELAX_TLS_GD_TO_LE: return R_RELAX_TLS_GD_TO_LE_NEG; } } -void X86::writeGotPltHeader(uint8_t *Buf) const { - write32le(Buf, In.Dynamic->getVA()); +void X86::writeGotPltHeader(uint8_t *buf) const { + write32le(buf, mainPart->dynamic->getVA()); } -void X86::writeGotPlt(uint8_t *Buf, const Symbol &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); + write32le(buf, s.getPltVA() + 6); } -void X86::writeIgotPlt(uint8_t *Buf, const Symbol &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()); + write32le(buf, s.getVA()); } -RelType X86::getDynRel(RelType Type) const { - if (Type == R_386_TLS_LE) +RelType X86::getDynRel(RelType type) const { + if (type == R_386_TLS_LE) return R_386_TLS_TPOFF; - if (Type == R_386_TLS_LE_32) + if (type == R_386_TLS_LE_32) return R_386_TLS_TPOFF32; - return Type; + return type; } -void X86::writePltHeader(uint8_t *Buf) const { - if (Config->Pic) { - const uint8_t V[] = { - 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl GOTPLT+4(%ebx) - 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *GOTPLT+8(%ebx) +void X86::writePltHeader(uint8_t *buf) const { + if (config->isPic) { + const uint8_t v[] = { + 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl 4(%ebx) + 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *8(%ebx) 0x90, 0x90, 0x90, 0x90 // nop }; - memcpy(Buf, V, sizeof(V)); - - uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); - uint32_t GotPlt = In.GotPlt->getVA() - Ebx; - write32le(Buf + 2, GotPlt + 4); - write32le(Buf + 8, GotPlt + 8); + memcpy(buf, v, sizeof(v)); return; } - const uint8_t PltData[] = { + const uint8_t pltData[] = { 0xff, 0x35, 0, 0, 0, 0, // pushl (GOTPLT+4) 0xff, 0x25, 0, 0, 0, 0, // jmp *(GOTPLT+8) 0x90, 0x90, 0x90, 0x90, // nop }; - memcpy(Buf, PltData, sizeof(PltData)); - uint32_t GotPlt = In.GotPlt->getVA(); - write32le(Buf + 2, GotPlt + 4); - write32le(Buf + 8, GotPlt + 8); + memcpy(buf, pltData, sizeof(pltData)); + uint32_t gotPlt = in.gotPlt->getVA(); + write32le(buf + 2, gotPlt + 4); + write32le(buf + 8, gotPlt + 8); } -void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Inst[] = { - 0xff, 0x00, 0, 0, 0, 0, // jmp *foo_in_GOT or jmp *foo@GOT(%ebx) - 0x68, 0, 0, 0, 0, // pushl $reloc_offset - 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC - }; - memcpy(Buf, Inst, sizeof(Inst)); - - if (Config->Pic) { - // jmp *foo@GOT(%ebx) - uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); - Buf[1] = 0xa3; - write32le(Buf + 2, GotPltEntryAddr - Ebx); +void X86::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + if (config->isPic) { + const uint8_t inst[] = { + 0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx) + 0x68, 0, 0, 0, 0, // pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC + }; + memcpy(buf, inst, sizeof(inst)); + write32le(buf + 2, gotPltEntryAddr - in.gotPlt->getVA()); } else { - // jmp *foo_in_GOT - Buf[1] = 0x25; - write32le(Buf + 2, GotPltEntryAddr); + const uint8_t inst[] = { + 0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT + 0x68, 0, 0, 0, 0, // pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC + }; + memcpy(buf, inst, sizeof(inst)); + write32le(buf + 2, gotPltEntryAddr); } - write32le(Buf + 7, RelOff); - write32le(Buf + 12, -getPltEntryOffset(Index) - 16); + write32le(buf + 7, relOff); + write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16); } -int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const { - switch (Type) { +int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { case R_386_8: case R_386_PC8: - return SignExtend64<8>(*Buf); + return SignExtend64<8>(*buf); case R_386_16: case R_386_PC16: - return SignExtend64<16>(read16le(Buf)); + return SignExtend64<16>(read16le(buf)); case R_386_32: 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_TLS_LDO_32: case R_386_TLS_LE: - return SignExtend64<32>(read32le(Buf)); + return SignExtend64<32>(read32le(buf)); default: return 0; } } -void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +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. - checkIntUInt(Loc, Val, 8, Type); - *Loc = Val; + checkIntUInt(loc, val, 8, type); + *loc = val; break; case R_386_PC8: - checkInt(Loc, Val, 8, Type); - *Loc = Val; + checkInt(loc, val, 8, type); + *loc = val; break; case R_386_16: - checkIntUInt(Loc, Val, 16, Type); - write16le(Loc, Val); + checkIntUInt(loc, val, 16, type); + write16le(loc, val); break; case R_386_PC16: // R_386_PC16 is normally used with 16 bit code. In that situation // the PC is 16 bits, just like the addend. This means that it can // point from any 16 bit address to any other if the possibility // of wrapping is included. // The only restriction we have to check then is that the destination // address fits in 16 bits. That is impossible to do here. The problem is // that we are passed the final value, which already had the // current location subtracted from it. // We just check that Val fits in 17 bits. This misses some cases, but // should have no false positives. - checkInt(Loc, Val, 17, Type); - write16le(Loc, Val); + checkInt(loc, val, 17, type); + write16le(loc, val); break; 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(Loc, Val, 32, Type); - write32le(Loc, Val); + checkInt(loc, val, 32, type); + write32le(loc, val); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -void X86::relaxTlsGdToLe(uint8_t *Loc, RelType 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 // to // movl %gs:0,%eax // subl $x@ntpoff,%eax - const uint8_t Inst[] = { + const uint8_t inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax 0x81, 0xe8, 0, 0, 0, 0, // subl Val(%ebx), %eax }; - memcpy(Loc - 3, Inst, sizeof(Inst)); - write32le(Loc + 5, Val); + memcpy(loc - 3, inst, sizeof(inst)); + write32le(loc + 5, val); } -void X86::relaxTlsGdToIe(uint8_t *Loc, RelType 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 // to // movl %gs:0, %eax // addl x@gotntpoff(%ebx), %eax - const uint8_t Inst[] = { + const uint8_t inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax 0x03, 0x83, 0, 0, 0, 0, // addl Val(%ebx), %eax }; - memcpy(Loc - 3, Inst, sizeof(Inst)); - write32le(Loc + 5, Val); + memcpy(loc - 3, inst, sizeof(inst)); + write32le(loc + 5, val); } // 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, RelType 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 // position dependent code. - uint8_t Reg = (Loc[-1] >> 3) & 7; + uint8_t reg = (loc[-1] >> 3) & 7; - if (Type == R_386_TLS_IE) { - if (Loc[-1] == 0xa1) { + if (type == R_386_TLS_IE) { + if (loc[-1] == 0xa1) { // "movl foo@indntpoff,%eax" -> "movl $foo,%eax" // This case is different from the generic case below because // this is a 5 byte instruction while below is 6 bytes. - Loc[-1] = 0xb8; - } else if (Loc[-2] == 0x8b) { + loc[-1] = 0xb8; + } else if (loc[-2] == 0x8b) { // "movl foo@indntpoff,%reg" -> "movl $foo,%reg" - Loc[-2] = 0xc7; - Loc[-1] = 0xc0 | Reg; + loc[-2] = 0xc7; + loc[-1] = 0xc0 | reg; } else { // "addl foo@indntpoff,%reg" -> "addl $foo,%reg" - Loc[-2] = 0x81; - Loc[-1] = 0xc0 | Reg; + loc[-2] = 0x81; + loc[-1] = 0xc0 | reg; } } else { - assert(Type == R_386_TLS_GOTIE); - if (Loc[-2] == 0x8b) { + assert(type == R_386_TLS_GOTIE); + if (loc[-2] == 0x8b) { // "movl foo@gottpoff(%rip),%reg" -> "movl $foo,%reg" - Loc[-2] = 0xc7; - Loc[-1] = 0xc0 | Reg; + loc[-2] = 0xc7; + loc[-1] = 0xc0 | reg; } else { // "addl foo@gotntpoff(%rip),%reg" -> "leal foo(%reg),%reg" - Loc[-2] = 0x8d; - Loc[-1] = 0x80 | (Reg << 3) | Reg; + loc[-2] = 0x8d; + loc[-1] = 0x80 | (reg << 3) | reg; } } - write32le(Loc, Val); + write32le(loc, val); } -void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { - if (Type == R_386_TLS_LDO_32) { - write32le(Loc, Val); +void X86::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_386_TLS_LDO_32) { + write32le(loc, val); return; } // Convert // leal foo(%reg),%eax // call ___tls_get_addr // to // movl %gs:0,%eax // nop // leal 0(%esi,1),%esi - const uint8_t Inst[] = { + const uint8_t inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax 0x90, // nop 0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi }; - memcpy(Loc - 2, Inst, sizeof(Inst)); + memcpy(loc - 2, inst, sizeof(inst)); } namespace { class RetpolinePic : public X86 { public: RetpolinePic(); - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; class RetpolineNoPic : public X86 { public: RetpolineNoPic(); - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; } // namespace RetpolinePic::RetpolinePic() { - PltHeaderSize = 48; - PltEntrySize = 32; + pltHeaderSize = 48; + pltEntrySize = 32; } -void RetpolinePic::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - write32le(Buf, S.getPltVA() + 17); +void RetpolinePic::writeGotPlt(uint8_t *buf, const Symbol &s) const { + write32le(buf, s.getPltVA() + 17); } -void RetpolinePic::writePltHeader(uint8_t *Buf) const { - const uint8_t Insn[] = { - 0xff, 0xb3, 0, 0, 0, 0, // 0: pushl GOTPLT+4(%ebx) +void RetpolinePic::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { + 0xff, 0xb3, 4, 0, 0, 0, // 0: pushl 4(%ebx) 0x50, // 6: pushl %eax - 0x8b, 0x83, 0, 0, 0, 0, // 7: mov GOTPLT+8(%ebx), %eax + 0x8b, 0x83, 8, 0, 0, 0, // 7: mov 8(%ebx), %eax 0xe8, 0x0e, 0x00, 0x00, 0x00, // d: call next 0xf3, 0x90, // 12: loop: pause 0x0f, 0xae, 0xe8, // 14: lfence 0xeb, 0xf9, // 17: jmp loop 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16 0x89, 0x0c, 0x24, // 20: next: mov %ecx, (%esp) 0x8b, 0x4c, 0x24, 0x04, // 23: mov 0x4(%esp), %ecx 0x89, 0x44, 0x24, 0x04, // 27: mov %eax ,0x4(%esp) 0x89, 0xc8, // 2b: mov %ecx, %eax 0x59, // 2d: pop %ecx 0xc3, // 2e: ret 0xcc, // 2f: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); - - uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); - uint32_t GotPlt = In.GotPlt->getVA() - Ebx; - write32le(Buf + 2, GotPlt + 4); - write32le(Buf + 9, GotPlt + 8); + memcpy(buf, insn, sizeof(insn)); } -void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Insn[] = { +void RetpolinePic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { 0x50, // pushl %eax 0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax 0xe8, 0, 0, 0, 0, // call plt+0x20 0xe9, 0, 0, 0, 0, // jmp plt+0x12 0x68, 0, 0, 0, 0, // pushl $reloc_offset 0xe9, 0, 0, 0, 0, // jmp plt+0 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); - uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); - unsigned Off = getPltEntryOffset(Index); - write32le(Buf + 3, GotPltEntryAddr - Ebx); - write32le(Buf + 8, -Off - 12 + 32); - write32le(Buf + 13, -Off - 17 + 18); - write32le(Buf + 18, RelOff); - write32le(Buf + 23, -Off - 27); + uint32_t ebx = in.gotPlt->getVA(); + unsigned off = pltHeaderSize + pltEntrySize * index; + write32le(buf + 3, gotPltEntryAddr - ebx); + write32le(buf + 8, -off - 12 + 32); + write32le(buf + 13, -off - 17 + 18); + write32le(buf + 18, relOff); + write32le(buf + 23, -off - 27); } RetpolineNoPic::RetpolineNoPic() { - PltHeaderSize = 48; - PltEntrySize = 32; + pltHeaderSize = 48; + pltEntrySize = 32; } -void RetpolineNoPic::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - write32le(Buf, S.getPltVA() + 16); +void RetpolineNoPic::writeGotPlt(uint8_t *buf, const Symbol &s) const { + write32le(buf, s.getPltVA() + 16); } -void RetpolineNoPic::writePltHeader(uint8_t *Buf) const { - const uint8_t Insn[] = { +void RetpolineNoPic::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { 0xff, 0x35, 0, 0, 0, 0, // 0: pushl GOTPLT+4 0x50, // 6: pushl %eax 0xa1, 0, 0, 0, 0, // 7: mov GOTPLT+8, %eax 0xe8, 0x0f, 0x00, 0x00, 0x00, // c: call next 0xf3, 0x90, // 11: loop: pause 0x0f, 0xae, 0xe8, // 13: lfence 0xeb, 0xf9, // 16: jmp loop 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 18: int3 0xcc, 0xcc, 0xcc, // 1f: int3; .align 16 0x89, 0x0c, 0x24, // 20: next: mov %ecx, (%esp) 0x8b, 0x4c, 0x24, 0x04, // 23: mov 0x4(%esp), %ecx 0x89, 0x44, 0x24, 0x04, // 27: mov %eax ,0x4(%esp) 0x89, 0xc8, // 2b: mov %ecx, %eax 0x59, // 2d: pop %ecx 0xc3, // 2e: ret 0xcc, // 2f: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); - uint32_t GotPlt = In.GotPlt->getVA(); - write32le(Buf + 2, GotPlt + 4); - write32le(Buf + 8, GotPlt + 8); + uint32_t gotPlt = in.gotPlt->getVA(); + write32le(buf + 2, gotPlt + 4); + write32le(buf + 8, gotPlt + 8); } -void RetpolineNoPic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Insn[] = { +void RetpolineNoPic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { 0x50, // 0: pushl %eax 0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax 0xe8, 0, 0, 0, 0, // 6: call plt+0x20 0xe9, 0, 0, 0, 0, // b: jmp plt+0x11 0x68, 0, 0, 0, 0, // 10: pushl $reloc_offset 0xe9, 0, 0, 0, 0, // 15: jmp plt+0 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding 0xcc, // 1f: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); - unsigned Off = getPltEntryOffset(Index); - write32le(Buf + 2, GotPltEntryAddr); - write32le(Buf + 7, -Off - 11 + 32); - write32le(Buf + 12, -Off - 16 + 17); - write32le(Buf + 17, RelOff); - write32le(Buf + 22, -Off - 26); + unsigned off = pltHeaderSize + pltEntrySize * index; + write32le(buf + 2, gotPltEntryAddr); + write32le(buf + 7, -off - 11 + 32); + write32le(buf + 12, -off - 16 + 17); + write32le(buf + 17, relOff); + write32le(buf + 22, -off - 26); } TargetInfo *elf::getX86TargetInfo() { - if (Config->ZRetpolineplt) { - if (Config->Pic) { - static RetpolinePic T; - return &T; + if (config->zRetpolineplt) { + if (config->isPic) { + static RetpolinePic t; + return &t; } - static RetpolineNoPic T; - return &T; + static RetpolineNoPic t; + return &t; } - static X86 T; - return &T; + static X86 t; + return &t; } Index: vendor/lld/dist/ELF/Arch/X86_64.cpp =================================================================== --- vendor/lld/dist/ELF/Arch/X86_64.cpp (revision 351287) +++ vendor/lld/dist/ELF/Arch/X86_64.cpp (revision 351288) @@ -1,656 +1,701 @@ //===- X86_64.cpp ---------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #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" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { -template class X86_64 : public TargetInfo { +class X86_64 : public TargetInfo { public: X86_64(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - RelType getDynRel(RelType Type) const override; - void writeGotPltHeader(uint8_t *Buf) const override; - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + int getTlsGdRelaxSkip(RelType type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writeGotPltHeader(uint8_t *buf) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; - 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, 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; - bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End, - uint8_t StOther) const override; - -private: - void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, - uint8_t ModRm) const; + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxGot(uint8_t *loc, RelType 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; + bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const override; }; } // namespace -template X86_64::X86_64() { - CopyRel = R_X86_64_COPY; - GotRel = R_X86_64_GLOB_DAT; - NoneRel = R_X86_64_NONE; - PltRel = R_X86_64_JUMP_SLOT; - RelativeRel = R_X86_64_RELATIVE; - IRelativeRel = R_X86_64_IRELATIVE; - TlsGotRel = R_X86_64_TPOFF64; - TlsModuleIndexRel = R_X86_64_DTPMOD64; - TlsOffsetRel = R_X86_64_DTPOFF64; - GotEntrySize = 8; - GotPltEntrySize = 8; - PltEntrySize = 16; - PltHeaderSize = 16; - TlsGdRelaxSkip = 2; - TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 +X86_64::X86_64() { + copyRel = R_X86_64_COPY; + gotRel = R_X86_64_GLOB_DAT; + noneRel = R_X86_64_NONE; + pltRel = R_X86_64_JUMP_SLOT; + relativeRel = R_X86_64_RELATIVE; + iRelativeRel = R_X86_64_IRELATIVE; + symbolicRel = R_X86_64_64; + tlsDescRel = R_X86_64_TLSDESC; + tlsGotRel = R_X86_64_TPOFF64; + tlsModuleIndexRel = R_X86_64_DTPMOD64; + tlsOffsetRel = R_X86_64_DTPOFF64; + pltEntrySize = 16; + pltHeaderSize = 16; + trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 // Align to the large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. - DefaultImageBase = 0x200000; + defaultImageBase = 0x200000; } -template -RelExpr X86_64::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +int X86_64::getTlsGdRelaxSkip(RelType type) const { return 2; } + +RelExpr X86_64::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + if (type == R_X86_64_GOTTPOFF) + config->hasStaticTlsModel = true; + + switch (type) { case R_X86_64_8: case R_X86_64_16: case R_X86_64_32: case R_X86_64_32S: case R_X86_64_64: + return R_ABS; case R_X86_64_DTPOFF32: case R_X86_64_DTPOFF64: - return R_ABS; + return R_DTPREL; case R_X86_64_TPOFF32: return R_TLS; + case R_X86_64_TLSDESC_CALL: + return R_TLSDESC_CALL; case R_X86_64_TLSLD: return R_TLSLD_PC; case R_X86_64_TLSGD: return R_TLSGD_PC; case R_X86_64_SIZE32: case R_X86_64_SIZE64: return R_SIZE; case R_X86_64_PLT32: return R_PLT_PC; + case R_X86_64_PC8: + case R_X86_64_PC16: case R_X86_64_PC32: case R_X86_64_PC64: return R_PC; case R_X86_64_GOT32: case R_X86_64_GOT64: - return R_GOT_FROM_END; + return R_GOTPLT; + case R_X86_64_GOTPC32_TLSDESC: + return R_TLSDESC_PC; case R_X86_64_GOTPCREL: case R_X86_64_GOTPCRELX: case R_X86_64_REX_GOTPCRELX: case R_X86_64_GOTTPOFF: return R_GOT_PC; case R_X86_64_GOTOFF64: - return R_GOTREL_FROM_END; + return R_GOTPLTREL; case R_X86_64_GOTPC32: case R_X86_64_GOTPC64: - return R_GOTONLY_PC_FROM_END; + return R_GOTPLTONLY_PC; case R_X86_64_NONE: return R_NONE; default: - return R_INVALID; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } -template void X86_64::writeGotPltHeader(uint8_t *Buf) const { +void X86_64::writeGotPltHeader(uint8_t *buf) const { // The first entry holds the value of _DYNAMIC. It is not clear why that is // required, but it is documented in the psabi and the glibc dynamic linker // seems to use it (note that this is relevant for linking ld.so, not any // other program). - write64le(Buf, In.Dynamic->getVA()); + write64le(buf, mainPart->dynamic->getVA()); } -template -void X86_64::writeGotPlt(uint8_t *Buf, const Symbol &S) const { +void X86_64::writeGotPlt(uint8_t *buf, const Symbol &s) const { // See comments in X86::writeGotPlt. - write64le(Buf, S.getPltVA() + 6); + write64le(buf, s.getPltVA() + 6); } -template void X86_64::writePltHeader(uint8_t *Buf) const { - const uint8_t PltData[] = { +void X86_64::writePltHeader(uint8_t *buf) const { + const uint8_t pltData[] = { 0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip) 0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip) 0x0f, 0x1f, 0x40, 0x00, // nop }; - memcpy(Buf, PltData, sizeof(PltData)); - uint64_t GotPlt = In.GotPlt->getVA(); - uint64_t Plt = In.Plt->getVA(); - write32le(Buf + 2, GotPlt - Plt + 2); // GOTPLT+8 - write32le(Buf + 8, GotPlt - Plt + 4); // GOTPLT+16 + memcpy(buf, pltData, sizeof(pltData)); + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + write32le(buf + 2, gotPlt - plt + 2); // GOTPLT+8 + write32le(buf + 8, gotPlt - plt + 4); // GOTPLT+16 } -template -void X86_64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Inst[] = { +void X86_64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t inst[] = { 0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip) 0x68, 0, 0, 0, 0, // pushq 0xe9, 0, 0, 0, 0, // jmpq plt[0] }; - memcpy(Buf, Inst, sizeof(Inst)); + memcpy(buf, inst, sizeof(inst)); - write32le(Buf + 2, GotPltEntryAddr - PltEntryAddr - 6); - write32le(Buf + 7, Index); - write32le(Buf + 12, -getPltEntryOffset(Index) - 16); + write32le(buf + 2, gotPltEntryAddr - pltEntryAddr - 6); + write32le(buf + 7, index); + write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16); } -template RelType X86_64::getDynRel(RelType Type) const { - if (Type == R_X86_64_64 || Type == R_X86_64_PC64 || Type == R_X86_64_SIZE32 || - Type == R_X86_64_SIZE64) - return Type; +RelType X86_64::getDynRel(RelType type) const { + if (type == R_X86_64_64 || type == R_X86_64_PC64 || type == R_X86_64_SIZE32 || + type == R_X86_64_SIZE64) + return type; return R_X86_64_NONE; } -template -void X86_64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, - uint64_t Val) const { - // Convert - // .byte 0x66 - // leaq x@tlsgd(%rip), %rdi - // .word 0x6666 - // rex64 - // call __tls_get_addr@plt - // to - // mov %fs:0x0,%rax - // lea x@tpoff,%rax - const uint8_t Inst[] = { - 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax - 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax - }; - memcpy(Loc - 4, Inst, sizeof(Inst)); +void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_X86_64_TLSGD) { + // Convert + // .byte 0x66 + // leaq x@tlsgd(%rip), %rdi + // .word 0x6666 + // rex64 + // call __tls_get_addr@plt + // to the following two instructions. + const uint8_t inst[] = { + 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, + 0x00, 0x00, // mov %fs:0x0,%rax + 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax + }; + memcpy(loc - 4, inst, sizeof(inst)); - // The original code used a pc relative relocation and so we have to - // compensate for the -4 in had in the addend. - write32le(Loc + 8, Val + 4); + // The original code used a pc relative relocation and so we have to + // compensate for the -4 in had in the addend. + write32le(loc + 8, val + 4); + } else { + // Convert + // lea x@tlsgd(%rip), %rax + // call *(%rax) + // to the following two instructions. + assert(type == R_X86_64_GOTPC32_TLSDESC); + if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { + error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " + "in callq *x@tlsdesc(%rip), %rax"); + return; + } + // movq $x@tpoff(%rip),%rax + loc[-2] = 0xc7; + loc[-1] = 0xc0; + write32le(loc, val + 4); + // xchg ax,ax + loc[4] = 0x66; + loc[5] = 0x90; + } } -template -void X86_64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, - uint64_t Val) const { - // Convert - // .byte 0x66 - // leaq x@tlsgd(%rip), %rdi - // .word 0x6666 - // rex64 - // call __tls_get_addr@plt - // to - // mov %fs:0x0,%rax - // addq x@tpoff,%rax - const uint8_t Inst[] = { - 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax - 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@tpoff,%rax - }; - memcpy(Loc - 4, Inst, sizeof(Inst)); +void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_X86_64_TLSGD) { + // Convert + // .byte 0x66 + // leaq x@tlsgd(%rip), %rdi + // .word 0x6666 + // rex64 + // call __tls_get_addr@plt + // to the following two instructions. + const uint8_t inst[] = { + 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, + 0x00, 0x00, // mov %fs:0x0,%rax + 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@gottpoff(%rip),%rax + }; + memcpy(loc - 4, inst, sizeof(inst)); - // Both code sequences are PC relatives, but since we are moving the constant - // forward by 8 bytes we have to subtract the value by 8. - write32le(Loc + 8, Val - 8); + // Both code sequences are PC relatives, but since we are moving the + // constant forward by 8 bytes we have to subtract the value by 8. + write32le(loc + 8, val - 8); + } else { + // Convert + // lea x@tlsgd(%rip), %rax + // call *(%rax) + // to the following two instructions. + assert(type == R_X86_64_GOTPC32_TLSDESC); + if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { + error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " + "in callq *x@tlsdesc(%rip), %rax"); + return; + } + // movq x@gottpoff(%rip),%rax + loc[-2] = 0x8b; + write32le(loc, val); + // xchg ax,ax + loc[4] = 0x66; + loc[5] = 0x90; + } } // In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to // R_X86_64_TPOFF32 so that it does not use GOT. -template -void X86_64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, - uint64_t Val) const { - uint8_t *Inst = Loc - 3; - uint8_t Reg = Loc[-1] >> 3; - uint8_t *RegSlot = Loc - 1; +void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { + uint8_t *inst = loc - 3; + uint8_t reg = loc[-1] >> 3; + uint8_t *regSlot = loc - 1; // Note that ADD with RSP or R12 is converted to ADD instead of LEA // because LEA with these registers needs 4 bytes to encode and thus // wouldn't fit the space. - if (memcmp(Inst, "\x48\x03\x25", 3) == 0) { + if (memcmp(inst, "\x48\x03\x25", 3) == 0) { // "addq foo@gottpoff(%rip),%rsp" -> "addq $foo,%rsp" - memcpy(Inst, "\x48\x81\xc4", 3); - } else if (memcmp(Inst, "\x4c\x03\x25", 3) == 0) { + memcpy(inst, "\x48\x81\xc4", 3); + } else if (memcmp(inst, "\x4c\x03\x25", 3) == 0) { // "addq foo@gottpoff(%rip),%r12" -> "addq $foo,%r12" - memcpy(Inst, "\x49\x81\xc4", 3); - } else if (memcmp(Inst, "\x4c\x03", 2) == 0) { + memcpy(inst, "\x49\x81\xc4", 3); + } else if (memcmp(inst, "\x4c\x03", 2) == 0) { // "addq foo@gottpoff(%rip),%r[8-15]" -> "leaq foo(%r[8-15]),%r[8-15]" - memcpy(Inst, "\x4d\x8d", 2); - *RegSlot = 0x80 | (Reg << 3) | Reg; - } else if (memcmp(Inst, "\x48\x03", 2) == 0) { + memcpy(inst, "\x4d\x8d", 2); + *regSlot = 0x80 | (reg << 3) | reg; + } else if (memcmp(inst, "\x48\x03", 2) == 0) { // "addq foo@gottpoff(%rip),%reg -> "leaq foo(%reg),%reg" - memcpy(Inst, "\x48\x8d", 2); - *RegSlot = 0x80 | (Reg << 3) | Reg; - } else if (memcmp(Inst, "\x4c\x8b", 2) == 0) { + memcpy(inst, "\x48\x8d", 2); + *regSlot = 0x80 | (reg << 3) | reg; + } else if (memcmp(inst, "\x4c\x8b", 2) == 0) { // "movq foo@gottpoff(%rip),%r[8-15]" -> "movq $foo,%r[8-15]" - memcpy(Inst, "\x49\xc7", 2); - *RegSlot = 0xc0 | Reg; - } else if (memcmp(Inst, "\x48\x8b", 2) == 0) { + memcpy(inst, "\x49\xc7", 2); + *regSlot = 0xc0 | reg; + } else if (memcmp(inst, "\x48\x8b", 2) == 0) { // "movq foo@gottpoff(%rip),%reg" -> "movq $foo,%reg" - memcpy(Inst, "\x48\xc7", 2); - *RegSlot = 0xc0 | Reg; + memcpy(inst, "\x48\xc7", 2); + *regSlot = 0xc0 | reg; } else { - error(getErrorLocation(Loc - 3) + + error(getErrorLocation(loc - 3) + "R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only"); } // The original code used a PC relative relocation. // Need to compensate for the -4 it had in the addend. - write32le(Loc, Val + 4); + write32le(loc, val + 4); } -template -void X86_64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, - uint64_t Val) const { - // Convert - // leaq bar@tlsld(%rip), %rdi - // callq __tls_get_addr@PLT - // leaq bar@dtpoff(%rax), %rcx - // to - // .word 0x6666 - // .byte 0x66 - // mov %fs:0,%rax - // leaq bar@tpoff(%rax), %rcx - if (Type == R_X86_64_DTPOFF64) { - write64le(Loc, Val); +void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_X86_64_DTPOFF64) { + write64le(loc, val); return; } - if (Type == R_X86_64_DTPOFF32) { - write32le(Loc, Val); + if (type == R_X86_64_DTPOFF32) { + write32le(loc, val); return; } - const uint8_t Inst[] = { + const uint8_t inst[] = { 0x66, 0x66, // .word 0x6666 0x66, // .byte 0x66 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0,%rax }; - memcpy(Loc - 3, Inst, sizeof(Inst)); + + if (loc[4] == 0xe8) { + // Convert + // leaq bar@tlsld(%rip), %rdi # 48 8d 3d + // callq __tls_get_addr@PLT # e8 + // leaq bar@dtpoff(%rax), %rcx + // to + // .word 0x6666 + // .byte 0x66 + // mov %fs:0,%rax + // leaq bar@tpoff(%rax), %rcx + memcpy(loc - 3, inst, sizeof(inst)); + return; + } + + if (loc[4] == 0xff && loc[5] == 0x15) { + // Convert + // leaq x@tlsld(%rip),%rdi # 48 8d 3d + // call *__tls_get_addr@GOTPCREL(%rip) # ff 15 + // to + // .long 0x66666666 + // movq %fs:0,%rax + // See "Table 11.9: LD -> LE Code Transition (LP64)" in + // https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-1.0.pdf + loc[-3] = 0x66; + memcpy(loc - 2, inst, sizeof(inst)); + return; + } + + error(getErrorLocation(loc - 3) + + "expected R_X86_64_PLT32 or R_X86_64_GOTPCRELX after R_X86_64_TLSLD"); } -template -void X86_64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_X86_64_8: - checkUInt(Loc, Val, 8, Type); - *Loc = Val; + checkIntUInt(loc, val, 8, type); + *loc = val; break; + case R_X86_64_PC8: + checkInt(loc, val, 8, type); + *loc = val; + break; case R_X86_64_16: - checkUInt(Loc, Val, 16, Type); - write16le(Loc, Val); + checkIntUInt(loc, val, 16, type); + write16le(loc, val); break; + case R_X86_64_PC16: + checkInt(loc, val, 16, type); + write16le(loc, val); + break; case R_X86_64_32: - checkUInt(Loc, Val, 32, Type); - write32le(Loc, Val); + checkUInt(loc, val, 32, type); + write32le(loc, val); break; case R_X86_64_32S: case R_X86_64_TPOFF32: case R_X86_64_GOT32: case R_X86_64_GOTPC32: + case R_X86_64_GOTPC32_TLSDESC: case R_X86_64_GOTPCREL: case R_X86_64_GOTPCRELX: case R_X86_64_REX_GOTPCRELX: case R_X86_64_PC32: case R_X86_64_GOTTPOFF: case R_X86_64_PLT32: case R_X86_64_TLSGD: case R_X86_64_TLSLD: case R_X86_64_DTPOFF32: case R_X86_64_SIZE32: - checkInt(Loc, Val, 32, Type); - write32le(Loc, Val); + checkInt(loc, val, 32, type); + write32le(loc, val); break; case R_X86_64_64: case R_X86_64_DTPOFF64: - case R_X86_64_GLOB_DAT: case R_X86_64_PC64: case R_X86_64_SIZE64: case R_X86_64_GOT64: case R_X86_64_GOTOFF64: case R_X86_64_GOTPC64: - write64le(Loc, Val); + write64le(loc, val); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -template -RelExpr X86_64::adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr RelExpr) const { - if (Type != R_X86_64_GOTPCRELX && Type != R_X86_64_REX_GOTPCRELX) - return RelExpr; - const uint8_t Op = Data[-2]; - const uint8_t ModRm = Data[-1]; +RelExpr X86_64::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr relExpr) const { + if (type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX) + return relExpr; + const uint8_t op = data[-2]; + const uint8_t modRm = data[-1]; // FIXME: When PIC is disabled and foo is defined locally in the // lower 32 bit address space, memory operand in mov can be converted into // immediate operand. Otherwise, mov must be changed to lea. We support only // latter relaxation at this moment. - if (Op == 0x8b) + if (op == 0x8b) return R_RELAX_GOT_PC; // Relax call and jmp. - if (Op == 0xff && (ModRm == 0x15 || ModRm == 0x25)) + if (op == 0xff && (modRm == 0x15 || modRm == 0x25)) return R_RELAX_GOT_PC; // Relaxation of test, adc, add, and, cmp, or, sbb, sub, xor. // If PIC then no relaxation is available. // We also don't relax test/binop instructions without REX byte, // they are 32bit operations and not common to have. - assert(Type == R_X86_64_REX_GOTPCRELX); - return Config->Pic ? RelExpr : R_RELAX_GOT_PC_NOPIC; + assert(type == R_X86_64_REX_GOTPCRELX); + return config->isPic ? relExpr : R_RELAX_GOT_PC_NOPIC; } // A subset of relaxations can only be applied for no-PIC. This method // handles such relaxations. Instructions encoding information was taken from: // "Intel 64 and IA-32 Architectures Software Developer's Manual V2" // (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/ // 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf) -template -void X86_64::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, - uint8_t ModRm) const { - const uint8_t Rex = Loc[-3]; +static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op, + uint8_t modRm) { + const uint8_t rex = loc[-3]; // Convert "test %reg, foo@GOTPCREL(%rip)" to "test $foo, %reg". - if (Op == 0x85) { + if (op == 0x85) { // See "TEST-Logical Compare" (4-428 Vol. 2B), // TEST r/m64, r64 uses "full" ModR / M byte (no opcode extension). // ModR/M byte has form XX YYY ZZZ, where // YYY is MODRM.reg(register 2), ZZZ is MODRM.rm(register 1). // XX has different meanings: // 00: The operand's memory address is in reg1. // 01: The operand's memory address is reg1 + a byte-sized displacement. // 10: The operand's memory address is reg1 + a word-sized displacement. // 11: The operand is reg1 itself. // If an instruction requires only one operand, the unused reg2 field // holds extra opcode bits rather than a register code // 0xC0 == 11 000 000 binary. // 0x38 == 00 111 000 binary. // We transfer reg2 to reg1 here as operand. // See "2.1.3 ModR/M and SIB Bytes" (Vol. 2A 2-3). - Loc[-1] = 0xc0 | (ModRm & 0x38) >> 3; // ModR/M byte. + loc[-1] = 0xc0 | (modRm & 0x38) >> 3; // ModR/M byte. // Change opcode from TEST r/m64, r64 to TEST r/m64, imm32 // See "TEST-Logical Compare" (4-428 Vol. 2B). - Loc[-2] = 0xf7; + loc[-2] = 0xf7; // Move R bit to the B bit in REX byte. // REX byte is encoded as 0100WRXB, where // 0100 is 4bit fixed pattern. // REX.W When 1, a 64-bit operand size is used. Otherwise, when 0, the // default operand size is used (which is 32-bit for most but not all // instructions). // REX.R This 1-bit value is an extension to the MODRM.reg field. // REX.X This 1-bit value is an extension to the SIB.index field. // REX.B This 1-bit value is an extension to the MODRM.rm field or the // SIB.base field. // See "2.2.1.2 More on REX Prefix Fields " (2-8 Vol. 2A). - Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2; - write32le(Loc, Val); + loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2; + write32le(loc, val); return; } // If we are here then we need to relax the adc, add, and, cmp, or, sbb, sub // or xor operations. // Convert "binop foo@GOTPCREL(%rip), %reg" to "binop $foo, %reg". // Logic is close to one for test instruction above, but we also // write opcode extension here, see below for details. - Loc[-1] = 0xc0 | (ModRm & 0x38) >> 3 | (Op & 0x3c); // ModR/M byte. + loc[-1] = 0xc0 | (modRm & 0x38) >> 3 | (op & 0x3c); // ModR/M byte. // Primary opcode is 0x81, opcode extension is one of: // 000b = ADD, 001b is OR, 010b is ADC, 011b is SBB, // 100b is AND, 101b is SUB, 110b is XOR, 111b is CMP. // This value was wrote to MODRM.reg in a line above. // See "3.2 INSTRUCTIONS (A-M)" (Vol. 2A 3-15), // "INSTRUCTION SET REFERENCE, N-Z" (Vol. 2B 4-1) for // descriptions about each operation. - Loc[-2] = 0x81; - Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2; - write32le(Loc, Val); + loc[-2] = 0x81; + loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2; + write32le(loc, val); } -template -void X86_64::relaxGot(uint8_t *Loc, uint64_t Val) const { - const uint8_t Op = Loc[-2]; - const uint8_t ModRm = Loc[-1]; +void X86_64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { + const uint8_t op = loc[-2]; + const uint8_t modRm = loc[-1]; // Convert "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg". - if (Op == 0x8b) { - Loc[-2] = 0x8d; - write32le(Loc, Val); + if (op == 0x8b) { + loc[-2] = 0x8d; + write32le(loc, val); return; } - if (Op != 0xff) { + if (op != 0xff) { // We are relaxing a rip relative to an absolute, so compensate // for the old -4 addend. - assert(!Config->Pic); - relaxGotNoPic(Loc, Val + 4, Op, ModRm); + assert(!config->isPic); + relaxGotNoPic(loc, val + 4, op, modRm); return; } // Convert call/jmp instructions. - if (ModRm == 0x15) { + if (modRm == 0x15) { // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call foo". // Instead we convert to "addr32 call foo" where addr32 is an instruction // prefix. That makes result expression to be a single instruction. - Loc[-2] = 0x67; // addr32 prefix - Loc[-1] = 0xe8; // call - write32le(Loc, Val); + loc[-2] = 0x67; // addr32 prefix + loc[-1] = 0xe8; // call + write32le(loc, val); return; } // Convert "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop". // jmp doesn't return, so it is fine to use nop here, it is just a stub. - assert(ModRm == 0x25); - Loc[-2] = 0xe9; // jmp - Loc[3] = 0x90; // nop - write32le(Loc - 1, Val + 1); + assert(modRm == 0x25); + loc[-2] = 0xe9; // jmp + loc[3] = 0x90; // nop + write32le(loc - 1, val + 1); } -// This anonymous namespace works around a warning bug in -// old versions of gcc. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 -namespace { - // A split-stack prologue starts by checking the amount of stack remaining // in one of two ways: // A) Comparing of the stack pointer to a field in the tcb. // B) Or a load of a stack pointer offset with an lea to r10 or r11. -template <> -bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *Loc, - uint8_t *End, - uint8_t StOther) const { - if (Loc + 8 >= End) +bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const { + if (!config->is64) { + error("Target doesn't support split stacks."); return false; + } + if (loc + 8 >= end) + return false; + // Replace "cmp %fs:0x70,%rsp" and subsequent branch // with "stc, nopl 0x0(%rax,%rax,1)" - if (memcmp(Loc, "\x64\x48\x3b\x24\x25", 5) == 0) { - memcpy(Loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8); + if (memcmp(loc, "\x64\x48\x3b\x24\x25", 5) == 0) { + memcpy(loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8); return true; } // Adjust "lea X(%rsp),%rYY" to lea "(X - 0x4000)(%rsp),%rYY" where rYY could // be r10 or r11. The lea instruction feeds a subsequent compare which checks // if there is X available stack space. Making X larger effectively reserves // that much additional space. The stack grows downward so subtract the value. - if (memcmp(Loc, "\x4c\x8d\x94\x24", 4) == 0 || - memcmp(Loc, "\x4c\x8d\x9c\x24", 4) == 0) { + if (memcmp(loc, "\x4c\x8d\x94\x24", 4) == 0 || + memcmp(loc, "\x4c\x8d\x9c\x24", 4) == 0) { // The offset bytes are encoded four bytes after the start of the // instruction. - write32le(Loc + 4, read32le(Loc + 4) - 0x4000); + write32le(loc + 4, read32le(loc + 4) - 0x4000); return true; } return false; } -template <> -bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *Loc, - uint8_t *End, - uint8_t StOther) const { - llvm_unreachable("Target doesn't support split stacks."); -} - -} // namespace - // These nonstandard PLT entries are to migtigate Spectre v2 security // vulnerability. In order to mitigate Spectre v2, we want to avoid indirect // branch instructions such as `jmp *GOTPLT(%rip)`. So, in the following PLT // entries, we use a CALL followed by MOV and RET to do the same thing as an // indirect jump. That instruction sequence is so-called "retpoline". // // We have two types of retpoline PLTs as a size optimization. If `-z now` // is specified, all dynamic symbols are resolved at load-time. Thus, when // that option is given, we can omit code for symbol lazy resolution. namespace { -template class Retpoline : public X86_64 { +class Retpoline : public X86_64 { public: Retpoline(); - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; -template class RetpolineZNow : public X86_64 { +class RetpolineZNow : public X86_64 { public: RetpolineZNow(); - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override {} - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override {} + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; } // namespace -template Retpoline::Retpoline() { - TargetInfo::PltHeaderSize = 48; - TargetInfo::PltEntrySize = 32; +Retpoline::Retpoline() { + pltHeaderSize = 48; + pltEntrySize = 32; } -template -void Retpoline::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - write64le(Buf, S.getPltVA() + 17); +void Retpoline::writeGotPlt(uint8_t *buf, const Symbol &s) const { + write64le(buf, s.getPltVA() + 17); } -template void Retpoline::writePltHeader(uint8_t *Buf) const { - const uint8_t Insn[] = { +void Retpoline::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { 0xff, 0x35, 0, 0, 0, 0, // 0: pushq GOTPLT+8(%rip) 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 6: mov GOTPLT+16(%rip), %r11 0xe8, 0x0e, 0x00, 0x00, 0x00, // d: callq next 0xf3, 0x90, // 12: loop: pause 0x0f, 0xae, 0xe8, // 14: lfence 0xeb, 0xf9, // 17: jmp loop 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16 0x4c, 0x89, 0x1c, 0x24, // 20: next: mov %r11, (%rsp) 0xc3, // 24: ret 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 25: int3; padding 0xcc, 0xcc, 0xcc, 0xcc, // 2c: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); - uint64_t GotPlt = In.GotPlt->getVA(); - uint64_t Plt = In.Plt->getVA(); - write32le(Buf + 2, GotPlt - Plt - 6 + 8); - write32le(Buf + 9, GotPlt - Plt - 13 + 16); + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + write32le(buf + 2, gotPlt - plt - 6 + 8); + write32le(buf + 9, gotPlt - plt - 13 + 16); } -template -void Retpoline::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Insn[] = { +void Retpoline::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 0: mov foo@GOTPLT(%rip), %r11 0xe8, 0, 0, 0, 0, // 7: callq plt+0x20 0xe9, 0, 0, 0, 0, // c: jmp plt+0x12 0x68, 0, 0, 0, 0, // 11: pushq 0xe9, 0, 0, 0, 0, // 16: jmp plt+0 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1b: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); - uint64_t Off = getPltEntryOffset(Index); + uint64_t off = pltHeaderSize + pltEntrySize * index; - write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7); - write32le(Buf + 8, -Off - 12 + 32); - write32le(Buf + 13, -Off - 17 + 18); - write32le(Buf + 18, Index); - write32le(Buf + 23, -Off - 27); + write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7); + write32le(buf + 8, -off - 12 + 32); + write32le(buf + 13, -off - 17 + 18); + write32le(buf + 18, index); + write32le(buf + 23, -off - 27); } -template RetpolineZNow::RetpolineZNow() { - TargetInfo::PltHeaderSize = 32; - TargetInfo::PltEntrySize = 16; +RetpolineZNow::RetpolineZNow() { + pltHeaderSize = 32; + pltEntrySize = 16; } -template -void RetpolineZNow::writePltHeader(uint8_t *Buf) const { - const uint8_t Insn[] = { +void RetpolineZNow::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { 0xe8, 0x0b, 0x00, 0x00, 0x00, // 0: call next 0xf3, 0x90, // 5: loop: pause 0x0f, 0xae, 0xe8, // 7: lfence 0xeb, 0xf9, // a: jmp loop 0xcc, 0xcc, 0xcc, 0xcc, // c: int3; .align 16 0x4c, 0x89, 0x1c, 0x24, // 10: next: mov %r11, (%rsp) 0xc3, // 14: ret 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 15: int3; padding 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding 0xcc, // 1f: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); } -template -void RetpolineZNow::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Insn[] = { +void RetpolineZNow::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // mov foo@GOTPLT(%rip), %r11 0xe9, 0, 0, 0, 0, // jmp plt+0 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); - write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7); - write32le(Buf + 8, -getPltEntryOffset(Index) - 12); + write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7); + write32le(buf + 8, -pltHeaderSize - pltEntrySize * index - 12); } -template static TargetInfo *getTargetInfo() { - if (Config->ZRetpolineplt) { - if (Config->ZNow) { - static RetpolineZNow T; - return &T; +static TargetInfo *getTargetInfo() { + if (config->zRetpolineplt) { + if (config->zNow) { + static RetpolineZNow t; + return &t; } - static Retpoline T; - return &T; + static Retpoline t; + return &t; } - static X86_64 T; - return &T; + static X86_64 t; + return &t; } -TargetInfo *elf::getX32TargetInfo() { return getTargetInfo(); } -TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); } +TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); } Index: vendor/lld/dist/ELF/CMakeLists.txt =================================================================== --- vendor/lld/dist/ELF/CMakeLists.txt (revision 351287) +++ vendor/lld/dist/ELF/CMakeLists.txt (revision 351288) @@ -1,68 +1,67 @@ set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(ELFOptionsTableGen) if(NOT LLD_BUILT_STANDALONE) set(tablegen_deps intrinsics_gen) endif() add_lld_library(lldELF AArch64ErrataFix.cpp Arch/AArch64.cpp Arch/AMDGPU.cpp Arch/ARM.cpp Arch/AVR.cpp Arch/Hexagon.cpp Arch/Mips.cpp Arch/MipsArchTree.cpp Arch/MSP430.cpp Arch/PPC.cpp Arch/PPC64.cpp Arch/RISCV.cpp Arch/SPARCV9.cpp Arch/X86.cpp Arch/X86_64.cpp CallGraphSort.cpp DWARF.cpp Driver.cpp DriverUtils.cpp EhFrame.cpp - Filesystem.cpp ICF.cpp InputFiles.cpp InputSection.cpp LTO.cpp LinkerScript.cpp MapFile.cpp MarkLive.cpp OutputSections.cpp Relocations.cpp ScriptLexer.cpp ScriptParser.cpp SymbolTable.cpp Symbols.cpp SyntheticSections.cpp Target.cpp Thunks.cpp Writer.cpp LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} BinaryFormat BitWriter Core DebugInfoDWARF LTO MC Object Option Support LINK_LIBS lldCommon ${LLVM_PTHREAD_LIB} DEPENDS ELFOptionsTableGen ${tablegen_deps} ) Index: vendor/lld/dist/ELF/CallGraphSort.cpp =================================================================== --- vendor/lld/dist/ELF/CallGraphSort.cpp (revision 351287) +++ vendor/lld/dist/ELF/CallGraphSort.cpp (revision 351288) @@ -1,240 +1,259 @@ //===- CallGraphSort.cpp --------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// /// /// Implementation of Call-Chain Clustering from: Optimizing Function Placement /// for Large-Scale Data-Center Applications /// https://research.fb.com/wp-content/uploads/2017/01/cgo2017-hfsort-final1.pdf /// /// The goal of this algorithm is to improve runtime performance of the final /// executable by arranging code sections such that page table and i-cache /// misses are minimized. /// /// Definitions: /// * Cluster /// * An ordered list of input sections which are layed out as a unit. At the /// beginning of the algorithm each input section has its own cluster and /// the weight of the cluster is the sum of the weight of all incomming /// edges. /// * Call-Chain Clustering (C³) Heuristic /// * Defines when and how clusters are combined. Pick the highest weighted /// input section then add it to its most likely predecessor if it wouldn't /// penalize it too much. /// * Density /// * The weight of the cluster divided by the size of the cluster. This is a /// proxy for the ammount of execution time spent per byte of the cluster. /// /// It does so given a call graph profile by the following: /// * Build a weighted call graph from the call graph profile /// * Sort input sections by weight /// * For each input section starting with the highest weight /// * Find its most likely predecessor cluster /// * Check if the combined cluster would be too large, or would have too low /// a density. /// * If not, then combine the clusters. /// * Sort non-empty clusters by density /// //===----------------------------------------------------------------------===// #include "CallGraphSort.h" #include "OutputSections.h" #include "SymbolTable.h" #include "Symbols.h" using namespace llvm; using namespace lld; using namespace lld::elf; namespace { struct Edge { - int From; - uint64_t Weight; + int from; + uint64_t weight; }; struct Cluster { - Cluster(int Sec, size_t S) : Sections{Sec}, Size(S) {} + Cluster(int sec, size_t s) : sections{sec}, size(s) {} double getDensity() const { - if (Size == 0) + if (size == 0) return 0; - return double(Weight) / double(Size); + return double(weight) / double(size); } - std::vector Sections; - size_t Size = 0; - uint64_t Weight = 0; - uint64_t InitialWeight = 0; - Edge BestPred = {-1, 0}; + std::vector sections; + size_t size = 0; + uint64_t weight = 0; + uint64_t initialWeight = 0; + Edge bestPred = {-1, 0}; }; class CallGraphSort { public: CallGraphSort(); DenseMap run(); private: - std::vector Clusters; - std::vector Sections; + std::vector clusters; + std::vector sections; void groupClusters(); }; // Maximum ammount the combined cluster density can be worse than the original // cluster to consider merging. constexpr int MAX_DENSITY_DEGRADATION = 8; // Maximum cluster size in bytes. constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024; } // end anonymous namespace -typedef std::pair - SectionPair; +using SectionPair = + std::pair; // Take the edge list in Config->CallGraphProfile, resolve symbol names to // Symbols, and generate a graph between InputSections with the provided // weights. CallGraphSort::CallGraphSort() { - MapVector &Profile = Config->CallGraphProfile; - DenseMap SecToCluster; + MapVector &profile = config->callGraphProfile; + DenseMap secToCluster; - auto GetOrCreateNode = [&](const InputSectionBase *IS) -> int { - auto Res = SecToCluster.insert(std::make_pair(IS, Clusters.size())); - if (Res.second) { - Sections.push_back(IS); - Clusters.emplace_back(Clusters.size(), IS->getSize()); + auto getOrCreateNode = [&](const InputSectionBase *isec) -> int { + auto res = secToCluster.insert(std::make_pair(isec, clusters.size())); + if (res.second) { + sections.push_back(isec); + clusters.emplace_back(clusters.size(), isec->getSize()); } - return Res.first->second; + return res.first->second; }; // Create the graph. - for (std::pair &C : Profile) { - const auto *FromSB = cast(C.first.first->Repl); - const auto *ToSB = cast(C.first.second->Repl); - uint64_t Weight = C.second; + for (std::pair &c : profile) { + const auto *fromSB = cast(c.first.first->repl); + const auto *toSB = cast(c.first.second->repl); + uint64_t weight = c.second; // Ignore edges between input sections belonging to different output // sections. This is done because otherwise we would end up with clusters // containing input sections that can't actually be placed adjacently in the // output. This messes with the cluster size and density calculations. We // would also end up moving input sections in other output sections without // moving them closer to what calls them. - if (FromSB->getOutputSection() != ToSB->getOutputSection()) + if (fromSB->getOutputSection() != toSB->getOutputSection()) continue; - int From = GetOrCreateNode(FromSB); - int To = GetOrCreateNode(ToSB); + int from = getOrCreateNode(fromSB); + int to = getOrCreateNode(toSB); - Clusters[To].Weight += Weight; + clusters[to].weight += weight; - if (From == To) + if (from == to) continue; // Remember the best edge. - Cluster &ToC = Clusters[To]; - if (ToC.BestPred.From == -1 || ToC.BestPred.Weight < Weight) { - ToC.BestPred.From = From; - ToC.BestPred.Weight = Weight; + Cluster &toC = clusters[to]; + if (toC.bestPred.from == -1 || toC.bestPred.weight < weight) { + toC.bestPred.from = from; + toC.bestPred.weight = weight; } } - for (Cluster &C : Clusters) - C.InitialWeight = C.Weight; + for (Cluster &c : clusters) + c.initialWeight = c.weight; } // It's bad to merge clusters which would degrade the density too much. -static bool isNewDensityBad(Cluster &A, Cluster &B) { - double NewDensity = double(A.Weight + B.Weight) / double(A.Size + B.Size); - return NewDensity < A.getDensity() / MAX_DENSITY_DEGRADATION; +static bool isNewDensityBad(Cluster &a, Cluster &b) { + double newDensity = double(a.weight + b.weight) / double(a.size + b.size); + return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION; } -static void mergeClusters(Cluster &Into, Cluster &From) { - Into.Sections.insert(Into.Sections.end(), From.Sections.begin(), - From.Sections.end()); - Into.Size += From.Size; - Into.Weight += From.Weight; - From.Sections.clear(); - From.Size = 0; - From.Weight = 0; +static void mergeClusters(Cluster &into, Cluster &from) { + into.sections.insert(into.sections.end(), from.sections.begin(), + from.sections.end()); + into.size += from.size; + into.weight += from.weight; + from.sections.clear(); + from.size = 0; + from.weight = 0; } // Group InputSections into clusters using the Call-Chain Clustering heuristic // then sort the clusters by density. void CallGraphSort::groupClusters() { - std::vector SortedSecs(Clusters.size()); - std::vector SecToCluster(Clusters.size()); + std::vector sortedSecs(clusters.size()); + std::vector secToCluster(clusters.size()); - for (size_t I = 0; I < Clusters.size(); ++I) { - SortedSecs[I] = I; - SecToCluster[I] = &Clusters[I]; + for (size_t i = 0; i < clusters.size(); ++i) { + sortedSecs[i] = i; + secToCluster[i] = &clusters[i]; } - std::stable_sort(SortedSecs.begin(), SortedSecs.end(), [&](int A, int B) { - return Clusters[B].getDensity() < Clusters[A].getDensity(); + llvm::stable_sort(sortedSecs, [&](int a, int b) { + return clusters[a].getDensity() > clusters[b].getDensity(); }); - for (int SI : SortedSecs) { - // Clusters[SI] is the same as SecToClusters[SI] here because it has not + for (int si : sortedSecs) { + // clusters[si] is the same as secToClusters[si] here because it has not // been merged into another cluster yet. - Cluster &C = Clusters[SI]; + Cluster &c = clusters[si]; // Don't consider merging if the edge is unlikely. - if (C.BestPred.From == -1 || C.BestPred.Weight * 10 <= C.InitialWeight) + if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight) continue; - Cluster *PredC = SecToCluster[C.BestPred.From]; - if (PredC == &C) + Cluster *predC = secToCluster[c.bestPred.from]; + if (predC == &c) continue; - if (C.Size + PredC->Size > MAX_CLUSTER_SIZE) + if (c.size + predC->size > MAX_CLUSTER_SIZE) continue; - if (isNewDensityBad(*PredC, C)) + if (isNewDensityBad(*predC, c)) continue; // NOTE: Consider using a disjoint-set to track section -> cluster mapping // if this is ever slow. - for (int SI : C.Sections) - SecToCluster[SI] = PredC; + for (int si : c.sections) + secToCluster[si] = predC; - mergeClusters(*PredC, C); + mergeClusters(*predC, c); } // Remove empty or dead nodes. Invalidates all cluster indices. - llvm::erase_if(Clusters, [](const Cluster &C) { - return C.Size == 0 || C.Sections.empty(); + llvm::erase_if(clusters, [](const Cluster &c) { + return c.size == 0 || c.sections.empty(); }); // Sort by density. - std::stable_sort(Clusters.begin(), Clusters.end(), - [](const Cluster &A, const Cluster &B) { - return A.getDensity() > B.getDensity(); - }); + llvm::stable_sort(clusters, [](const Cluster &a, const Cluster &b) { + return a.getDensity() > b.getDensity(); + }); } DenseMap CallGraphSort::run() { groupClusters(); // Generate order. - DenseMap OrderMap; - ssize_t CurOrder = 1; + DenseMap orderMap; + ssize_t curOrder = 1; - for (const Cluster &C : Clusters) - for (int SecIndex : C.Sections) - OrderMap[Sections[SecIndex]] = CurOrder++; + for (const Cluster &c : clusters) + for (int secIndex : c.sections) + orderMap[sections[secIndex]] = curOrder++; - return OrderMap; + if (!config->printSymbolOrder.empty()) { + std::error_code ec; + raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::F_None); + if (ec) { + error("cannot open " + config->printSymbolOrder + ": " + ec.message()); + return orderMap; + } + + // Print the symbols ordered by C3, in the order of increasing curOrder + // Instead of sorting all the orderMap, just repeat the loops above. + for (const Cluster &c : clusters) + for (int secIndex : c.sections) + // Search all the symbols in the file of the section + // and find out a Defined symbol with name that is within the section. + for (Symbol *sym: sections[secIndex]->file->getSymbols()) + if (!sym->isSection()) // Filter out section-type symbols here. + if (auto *d = dyn_cast(sym)) + if (sections[secIndex] == d->section) + os << sym->getName() << "\n"; + } + + return orderMap; } // Sort sections by the profile data provided by -callgraph-profile-file // // This first builds a call graph based on the profile data then merges sections // according to the C³ huristic. All clusters are then sorted by a density // metric to further improve locality. DenseMap elf::computeCallGraphProfileOrder() { return CallGraphSort().run(); } Index: vendor/lld/dist/ELF/CallGraphSort.h =================================================================== --- vendor/lld/dist/ELF/CallGraphSort.h (revision 351287) +++ vendor/lld/dist/ELF/CallGraphSort.h (revision 351288) @@ -1,23 +1,22 @@ //===- CallGraphSort.h ------------------------------------------*- C++ -*-===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLD_ELF_CALL_GRAPH_SORT_H #define LLD_ELF_CALL_GRAPH_SORT_H #include "llvm/ADT/DenseMap.h" namespace lld { namespace elf { class InputSectionBase; llvm::DenseMap computeCallGraphProfileOrder(); } // namespace elf } // namespace lld #endif Index: vendor/lld/dist/ELF/Config.h =================================================================== --- vendor/lld/dist/ELF/Config.h (revision 351287) +++ vendor/lld/dist/ELF/Config.h (revision 351288) @@ -1,293 +1,321 @@ //===- Config.h -------------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// #ifndef LLD_ELF_CONFIG_H #define LLD_ELF_CONFIG_H #include "lld/Common/ErrorHandler.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/CachePruning.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Endian.h" +#include #include namespace lld { namespace elf { class InputFile; class InputSectionBase; enum ELFKind { ELFNoneKind, ELF32LEKind, ELF32BEKind, ELF64LEKind, ELF64BEKind }; // For --build-id. enum class BuildIdKind { None, Fast, Md5, Sha1, Hexstring, Uuid }; // For --discard-{all,locals,none}. enum class DiscardPolicy { Default, All, Locals, None }; // For --icf={none,safe,all}. enum class ICFLevel { None, Safe, All }; // For --strip-{all,debug}. enum class StripPolicy { None, All, Debug }; // For --unresolved-symbols. enum class UnresolvedPolicy { ReportError, Warn, Ignore }; // For --orphan-handling. enum class OrphanHandlingPolicy { Place, Warn, Error }; // For --sort-section and linkerscript sorting rules. enum class SortSectionPolicy { Default, None, Alignment, Name, Priority }; // For --target2 enum class Target2Policy { Abs, Rel, GotRel }; // For tracking ARM Float Argument PCS enum class ARMVFPArgKind { Default, Base, VFP, ToolChain }; struct SymbolVersion { - llvm::StringRef Name; - bool IsExternCpp; - bool HasWildcard; + llvm::StringRef name; + bool isExternCpp; + bool hasWildcard; }; // This struct contains symbols version definition that // can be found in version script if it is used for link. struct VersionDefinition { - llvm::StringRef Name; - uint16_t Id = 0; - std::vector Globals; - size_t NameOff = 0; // Offset in the string table + llvm::StringRef name; + uint16_t id = 0; + std::vector globals; }; // 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 { - uint8_t OSABI = 0; - llvm::CachePruningPolicy ThinLTOCachePolicy; - llvm::StringMap SectionStartMap; - llvm::StringRef Chroot; - llvm::StringRef DynamicLinker; - llvm::StringRef DwoDir; - llvm::StringRef Entry; - llvm::StringRef Emulation; - llvm::StringRef Fini; - llvm::StringRef Init; - llvm::StringRef LTOAAPipeline; - llvm::StringRef LTONewPmPasses; - llvm::StringRef LTOObjPath; - llvm::StringRef LTOSampleProfile; - llvm::StringRef MapFile; - llvm::StringRef OutputFile; - llvm::StringRef OptRemarksFilename; - llvm::StringRef ProgName; - llvm::StringRef SoName; - llvm::StringRef Sysroot; - llvm::StringRef ThinLTOCacheDir; - llvm::StringRef ThinLTOIndexOnlyArg; - std::pair ThinLTOObjectSuffixReplace; - std::pair ThinLTOPrefixReplace; - std::string Rpath; - std::vector VersionDefinitions; - std::vector AuxiliaryList; - std::vector FilterList; - std::vector SearchPaths; - std::vector SymbolOrderingFile; - std::vector Undefined; - std::vector DynamicList; - std::vector VersionScriptGlobals; - std::vector VersionScriptLocals; - std::vector BuildIdVector; + uint8_t osabi = 0; + uint32_t andFeatures = 0; + llvm::CachePruningPolicy thinLTOCachePolicy; + llvm::StringMap sectionStartMap; + llvm::StringRef chroot; + llvm::StringRef dynamicLinker; + llvm::StringRef dwoDir; + llvm::StringRef entry; + llvm::StringRef emulation; + llvm::StringRef fini; + llvm::StringRef init; + llvm::StringRef ltoAAPipeline; + llvm::StringRef ltoCSProfileFile; + llvm::StringRef ltoNewPmPasses; + llvm::StringRef ltoObjPath; + llvm::StringRef ltoSampleProfile; + llvm::StringRef mapFile; + llvm::StringRef outputFile; + llvm::StringRef optRemarksFilename; + llvm::StringRef optRemarksPasses; + llvm::StringRef optRemarksFormat; + llvm::StringRef progName; + llvm::StringRef printSymbolOrder; + llvm::StringRef soName; + llvm::StringRef sysroot; + llvm::StringRef thinLTOCacheDir; + llvm::StringRef thinLTOIndexOnlyArg; + std::pair thinLTOObjectSuffixReplace; + std::pair thinLTOPrefixReplace; + std::string rpath; + std::vector versionDefinitions; + std::vector auxiliaryList; + std::vector filterList; + std::vector searchPaths; + std::vector symbolOrderingFile; + std::vector undefined; + std::vector dynamicList; + std::vector versionScriptGlobals; + std::vector versionScriptLocals; + std::vector buildIdVector; llvm::MapVector, uint64_t> - CallGraphProfile; - bool AllowMultipleDefinition; - bool AndroidPackDynRelocs; - bool ARMHasBlx = false; - bool ARMHasMovtMovw = false; - bool ARMJ1J2BranchEncoding = false; - bool AsNeeded = false; - bool Bsymbolic; - bool BsymbolicFunctions; - bool CallGraphProfileSort; - bool CheckSections; - bool CompressDebugSections; - bool Cref; - bool DefineCommon; - bool Demangle = true; - bool DisableVerify; - bool EhFrameHdr; - bool EmitLLVM; - bool EmitRelocs; - bool EnableNewDtags; - bool ExecuteOnly; - bool ExportDynamic; - bool FixCortexA53Errata843419; - bool FormatBinary = false; - bool GcSections; - bool GdbIndex; - bool GnuHash = false; - bool GnuUnique; - bool HasDynamicList = false; - bool HasDynSymTab; - bool IgnoreDataAddressEquality; - bool IgnoreFunctionAddressEquality; - bool LTODebugPassManager; - bool LTONewPassManager; - bool MergeArmExidx; - bool MipsN32Abi = false; - bool NoinhibitExec; - bool Nostdlib; - bool OFormatBinary; - bool Omagic; - bool OptRemarksWithHotness; - bool Pie; - bool PrintGcSections; - bool PrintIcfSections; - bool Relocatable; - bool RelrPackDynRelocs; - bool SaveTemps; - bool SingleRoRx; - bool Shared; - bool Static = false; - bool SysvHash = false; - bool Target1Rel; - bool Trace; - bool ThinLTOEmitImportsFiles; - bool ThinLTOIndexOnly; - bool TocOptimize; - bool UndefinedVersion; - bool UseAndroidRelrTags = false; - bool WarnBackrefs; - bool WarnCommon; - bool WarnIfuncTextrel; - bool WarnMissingEntry; - bool WarnSymbolOrdering; - bool WriteAddends; - bool ZCombreloc; - bool ZCopyreloc; - bool ZExecstack; - bool ZGlobal; - bool ZHazardplt; - bool ZInitfirst; - bool ZInterpose; - bool ZKeepTextSectionPrefix; - bool ZNodefaultlib; - bool ZNodelete; - bool ZNodlopen; - bool ZNow; - bool ZOrigin; - bool ZRelro; - bool ZRodynamic; - bool ZText; - bool ZRetpolineplt; - bool ZWxneeded; - DiscardPolicy Discard; - ICFLevel ICF; - OrphanHandlingPolicy OrphanHandling; - SortSectionPolicy SortSection; - StripPolicy Strip; - UnresolvedPolicy UnresolvedSymbols; - Target2Policy Target2; - ARMVFPArgKind ARMVFPArgs = ARMVFPArgKind::Default; - BuildIdKind BuildId = BuildIdKind::None; - ELFKind EKind = ELFNoneKind; - uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL; - uint16_t EMachine = llvm::ELF::EM_NONE; - llvm::Optional ImageBase; - uint64_t MaxPageSize; - uint64_t MipsGotSize; - uint64_t ZStackSize; - unsigned LTOPartitions; - unsigned LTOO; - unsigned Optimize; - unsigned ThinLTOJobs; - int32_t SplitStackAdjustSize; + callGraphProfile; + bool allowMultipleDefinition; + bool allowShlibUndefined; + bool androidPackDynRelocs; + bool armHasBlx = false; + bool armHasMovtMovw = false; + bool armJ1J2BranchEncoding = false; + bool asNeeded = false; + bool bsymbolic; + bool bsymbolicFunctions; + bool callGraphProfileSort; + bool checkSections; + bool compressDebugSections; + bool cref; + bool defineCommon; + bool demangle = true; + bool dependentLibraries; + bool disableVerify; + bool ehFrameHdr; + bool emitLLVM; + bool emitRelocs; + bool enableNewDtags; + bool executeOnly; + bool exportDynamic; + bool fixCortexA53Errata843419; + bool forceBTI; + bool formatBinary = false; + bool requireCET; + bool gcSections; + bool gdbIndex; + bool gnuHash = false; + bool gnuUnique; + bool hasDynamicList = false; + bool hasDynSymTab; + bool ignoreDataAddressEquality; + bool ignoreFunctionAddressEquality; + bool ltoCSProfileGenerate; + bool ltoDebugPassManager; + bool ltoNewPassManager; + bool mergeArmExidx; + bool mipsN32Abi = false; + bool nmagic; + bool noinhibitExec; + bool nostdlib; + bool oFormatBinary; + bool omagic; + bool optRemarksWithHotness; + bool pacPlt; + bool picThunk; + bool pie; + bool printGcSections; + bool printIcfSections; + bool relocatable; + bool relrPackDynRelocs; + bool saveTemps; + bool singleRoRx; + bool shared; + bool isStatic = false; + bool sysvHash = false; + bool target1Rel; + bool trace; + bool thinLTOEmitImportsFiles; + bool thinLTOIndexOnly; + bool tocOptimize; + bool undefinedVersion; + bool useAndroidRelrTags = false; + bool warnBackrefs; + bool warnCommon; + bool warnIfuncTextrel; + bool warnMissingEntry; + bool warnSymbolOrdering; + bool writeAddends; + bool zCombreloc; + bool zCopyreloc; + bool zExecstack; + bool zGlobal; + bool zHazardplt; + bool zIfuncNoplt; + bool zInitfirst; + bool zInterpose; + bool zKeepTextSectionPrefix; + bool zNodefaultlib; + bool zNodelete; + bool zNodlopen; + bool zNow; + bool zOrigin; + bool zRelro; + bool zRodynamic; + bool zText; + bool zRetpolineplt; + bool zWxneeded; + DiscardPolicy discard; + ICFLevel icf; + OrphanHandlingPolicy orphanHandling; + SortSectionPolicy sortSection; + StripPolicy strip; + UnresolvedPolicy unresolvedSymbols; + Target2Policy target2; + ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default; + BuildIdKind buildId = BuildIdKind::None; + ELFKind ekind = ELFNoneKind; + uint16_t defaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL; + uint16_t emachine = llvm::ELF::EM_NONE; + llvm::Optional imageBase; + uint64_t commonPageSize; + uint64_t maxPageSize; + uint64_t mipsGotSize; + uint64_t zStackSize; + unsigned ltoPartitions; + unsigned ltoo; + unsigned optimize; + unsigned thinLTOJobs; + int32_t splitStackAdjustSize; // The following config options do not directly correspond to any // particualr command line options. // True if we need to pass through relocations in input files to the // output file. Usually false because we consume relocations. - bool CopyRelocs; + bool copyRelocs; // True if the target is ELF64. False if ELF32. - bool Is64; + bool is64; // True if the target is little-endian. False if big-endian. - bool IsLE; + bool isLE; - // endianness::little if IsLE is true. endianness::big otherwise. - llvm::support::endianness Endianness; + // endianness::little if isLE is true. endianness::big otherwise. + llvm::support::endianness endianness; // True if the target is the little-endian MIPS64. // // The reason why we have this variable only for the MIPS is because // we use this often. Some ELF headers for MIPS64EL are in a // mixed-endian (which is horrible and I'd say that's a serious spec // bug), and we need to know whether we are reading MIPS ELF files or // not in various places. // // (Note that MIPS64EL is not a typo for MIPS64LE. This is the official // name whatever that means. A fun hypothesis is that "EL" is short for // little-endian written in the little-endian order, but I don't know // if that's true.) - bool IsMips64EL; + bool isMips64EL; + // True if we need to set the DF_STATIC_TLS flag to an output file, + // which works as a hint to the dynamic loader that the file contains + // code compiled with the static TLS model. The thread-local variable + // compiled with the static TLS model is faster but less flexible, and + // it may not be loaded using dlopen(). + // + // We set this flag to true when we see a relocation for the static TLS + // model. Once this becomes true, it will never become false. + // + // Since the flag is updated by multi-threaded code, we use std::atomic. + // (Writing to a variable is not considered thread-safe even if the + // variable is boolean and we always set the same value from all threads.) + std::atomic hasStaticTlsModel{false}; + // Holds set of ELF header flags for the target. - uint32_t EFlags = 0; + 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 // the location where the relocations are applied. So, REL is more // compact than RELA but requires a bit of more work to process. // // (From the linker writer's view, this distinction is not necessary. // If the ELF had chosen whichever and sticked with it, it would have // been easier to write code to process relocations, but it's too late // to change the spec.) // // Each ABI defines its relocation type. IsRela is true if target // uses RELA. As far as we know, all 64-bit ABIs are using RELA. A // few 32-bit ABIs are using RELA too. - bool IsRela; + bool isRela; // True if we are creating position-independent code. - bool Pic; + bool isPic; // 4 for ELF32, 8 for ELF64. - int Wordsize; + int wordsize; }; // The only instance of Configuration struct. -extern Configuration *Config; +extern Configuration *config; -static inline void errorOrWarn(const Twine &Msg) { - if (!Config->NoinhibitExec) - error(Msg); +static inline void errorOrWarn(const Twine &msg) { + if (!config->noinhibitExec) + error(msg); else - warn(Msg); + warn(msg); } } // namespace elf } // namespace lld #endif Index: vendor/lld/dist/ELF/DWARF.cpp =================================================================== --- vendor/lld/dist/ELF/DWARF.cpp (revision 351287) +++ vendor/lld/dist/ELF/DWARF.cpp (revision 351288) @@ -1,109 +1,129 @@ //===- DWARF.cpp ----------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// // // The -gdb-index option instructs the linker to emit a .gdb_index section. // The section contains information to make gdb startup faster. // The format of the section is described at // https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html. // //===----------------------------------------------------------------------===// #include "DWARF.h" #include "Symbols.h" #include "Target.h" #include "lld/Common/Memory.h" #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" #include "llvm/Object/ELFObjectFile.h" using namespace llvm; using namespace llvm::object; using namespace lld; using namespace lld::elf; -template LLDDwarfObj::LLDDwarfObj(ObjFile *Obj) { - for (InputSectionBase *Sec : Obj->getSections()) { - if (!Sec) +template LLDDwarfObj::LLDDwarfObj(ObjFile *obj) { + for (InputSectionBase *sec : obj->getSections()) { + if (!sec) continue; - if (LLDDWARFSection *M = - StringSwitch(Sec->Name) - .Case(".debug_addr", &AddrSection) - .Case(".debug_gnu_pubnames", &GnuPubNamesSection) - .Case(".debug_gnu_pubtypes", &GnuPubTypesSection) - .Case(".debug_info", &InfoSection) - .Case(".debug_ranges", &RangeSection) - .Case(".debug_rnglists", &RngListsSection) - .Case(".debug_line", &LineSection) + if (LLDDWARFSection *m = + StringSwitch(sec->name) + .Case(".debug_addr", &addrSection) + .Case(".debug_gnu_pubnames", &gnuPubNamesSection) + .Case(".debug_gnu_pubtypes", &gnuPubTypesSection) + .Case(".debug_info", &infoSection) + .Case(".debug_ranges", &rangeSection) + .Case(".debug_rnglists", &rngListsSection) + .Case(".debug_line", &lineSection) .Default(nullptr)) { - M->Data = toStringRef(Sec->data()); - M->Sec = Sec; + m->Data = toStringRef(sec->data()); + m->sec = sec; continue; } - if (Sec->Name == ".debug_abbrev") - AbbrevSection = toStringRef(Sec->data()); - else if (Sec->Name == ".debug_str") - StrSection = toStringRef(Sec->data()); - else if (Sec->Name == ".debug_line_str") - LineStringSection = toStringRef(Sec->data()); + if (sec->name == ".debug_abbrev") + abbrevSection = toStringRef(sec->data()); + else if (sec->name == ".debug_str") + strSection = toStringRef(sec->data()); + else if (sec->name == ".debug_line_str") + lineStringSection = toStringRef(sec->data()); } } +namespace { +template struct LLDRelocationResolver { + // In the ELF ABIs, S sepresents the value of the symbol in the relocation + // entry. For Rela, the addend is stored as part of the relocation entry. + static uint64_t resolve(object::RelocationRef ref, uint64_t s, + uint64_t /* A */) { + return s + ref.getRawDataRefImpl().p; + } +}; + +template struct LLDRelocationResolver> { + // For Rel, the addend A is supplied by the caller. + static uint64_t resolve(object::RelocationRef /*Ref*/, uint64_t s, + uint64_t a) { + return s + a; + } +}; +} // namespace + // 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 template Optional -LLDDwarfObj::findAux(const InputSectionBase &Sec, uint64_t Pos, - ArrayRef 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) +LLDDwarfObj::findAux(const InputSectionBase &sec, uint64_t pos, + ArrayRef rels) const { + auto it = + partition_point(rels, [=](const RelTy &a) { return a.r_offset < pos; }); + if (it == rels.end() || it->r_offset != pos) return None; - const RelTy &Rel = *It; + const RelTy &rel = *it; - const ObjFile *File = Sec.getFile(); - uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL); - const typename ELFT::Sym &Sym = File->getELFSyms()[SymIndex]; - uint32_t SecIndex = File->getSectionIndex(Sym); + const ObjFile *file = sec.getFile(); + uint32_t symIndex = rel.getSymbol(config->isMips64EL); + const typename ELFT::Sym &sym = file->template getELFSyms()[symIndex]; + uint32_t secIndex = file->getSectionIndex(sym); - // Broken debug info can point to a non-Defined symbol. - auto *DR = dyn_cast(&File->getRelocTargetSym(Rel)); - if (!DR) { - RelType Type = Rel.getType(Config->IsMips64EL); - if (Type != Target->NoneRel) - error(toString(File) + ": relocation " + lld::toString(Type) + " at 0x" + - llvm::utohexstr(Rel.r_offset) + " has unsupported target"); - return None; + // An undefined symbol may be a symbol defined in a discarded section. We + // shall still resolve it. This is important for --gdb-index: the end address + // offset of an entry in .debug_ranges is relocated. If it is not resolved, + // its zero value will terminate the decoding of .debug_ranges prematurely. + Symbol &s = file->getRelocTargetSym(rel); + uint64_t val = 0; + if (auto *dr = dyn_cast(&s)) { + val = dr->value; + + // FIXME: We should be consistent about always adding the file + // offset or not. + if (dr->section->flags & ELF::SHF_ALLOC) + val += cast(dr->section)->getOffsetInFile(); } - uint64_t Val = DR->Value + getAddend(Rel); - // FIXME: We should be consistent about always adding the file - // offset or not. - if (DR->Section->Flags & ELF::SHF_ALLOC) - Val += cast(DR->Section)->getOffsetInFile(); - - return RelocAddrEntry{SecIndex, Val}; + DataRefImpl d; + d.p = getAddend(rel); + return RelocAddrEntry{secIndex, RelocationRef(d, nullptr), + val, Optional(), + 0, LLDRelocationResolver::resolve}; } template -Optional LLDDwarfObj::find(const llvm::DWARFSection &S, - uint64_t Pos) const { - auto &Sec = static_cast(S); - if (Sec.Sec->AreRelocsRela) - return findAux(*Sec.Sec, Pos, Sec.Sec->template relas()); - return findAux(*Sec.Sec, Pos, Sec.Sec->template rels()); +Optional LLDDwarfObj::find(const llvm::DWARFSection &s, + uint64_t pos) const { + auto &sec = static_cast(s); + if (sec.sec->areRelocsRela) + return findAux(*sec.sec, pos, sec.sec->template relas()); + return findAux(*sec.sec, pos, sec.sec->template rels()); } template class elf::LLDDwarfObj; template class elf::LLDDwarfObj; template class elf::LLDDwarfObj; template class elf::LLDDwarfObj; Index: vendor/lld/dist/ELF/DWARF.h =================================================================== --- vendor/lld/dist/ELF/DWARF.h (revision 351287) +++ vendor/lld/dist/ELF/DWARF.h (revision 351288) @@ -1,93 +1,92 @@ //===- DWARF.h -----------------------------------------------*- C++ -*-===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===-------------------------------------------------------------------===// #ifndef LLD_ELF_DWARF_H #define LLD_ELF_DWARF_H #include "InputFiles.h" #include "llvm/ADT/STLExtras.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/Object/ELF.h" namespace lld { namespace elf { class InputSection; struct LLDDWARFSection final : public llvm::DWARFSection { - InputSectionBase *Sec = nullptr; + InputSectionBase *sec = nullptr; }; template class LLDDwarfObj final : public llvm::DWARFObject { public: - explicit LLDDwarfObj(ObjFile *Obj); + explicit LLDDwarfObj(ObjFile *obj); void forEachInfoSections( - llvm::function_ref F) const override { - F(InfoSection); + llvm::function_ref f) const override { + f(infoSection); } const llvm::DWARFSection &getRangeSection() const override { - return RangeSection; + return rangeSection; } const llvm::DWARFSection &getRnglistsSection() const override { - return RngListsSection; + return rngListsSection; } const llvm::DWARFSection &getLineSection() const override { - return LineSection; + return lineSection; } const llvm::DWARFSection &getAddrSection() const override { - return AddrSection; + return addrSection; } const llvm::DWARFSection &getGnuPubNamesSection() const override { - return GnuPubNamesSection; + return gnuPubNamesSection; } const llvm::DWARFSection &getGnuPubTypesSection() const override { - return GnuPubTypesSection; + return gnuPubTypesSection; } StringRef getFileName() const override { return ""; } - StringRef getAbbrevSection() const override { return AbbrevSection; } - StringRef getStringSection() const override { return StrSection; } - StringRef getLineStringSection() const override { return LineStringSection; } + StringRef getAbbrevSection() const override { return abbrevSection; } + StringRef getStringSection() const override { return strSection; } + StringRef getLineStringSection() const override { return lineStringSection; } bool isLittleEndian() const override { return ELFT::TargetEndianness == llvm::support::little; } - llvm::Optional find(const llvm::DWARFSection &Sec, - uint64_t Pos) const override; + llvm::Optional find(const llvm::DWARFSection &sec, + uint64_t pos) const override; private: template - llvm::Optional findAux(const InputSectionBase &Sec, - uint64_t Pos, - ArrayRef Rels) const; + llvm::Optional findAux(const InputSectionBase &sec, + uint64_t pos, + ArrayRef rels) const; - LLDDWARFSection GnuPubNamesSection; - LLDDWARFSection GnuPubTypesSection; - LLDDWARFSection InfoSection; - LLDDWARFSection RangeSection; - LLDDWARFSection RngListsSection; - LLDDWARFSection LineSection; - LLDDWARFSection AddrSection; - StringRef AbbrevSection; - StringRef StrSection; - StringRef LineStringSection; + LLDDWARFSection gnuPubNamesSection; + LLDDWARFSection gnuPubTypesSection; + LLDDWARFSection infoSection; + LLDDWARFSection rangeSection; + LLDDWARFSection rngListsSection; + LLDDWARFSection lineSection; + LLDDWARFSection addrSection; + StringRef abbrevSection; + StringRef strSection; + StringRef lineStringSection; }; } // namespace elf } // namespace lld #endif Index: vendor/lld/dist/ELF/Driver.cpp =================================================================== --- vendor/lld/dist/ELF/Driver.cpp (revision 351287) +++ vendor/lld/dist/ELF/Driver.cpp (revision 351288) @@ -1,1648 +1,1911 @@ //===- Driver.cpp ---------------------------------------------------------===// // -// The LLVM Linker +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// //===----------------------------------------------------------------------===// // // The driver drives the entire linking process. It is responsible for // parsing command line options and doing whatever it is instructed to do. // // One notable thing in the LLD's driver when compared to other linkers is // that the LLD's driver is agnostic on the host operating system. // Other linkers usually have implicit default values (such as a dynamic // linker path or library paths) for each host OS. // // I don't think implicit default values are useful because they are // usually explicitly specified by the compiler driver. They can even // be harmful when you are doing cross-linking. Therefore, in LLD, we // simply trust the compiler driver to pass all required options and // don't try to make effort on our side. // //===----------------------------------------------------------------------===// #include "Driver.h" #include "Config.h" -#include "Filesystem.h" #include "ICF.h" #include "InputFiles.h" #include "InputSection.h" #include "LinkerScript.h" #include "MarkLive.h" #include "OutputSections.h" #include "ScriptParser.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "Writer.h" #include "lld/Common/Args.h" #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/Filesystem.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "lld/Common/TargetOptionsCommandFlags.h" #include "lld/Common/Threads.h" #include "lld/Common/Version.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" +#include "llvm/Support/GlobPattern.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" #include #include using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::sys; using namespace llvm::support; using namespace lld; using namespace lld::elf; -Configuration *elf::Config; -LinkerDriver *elf::Driver; +Configuration *elf::config; +LinkerDriver *elf::driver; -static void setConfigs(opt::InputArgList &Args); +static void setConfigs(opt::InputArgList &args); +static void readConfigs(opt::InputArgList &args); -bool elf::link(ArrayRef Args, bool CanExitEarly, - raw_ostream &Error) { - errorHandler().LogName = args::getFilenameWithoutExe(Args[0]); - errorHandler().ErrorLimitExceededMsg = +bool elf::link(ArrayRef args, bool canExitEarly, + raw_ostream &error) { + errorHandler().logName = args::getFilenameWithoutExe(args[0]); + errorHandler().errorLimitExceededMsg = "too many errors emitted, stopping now (use " "-error-limit=0 to see all errors)"; - errorHandler().ErrorOS = &Error; - errorHandler().ExitEarly = CanExitEarly; - errorHandler().ColorDiagnostics = Error.has_colors(); + errorHandler().errorOS = &error; + errorHandler().exitEarly = canExitEarly; + errorHandler().colorDiagnostics = error.has_colors(); - InputSections.clear(); - OutputSections.clear(); - BinaryFiles.clear(); - BitcodeFiles.clear(); - ObjectFiles.clear(); - SharedFiles.clear(); + inputSections.clear(); + outputSections.clear(); + binaryFiles.clear(); + bitcodeFiles.clear(); + objectFiles.clear(); + sharedFiles.clear(); - Config = make(); - Driver = make(); - Script = make(); - Symtab = make(); + config = make(); + driver = make(); + script = make(); + symtab = make(); - Tar = nullptr; - memset(&In, 0, sizeof(In)); + tar = nullptr; + memset(&in, 0, sizeof(in)); - Config->ProgName = Args[0]; + partitions = {Partition()}; - Driver->main(Args); + SharedFile::vernauxNum = 0; + config->progName = args[0]; + + driver->main(args); + // Exit immediately if we don't need to return to the caller. // This saves time because the overhead of calling destructors // for all globally-allocated objects is not negligible. - if (CanExitEarly) + if (canExitEarly) exitLld(errorCount() ? 1 : 0); freeArena(); return !errorCount(); } // Parses a linker -m option. -static std::tuple parseEmulation(StringRef Emul) { - uint8_t OSABI = 0; - StringRef S = Emul; - if (S.endswith("_fbsd")) { - S = S.drop_back(5); - OSABI = ELFOSABI_FREEBSD; +static std::tuple parseEmulation(StringRef emul) { + uint8_t osabi = 0; + StringRef s = emul; + if (s.endswith("_fbsd")) { + s = s.drop_back(5); + osabi = ELFOSABI_FREEBSD; } - std::pair Ret = - StringSwitch>(S) + std::pair ret = + StringSwitch>(s) .Cases("aarch64elf", "aarch64linux", "aarch64_elf64_le_vec", {ELF64LEKind, EM_AARCH64}) .Cases("armelf", "armelf_linux_eabi", {ELF32LEKind, EM_ARM}) .Case("elf32_x86_64", {ELF32LEKind, EM_X86_64}) .Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS}) .Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS}) .Case("elf32lriscv", {ELF32LEKind, EM_RISCV}) - .Case("elf32ppc", {ELF32BEKind, EM_PPC}) + .Cases("elf32ppc", "elf32ppclinux", {ELF32BEKind, EM_PPC}) .Case("elf64btsmip", {ELF64BEKind, EM_MIPS}) .Case("elf64ltsmip", {ELF64LEKind, EM_MIPS}) .Case("elf64lriscv", {ELF64LEKind, EM_RISCV}) .Case("elf64ppc", {ELF64BEKind, EM_PPC64}) .Case("elf64lppc", {ELF64LEKind, EM_PPC64}) .Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64}) .Case("elf_i386", {ELF32LEKind, EM_386}) .Case("elf_iamcu", {ELF32LEKind, EM_IAMCU}) .Default({ELFNoneKind, EM_NONE}); - if (Ret.first == ELFNoneKind) - error("unknown emulation: " + Emul); - return std::make_tuple(Ret.first, Ret.second, OSABI); + if (ret.first == ELFNoneKind) + error("unknown emulation: " + emul); + return std::make_tuple(ret.first, ret.second, osabi); } // Returns slices of MB by parsing MB as an archive file. // Each slice consists of a member file in the archive. std::vector> static getArchiveMembers( - MemoryBufferRef MB) { - std::unique_ptr File = - CHECK(Archive::create(MB), - MB.getBufferIdentifier() + ": failed to parse archive"); + MemoryBufferRef mb) { + std::unique_ptr file = + CHECK(Archive::create(mb), + mb.getBufferIdentifier() + ": failed to parse archive"); - std::vector> V; - Error Err = Error::success(); - bool AddToTar = File->isThin() && Tar; - for (const ErrorOr &COrErr : File->children(Err)) { - Archive::Child C = - CHECK(COrErr, MB.getBufferIdentifier() + + std::vector> v; + Error err = Error::success(); + bool addToTar = file->isThin() && tar; + for (const ErrorOr &cOrErr : file->children(err)) { + Archive::Child c = + CHECK(cOrErr, mb.getBufferIdentifier() + ": could not get the child of the archive"); - MemoryBufferRef MBRef = - CHECK(C.getMemoryBufferRef(), - MB.getBufferIdentifier() + + MemoryBufferRef mbref = + 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 (addToTar) + tar->append(relativeToRoot(check(c.getFullName())), mbref.getBuffer()); + v.push_back(std::make_pair(mbref, c.getChildOffset())); } - if (Err) - fatal(MB.getBufferIdentifier() + ": Archive::children failed: " + - toString(std::move(Err))); + if (err) + fatal(mb.getBufferIdentifier() + ": Archive::children failed: " + + toString(std::move(err))); // Take ownership of memory buffers created for members of thin archives. - for (std::unique_ptr &MB : File->takeThinBuffers()) - make>(std::move(MB)); + for (std::unique_ptr &mb : file->takeThinBuffers()) + make>(std::move(mb)); - return V; + return v; } // Opens a file and create a file object. Path has to be resolved already. -void LinkerDriver::addFile(StringRef Path, bool WithLOption) { +void LinkerDriver::addFile(StringRef path, bool withLOption) { using namespace sys::fs; - Optional Buffer = readFile(Path); - if (!Buffer.hasValue()) + Optional buffer = readFile(path); + if (!buffer.hasValue()) return; - MemoryBufferRef MBRef = *Buffer; + MemoryBufferRef mbref = *buffer; - if (Config->FormatBinary) { - Files.push_back(make(MBRef)); + if (config->formatBinary) { + files.push_back(make(mbref)); return; } - switch (identify_magic(MBRef.getBuffer())) { + switch (identify_magic(mbref.getBuffer())) { case file_magic::unknown: - readLinkerScript(MBRef); + readLinkerScript(mbref); return; case file_magic::archive: { // Handle -whole-archive. - if (InWholeArchive) { - for (const auto &P : getArchiveMembers(MBRef)) - Files.push_back(createObjectFile(P.first, Path, P.second)); + if (inWholeArchive) { + for (const auto &p : getArchiveMembers(mbref)) + files.push_back(createObjectFile(p.first, path, p.second)); return; } - std::unique_ptr File = - CHECK(Archive::create(MBRef), Path + ": failed to parse archive"); + std::unique_ptr file = + 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 // understand the LLVM bitcode file. It is a pretty common error, so // 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(P.first, Path, P.second)); + if (!file->isEmpty() && !file->hasSymbolTable()) { + // Check if all members are bitcode files. If not, ignore, which is the + // default action without the LTO hack described above. + for (const std::pair &p : + getArchiveMembers(mbref)) + if (identify_magic(p.first.getBuffer()) != file_magic::bitcode) { + error(path + ": archive has no index; run ranlib to add one"); + return; + } + + for (const std::pair &p : + getArchiveMembers(mbref)) + files.push_back(make(p.first, path, p.second)); return; } // Handle the regular case. - Files.push_back(make(std::move(File))); + files.push_back(make(std::move(file))); return; } case file_magic::elf_shared_object: - if (Config->Static || Config->Relocatable) { - error("attempted static link of dynamic object " + Path); + if (config->isStatic || config->relocatable) { + error("attempted static link of dynamic object " + path); return; } // DSOs usually have DT_SONAME tags in their ELF headers, and the // sonames are used to identify DSOs. But if they are missing, // they are identified by filenames. We don't know whether the new // file has a DT_SONAME or not because we haven't parsed it yet. // Here, we set the default soname for the file because we might // need it later. // // If a file was specified by -lfoo, the directory part is not // significant, as a user did not specify it. This behavior is // compatible with GNU. - Files.push_back( - createSharedFile(MBRef, WithLOption ? path::filename(Path) : Path)); + files.push_back( + make(mbref, withLOption ? path::filename(path) : path)); return; case file_magic::bitcode: case file_magic::elf_relocatable: - if (InLib) - Files.push_back(make(MBRef, "", 0)); + if (inLib) + files.push_back(make(mbref, "", 0)); else - Files.push_back(createObjectFile(MBRef)); + files.push_back(createObjectFile(mbref)); break; default: - error(Path + ": unknown file type"); + error(path + ": unknown file type"); } } // Add a given library by searching it from input search paths. -void LinkerDriver::addLibrary(StringRef Name) { - if (Optional Path = searchLibrary(Name)) - addFile(*Path, /*WithLOption=*/true); +void LinkerDriver::addLibrary(StringRef name) { + if (Optional path = searchLibrary(name)) + addFile(*path, /*withLOption=*/true); else - error("unable to find library -l" + Name); + error("unable to find library -l" + name); } // This function is called on startup. We need this for LTO since // LTO calls LLVM functions to compile bitcode files to native code. // Technically this can be delayed until we read bitcode files, but // we don't bother to do lazily because the initialization is fast. static void initLLVM() { InitializeAllTargets(); InitializeAllTargetMCs(); InitializeAllAsmPrinters(); InitializeAllAsmParsers(); } // Some command line options or some combinations of them are not allowed. // This function checks for such errors. static void checkOptions() { // The MIPS ABI as of 2016 does not support the GNU-style symbol lookup // table which is a relatively new feature. - if (Config->EMachine == EM_MIPS && Config->GnuHash) + 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) + if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64) error("--fix-cortex-a53-843419 is only supported on AArch64 targets"); - if (Config->TocOptimize && Config->EMachine != EM_PPC64) + if (config->tocOptimize && config->emachine != EM_PPC64) error("--toc-optimize is only supported on the PowerPC64 target"); - if (Config->Pie && Config->Shared) + if (config->pie && config->shared) error("-shared and -pie may not be used together"); - if (!Config->Shared && !Config->FilterList.empty()) + if (!config->shared && !config->filterList.empty()) error("-F may not be used without -shared"); - if (!Config->Shared && !Config->AuxiliaryList.empty()) + if (!config->shared && !config->auxiliaryList.empty()) error("-f may not be used without -shared"); - if (!Config->Relocatable && !Config->DefineCommon) + if (!config->relocatable && !config->defineCommon) error("-no-define-common not supported in non relocatable output"); - if (Config->Relocatable) { - if (Config->Shared) + if (config->zText && config->zIfuncNoplt) + error("-z text and -z ifunc-noplt may not be used together"); + + if (config->relocatable) { + if (config->shared) error("-r and -shared may not be used together"); - if (Config->GcSections) + if (config->gcSections) error("-r and --gc-sections may not be used together"); - if (Config->GdbIndex) + if (config->gdbIndex) error("-r and --gdb-index may not be used together"); - if (Config->ICF != ICFLevel::None) + if (config->icf != ICFLevel::None) error("-r and --icf may not be used together"); - if (Config->Pie) + if (config->pie) error("-r and -pie may not be used together"); } - if (Config->ExecuteOnly) { - if (Config->EMachine != EM_AARCH64) + if (config->executeOnly) { + if (config->emachine != EM_AARCH64) error("-execute-only is only supported on AArch64 targets"); - if (Config->SingleRoRx && !Script->HasSectionsCommand) + if (config->singleRoRx && !script->hasSectionsCommand) error("-execute-only and -no-rosegment cannot be used together"); } + + if (config->zRetpolineplt && config->requireCET) + error("--require-cet may not be used with -z retpolineplt"); + + if (config->emachine != EM_AARCH64) { + if (config->pacPlt) + error("--pac-plt only supported on AArch64"); + if (config->forceBTI) + error("--force-bti only supported on AArch64"); + } } -static const char *getReproduceOption(opt::InputArgList &Args) { - if (auto *Arg = Args.getLastArg(OPT_reproduce)) - return Arg->getValue(); +static const char *getReproduceOption(opt::InputArgList &args) { + if (auto *arg = args.getLastArg(OPT_reproduce)) + return arg->getValue(); return getenv("LLD_REPRODUCE"); } -static bool hasZOption(opt::InputArgList &Args, StringRef Key) { - for (auto *Arg : Args.filtered(OPT_z)) - if (Key == Arg->getValue()) +static bool hasZOption(opt::InputArgList &args, StringRef key) { + for (auto *arg : args.filtered(OPT_z)) + if (key == arg->getValue()) return true; return false; } -static bool getZFlag(opt::InputArgList &Args, StringRef K1, StringRef K2, +static bool getZFlag(opt::InputArgList &args, StringRef k1, StringRef k2, bool Default) { - for (auto *Arg : Args.filtered_reverse(OPT_z)) { - if (K1 == Arg->getValue()) + for (auto *arg : args.filtered_reverse(OPT_z)) { + if (k1 == arg->getValue()) return true; - if (K2 == Arg->getValue()) + if (k2 == arg->getValue()) return false; } return Default; } -static bool isKnownZFlag(StringRef S) { - return S == "combreloc" || S == "copyreloc" || S == "defs" || - S == "execstack" || S == "global" || S == "hazardplt" || - S == "initfirst" || S == "interpose" || - S == "keep-text-section-prefix" || S == "lazy" || S == "muldefs" || - S == "nocombreloc" || S == "nocopyreloc" || S == "nodefaultlib" || - S == "nodelete" || S == "nodlopen" || S == "noexecstack" || - S == "nokeep-text-section-prefix" || S == "norelro" || S == "notext" || - S == "now" || S == "origin" || S == "relro" || S == "retpolineplt" || - S == "rodynamic" || S == "text" || S == "wxneeded" || - S.startswith("max-page-size=") || S.startswith("stack-size="); +static bool isKnownZFlag(StringRef s) { + return s == "combreloc" || s == "copyreloc" || s == "defs" || + s == "execstack" || s == "global" || s == "hazardplt" || + s == "ifunc-noplt" || s == "initfirst" || s == "interpose" || + s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" || + s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" || + s == "nodelete" || s == "nodlopen" || s == "noexecstack" || + s == "nokeep-text-section-prefix" || s == "norelro" || s == "notext" || + s == "now" || s == "origin" || s == "relro" || s == "retpolineplt" || + s == "rodynamic" || s == "text" || s == "wxneeded" || + s.startswith("common-page-size") || s.startswith("max-page-size=") || + s.startswith("stack-size="); } // Report an error for an unknown -z option. -static void checkZOptions(opt::InputArgList &Args) { - for (auto *Arg : Args.filtered(OPT_z)) - if (!isKnownZFlag(Arg->getValue())) - error("unknown -z value: " + StringRef(Arg->getValue())); +static void checkZOptions(opt::InputArgList &args) { + for (auto *arg : args.filtered(OPT_z)) + if (!isKnownZFlag(arg->getValue())) + error("unknown -z value: " + StringRef(arg->getValue())); } -void LinkerDriver::main(ArrayRef ArgsArr) { - ELFOptTable Parser; - opt::InputArgList Args = Parser.parse(ArgsArr.slice(1)); +void LinkerDriver::main(ArrayRef argsArr) { + ELFOptTable parser; + opt::InputArgList args = parser.parse(argsArr.slice(1)); // Interpret this flag early because error() depends on them. - errorHandler().ErrorLimit = args::getInteger(Args, OPT_error_limit, 20); + errorHandler().errorLimit = args::getInteger(args, OPT_error_limit, 20); + checkZOptions(args); // Handle -help - if (Args.hasArg(OPT_help)) { + if (args.hasArg(OPT_help)) { printHelp(); return; } // Handle -v or -version. // // A note about "compatible with GNU linkers" message: this is a hack for // scripts generated by GNU Libtool 2.4.6 (released in February 2014 and // still the newest version in March 2017) or earlier to recognize LLD as // a GNU compatible linker. As long as an output for the -v option // contains "GNU" or "with BFD", they recognize us as GNU-compatible. // // This is somewhat ugly hack, but in reality, we had no choice other // than doing this. Considering the very long release cycle of Libtool, // it is not easy to improve it to recognize LLD as a GNU compatible // linker in a timely manner. Even if we can make it, there are still a // lot of "configure" scripts out there that are generated by old version // of Libtool. We cannot convince every software developer to migrate to // the latest version and re-generate scripts. So we have this hack. - if (Args.hasArg(OPT_v) || Args.hasArg(OPT_version)) + if (args.hasArg(OPT_v) || args.hasArg(OPT_version)) message(getLLDVersion() + " (compatible with GNU linkers)"); - if (const char *Path = getReproduceOption(Args)) { + if (const char *path = getReproduceOption(args)) { // Note that --reproduce is a debug option so you can ignore it // if you are trying to understand the whole picture of the code. - Expected> ErrOrWriter = - TarWriter::create(Path, path::stem(Path)); - if (ErrOrWriter) { - Tar = std::move(*ErrOrWriter); - Tar->append("response.txt", createResponseFile(Args)); - Tar->append("version.txt", getLLDVersion() + "\n"); + Expected> errOrWriter = + TarWriter::create(path, path::stem(path)); + if (errOrWriter) { + tar = std::move(*errOrWriter); + tar->append("response.txt", createResponseFile(args)); + tar->append("version.txt", getLLDVersion() + "\n"); } else { - error("--reproduce: " + toString(ErrOrWriter.takeError())); + error("--reproduce: " + toString(errOrWriter.takeError())); } } - readConfigs(Args); - checkZOptions(Args); + readConfigs(args); // The behavior of -v or --version is a bit strange, but this is // needed for compatibility with GNU linkers. - if (Args.hasArg(OPT_v) && !Args.hasArg(OPT_INPUT)) + if (args.hasArg(OPT_v) && !args.hasArg(OPT_INPUT)) return; - if (Args.hasArg(OPT_version)) + if (args.hasArg(OPT_version)) return; initLLVM(); - createFiles(Args); + createFiles(args); if (errorCount()) return; inferMachineType(); - setConfigs(Args); + setConfigs(args); checkOptions(); if (errorCount()) return; - switch (Config->EKind) { + // The Target instance handles target-specific stuff, such as applying + // relocations or writing a PLT section. It also contains target-dependent + // values such as a default image base address. + target = getTarget(); + + switch (config->ekind) { case ELF32LEKind: - link(Args); + link(args); return; case ELF32BEKind: - link(Args); + link(args); return; case ELF64LEKind: - link(Args); + link(args); return; case ELF64BEKind: - link(Args); + link(args); return; default: llvm_unreachable("unknown Config->EKind"); } } -static std::string getRpath(opt::InputArgList &Args) { - std::vector V = args::getStrings(Args, OPT_rpath); - return llvm::join(V.begin(), V.end(), ":"); +static std::string getRpath(opt::InputArgList &args) { + std::vector 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) { - UnresolvedPolicy ErrorOrWarn = Args.hasFlag(OPT_error_unresolved_symbols, +static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) { + UnresolvedPolicy errorOrWarn = args.hasFlag(OPT_error_unresolved_symbols, OPT_warn_unresolved_symbols, true) ? UnresolvedPolicy::ReportError : UnresolvedPolicy::Warn; // Process the last of -unresolved-symbols, -no-undefined or -z defs. - for (auto *Arg : llvm::reverse(Args)) { - switch (Arg->getOption().getID()) { + for (auto *arg : llvm::reverse(args)) { + switch (arg->getOption().getID()) { case OPT_unresolved_symbols: { - StringRef S = Arg->getValue(); - if (S == "ignore-all" || S == "ignore-in-object-files") + StringRef s = arg->getValue(); + if (s == "ignore-all" || s == "ignore-in-object-files") return UnresolvedPolicy::Ignore; - if (S == "ignore-in-shared-libs" || S == "report-all") - return ErrorOrWarn; - error("unknown --unresolved-symbols value: " + S); + if (s == "ignore-in-shared-libs" || s == "report-all") + return errorOrWarn; + error("unknown --unresolved-symbols value: " + s); continue; } case OPT_no_undefined: - return ErrorOrWarn; + return errorOrWarn; case OPT_z: - if (StringRef(Arg->getValue()) == "defs") - return ErrorOrWarn; + if (StringRef(arg->getValue()) == "defs") + return errorOrWarn; continue; } } // -shared implies -unresolved-symbols=ignore-all because missing // symbols are likely to be resolved at runtime using other DSOs. - if (Config->Shared) + if (config->shared) return UnresolvedPolicy::Ignore; - return ErrorOrWarn; + return errorOrWarn; } -static Target2Policy getTarget2(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_target2, "got-rel"); - if (S == "rel") +static Target2Policy getTarget2(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_target2, "got-rel"); + if (s == "rel") return Target2Policy::Rel; - if (S == "abs") + if (s == "abs") return Target2Policy::Abs; - if (S == "got-rel") + if (s == "got-rel") return Target2Policy::GotRel; - error("unknown --target2 option: " + S); + error("unknown --target2 option: " + s); return Target2Policy::GotRel; } -static bool isOutputFormatBinary(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_oformat, "elf"); - if (S == "binary") +static bool isOutputFormatBinary(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_oformat, "elf"); + if (s == "binary") return true; - if (!S.startswith("elf")) - error("unknown --oformat value: " + S); + if (!s.startswith("elf")) + error("unknown --oformat value: " + s); return false; } -static DiscardPolicy getDiscard(opt::InputArgList &Args) { - if (Args.hasArg(OPT_relocatable)) +static DiscardPolicy getDiscard(opt::InputArgList &args) { + if (args.hasArg(OPT_relocatable)) return DiscardPolicy::None; - auto *Arg = - Args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none); - if (!Arg) + auto *arg = + args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none); + if (!arg) return DiscardPolicy::Default; - if (Arg->getOption().getID() == OPT_discard_all) + if (arg->getOption().getID() == OPT_discard_all) return DiscardPolicy::All; - if (Arg->getOption().getID() == OPT_discard_locals) + if (arg->getOption().getID() == OPT_discard_locals) return DiscardPolicy::Locals; return DiscardPolicy::None; } -static StringRef getDynamicLinker(opt::InputArgList &Args) { - auto *Arg = Args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker); - if (!Arg || Arg->getOption().getID() == OPT_no_dynamic_linker) +static StringRef getDynamicLinker(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker); + if (!arg || arg->getOption().getID() == OPT_no_dynamic_linker) return ""; - return Arg->getValue(); + return arg->getValue(); } -static ICFLevel getICF(opt::InputArgList &Args) { - auto *Arg = Args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all); - if (!Arg || Arg->getOption().getID() == OPT_icf_none) +static ICFLevel getICF(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all); + if (!arg || arg->getOption().getID() == OPT_icf_none) return ICFLevel::None; - if (Arg->getOption().getID() == OPT_icf_safe) + if (arg->getOption().getID() == OPT_icf_safe) return ICFLevel::Safe; return ICFLevel::All; } -static StripPolicy getStrip(opt::InputArgList &Args) { - if (Args.hasArg(OPT_relocatable)) +static StripPolicy getStrip(opt::InputArgList &args) { + if (args.hasArg(OPT_relocatable)) return StripPolicy::None; - auto *Arg = Args.getLastArg(OPT_strip_all, OPT_strip_debug); - if (!Arg) + auto *arg = args.getLastArg(OPT_strip_all, OPT_strip_debug); + if (!arg) return StripPolicy::None; - if (Arg->getOption().getID() == OPT_strip_all) + if (arg->getOption().getID() == OPT_strip_all) return StripPolicy::All; return StripPolicy::Debug; } -static uint64_t parseSectionAddress(StringRef S, const opt::Arg &Arg) { - uint64_t VA = 0; - if (S.startswith("0x")) - S = S.drop_front(2); - if (!to_integer(S, VA, 16)) - error("invalid argument: " + toString(Arg)); - return VA; +static uint64_t parseSectionAddress(StringRef s, opt::InputArgList &args, + const opt::Arg &arg) { + uint64_t va = 0; + if (s.startswith("0x")) + s = s.drop_front(2); + if (!to_integer(s, va, 16)) + error("invalid argument: " + arg.getAsString(args)); + return va; } -static StringMap getSectionStartMap(opt::InputArgList &Args) { - StringMap Ret; - for (auto *Arg : Args.filtered(OPT_section_start)) { - StringRef Name; - StringRef Addr; - std::tie(Name, Addr) = StringRef(Arg->getValue()).split('='); - Ret[Name] = parseSectionAddress(Addr, *Arg); +static StringMap getSectionStartMap(opt::InputArgList &args) { + StringMap ret; + for (auto *arg : args.filtered(OPT_section_start)) { + StringRef name; + StringRef addr; + std::tie(name, addr) = StringRef(arg->getValue()).split('='); + ret[name] = parseSectionAddress(addr, args, *arg); } - if (auto *Arg = Args.getLastArg(OPT_Ttext)) - Ret[".text"] = parseSectionAddress(Arg->getValue(), *Arg); - if (auto *Arg = Args.getLastArg(OPT_Tdata)) - Ret[".data"] = parseSectionAddress(Arg->getValue(), *Arg); - if (auto *Arg = Args.getLastArg(OPT_Tbss)) - Ret[".bss"] = parseSectionAddress(Arg->getValue(), *Arg); - return Ret; + if (auto *arg = args.getLastArg(OPT_Ttext)) + ret[".text"] = parseSectionAddress(arg->getValue(), args, *arg); + if (auto *arg = args.getLastArg(OPT_Tdata)) + ret[".data"] = parseSectionAddress(arg->getValue(), args, *arg); + if (auto *arg = args.getLastArg(OPT_Tbss)) + ret[".bss"] = parseSectionAddress(arg->getValue(), args, *arg); + return ret; } -static SortSectionPolicy getSortSection(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_sort_section); - if (S == "alignment") +static SortSectionPolicy getSortSection(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_sort_section); + if (s == "alignment") return SortSectionPolicy::Alignment; - if (S == "name") + if (s == "name") return SortSectionPolicy::Name; - if (!S.empty()) - error("unknown --sort-section rule: " + S); + if (!s.empty()) + error("unknown --sort-section rule: " + s); return SortSectionPolicy::Default; } -static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_orphan_handling, "place"); - if (S == "warn") +static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_orphan_handling, "place"); + if (s == "warn") return OrphanHandlingPolicy::Warn; - if (S == "error") + if (s == "error") return OrphanHandlingPolicy::Error; - if (S != "place") - error("unknown --orphan-handling mode: " + S); + if (s != "place") + error("unknown --orphan-handling mode: " + s); return OrphanHandlingPolicy::Place; } // Parse --build-id or --build-id=