diff options
Diffstat (limited to 'COFF')
-rw-r--r-- | COFF/CMakeLists.txt | 1 | ||||
-rw-r--r-- | COFF/Chunks.cpp | 855 | ||||
-rw-r--r-- | COFF/Chunks.h | 574 | ||||
-rw-r--r-- | COFF/Config.h | 241 | ||||
-rw-r--r-- | COFF/DLL.cpp | 703 | ||||
-rw-r--r-- | COFF/DLL.h | 60 | ||||
-rw-r--r-- | COFF/DebugTypes.cpp | 268 | ||||
-rw-r--r-- | COFF/DebugTypes.h | 60 | ||||
-rw-r--r-- | COFF/Driver.cpp | 1922 | ||||
-rw-r--r-- | COFF/Driver.h | 111 | ||||
-rw-r--r-- | COFF/DriverUtils.cpp | 965 | ||||
-rw-r--r-- | COFF/ICF.cpp | 283 | ||||
-rw-r--r-- | COFF/ICF.h | 9 | ||||
-rw-r--r-- | COFF/InputFiles.cpp | 915 | ||||
-rw-r--r-- | COFF/InputFiles.h | 209 | ||||
-rw-r--r-- | COFF/LTO.cpp | 188 | ||||
-rw-r--r-- | COFF/LTO.h | 19 | ||||
-rw-r--r-- | COFF/MapFile.cpp | 115 | ||||
-rw-r--r-- | COFF/MapFile.h | 9 | ||||
-rw-r--r-- | COFF/MarkLive.cpp | 65 | ||||
-rw-r--r-- | COFF/MarkLive.h | 11 | ||||
-rw-r--r-- | COFF/MinGW.cpp | 160 | ||||
-rw-r--r-- | COFF/MinGW.h | 25 | ||||
-rw-r--r-- | COFF/Options.td | 65 | ||||
-rw-r--r-- | COFF/PDB.cpp | 2035 | ||||
-rw-r--r-- | COFF/PDB.h | 19 | ||||
-rw-r--r-- | COFF/SymbolTable.cpp | 729 | ||||
-rw-r--r-- | COFF/SymbolTable.h | 88 | ||||
-rw-r--r-- | COFF/Symbols.cpp | 106 | ||||
-rw-r--r-- | COFF/Symbols.h | 243 | ||||
-rw-r--r-- | COFF/TypeMerger.h | 65 | ||||
-rw-r--r-- | COFF/Writer.cpp | 2044 | ||||
-rw-r--r-- | COFF/Writer.h | 62 |
33 files changed, 7470 insertions, 5754 deletions
diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt index bb241e788c19..c7ef7c47fea1 100644 --- a/COFF/CMakeLists.txt +++ b/COFF/CMakeLists.txt @@ -8,6 +8,7 @@ endif() add_lld_library(lldCOFF Chunks.cpp + DebugTypes.cpp DLL.cpp Driver.cpp DriverUtils.cpp diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp index 29131d7eb8db..0e43d2b478b4 100644 --- a/COFF/Chunks.cpp +++ b/COFF/Chunks.cpp @@ -1,9 +1,8 @@ //===- Chunks.cpp ---------------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// @@ -30,203 +29,201 @@ 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)); + + // Initialize sectionName. + StringRef sectionName; + if (Expected<StringRef> e = file->getCOFFObj()->getSectionName(header)) + sectionName = *e; + sectionNameData = sectionName.data(); + sectionNameSize = sectionName.size(); - Alignment = Header->getAlignment(); + 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(); -} - -// 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)); + live = !config->doGC || !isCOMDAT(); } -// 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); -} +// 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"); -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); -} - -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; + 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; 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. @@ -237,171 +234,178 @@ void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit) { // 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); -} - -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; + 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; + 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<std::string> 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<uint8_t> A = getContents(); - if (!A.empty()) - memcpy(Buf + OutputSectionOff, A.data(), A.size()); + ArrayRef<uint8_t> 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<Defined>(RelocTargets[I]); + auto *sym = + dyn_cast_or_null<Defined>(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<DefinedAbsolute>(Sym) && !isa<DefinedSynthetic>(Sym))) { - maybeReportRelocationToDiscarded(this, Sym, Rel); + if (!sym || + (!os && !isa<DefinedAbsolute>(sym) && !isa<DefinedSynthetic>(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"); @@ -409,28 +413,32 @@ void SectionChunk::writeTo(uint8_t *Buf) const { } } -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: @@ -442,18 +450,16 @@ static uint8_t getBaserelType(const coff_relocation &Rel) { // 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<Baserel> *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<Baserel> *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<DefinedAbsolute>(Target)) + Symbol *target = file->getSymbol(rel.SymbolTableIndex); + if (!target || isa<DefinedAbsolute>(target)) continue; - Res->emplace_back(RVA + Rel.VirtualAddress, Ty); + res->emplace_back(rva + rel.VirtualAddress, ty); } } @@ -464,7 +470,7 @@ void SectionChunk::getBaserels(std::vector<Baserel> *Res) { // 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. @@ -490,9 +496,9 @@ static int getRuntimePseudoRelocSize(uint16_t Type) { // 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: @@ -507,7 +513,7 @@ static int getRuntimePseudoRelocSize(uint16_t Type) { return 0; } case I386: - switch (Type) { + switch (type) { case IMAGE_REL_I386_DIR32: case IMAGE_REL_I386_REL32: return 32; @@ -515,14 +521,14 @@ static int getRuntimePseudoRelocSize(uint16_t Type) { 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: @@ -541,75 +547,106 @@ static int getRuntimePseudoRelocSize(uint16_t Type) { // to a module local variable, which turned out to actually need to be // imported from another DLL). void SectionChunk::getRuntimePseudoRelocs( - std::vector<RuntimePseudoReloc> &Res) { - for (const coff_relocation &Rel : Relocs) { - auto *Target = - dyn_cast_or_null<Defined>(File->getSymbol(Rel.SymbolTableIndex)); - if (!Target || !Target->IsRuntimePseudoReloc) + std::vector<RuntimePseudoReloc> &res) { + for (const coff_relocation &rel : getRelocs()) { + auto *target = + dyn_cast_or_null<Defined>(file->getSymbol(rel.SymbolTableIndex)); + if (!target || !target->isRuntimePseudoReloc) continue; - int SizeInBits = getRuntimePseudoRelocSize(Rel.Type); - if (SizeInBits == 0) { - error("unable to automatically import from " + Target->getName() + + 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<uint8_t> SectionChunk::getContents() const { - ArrayRef<uint8_t> A; - File->getCOFFObj()->getSectionContents(Header, A); - return A; + ArrayRef<uint8_t> 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<uint8_t> SectionChunk::consumeDebugMagic() { + assert(isCodeView()); + return consumeDebugMagic(getContents(), getSectionName()); +} + +ArrayRef<uint8_t> SectionChunk::consumeDebugMagic(ArrayRef<uint8_t> 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<SectionChunk *> 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<uintptr_t>(Header); - SectionRef S(R, File->getCOFFObj()); - return S.getIndex() + 1; + DataRefImpl r; + r.p = reinterpret_cast<uintptr_t>(header); + SectionRef s(r, file->getCOFFObj()); + return s.getIndex() + 1; } -CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) { - // Common symbols are aligned on natural boundaries up to 32 bytes. +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 { @@ -617,119 +654,139 @@ uint32_t CommonChunk::getOutputCharacteristics() const { 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<Baserel> *Res) { - Res->emplace_back(getRVA() + 2); +void ImportThunkChunkX86::getBaserels(std::vector<Baserel> *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<Baserel> *Res) { - Res->emplace_back(getRVA(), IMAGE_REL_BASED_ARM_MOV32T); +void ImportThunkChunkARM::getBaserels(std::vector<Baserel> *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 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)); +} + +// 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); } -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 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::getBaserels(std::vector<Baserel> *Res) { - Res->emplace_back(getRVA()); +void LocalImportChunk::getBaserels(std::vector<Baserel> *res) { + res->emplace_back(getRVA()); } -size_t LocalImportChunk::getSize() const { return Config->Wordsize; } +size_t LocalImportChunk::getSize() const { return config->wordsize; } -void LocalImportChunk::writeTo(uint8_t *Buf) const { - if (Config->is64()) { - write64le(Buf + OutputSectionOff, Sym->getRVA() + Config->ImageBase); +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<ulittle32_t *>(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<ulittle32_t *>(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<ulittle32_t *>(Buf + OutputSectionOff); + ulittle32_t *table = reinterpret_cast<ulittle32_t *>(buf); // This is the list header, to signal the runtime pseudo relocation v2 // format. - Table[0] = 0; - Table[1] = 0; - Table[2] = 1; - - size_t Idx = 3; - for (const RuntimePseudoReloc &RPR : Relocs) { - Table[Idx + 0] = RPR.Sym->getRVA(); - Table[Idx + 1] = RPR.Target->getRVA() + RPR.TargetOffset; - Table[Idx + 2] = RPR.Flags; - Idx += 3; + 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; } } @@ -772,26 +829,26 @@ void PseudoRelocTableChunk::writeTo(uint8_t *Buf) const { // // 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; @@ -803,36 +860,38 @@ uint8_t Baserel::getDefaultType() { } } -std::map<uint32_t, MergeChunk *> 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<MergeChunk>(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<MergeChunk>(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); } } @@ -841,21 +900,21 @@ uint32_t MergeChunk::getOutputCharacteristics() const { } 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); } } diff --git a/COFF/Chunks.h b/COFF/Chunks.h index f8a0ddd8ef3b..6bb629fe998b 100644 --- a/COFF/Chunks.h +++ b/COFF/Chunks.h @@ -1,9 +1,8 @@ //===- Chunks.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 // //===----------------------------------------------------------------------===// @@ -14,6 +13,7 @@ #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" @@ -40,10 +40,13 @@ 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 @@ -51,81 +54,126 @@ const uint32_t TypeMask = 0x000000E0; // 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; + + // 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 by the writer once before assigning addresses and writing - // the output. - virtual void readRelocTargets() {} + // Returns readable/writable/executable bits. + uint32_t getOutputCharacteristics() const; - // Called if restarting thunk addition. - virtual void resetRelocTargets() {} + // Returns the section name if this is a section chunk. + // It is illegal to call this function on non-section chunks. + StringRef getSectionName() const; - // Called by the writer after an RVA is assigned, but before calling - // getSize(). - virtual void finalizeContents() {} + // 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; - // The writer sets and uses the addresses. - uint64_t getRVA() const { return RVA; } - void setRVA(uint64_t V) { RVA = V; } + // Windows-specific. + // Collect all locations that contain absolute addresses for base relocations. + void getBaserels(std::vector<Baserel> *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; + +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; - // Returns readable/writable/executable bits. 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<Baserel> *Res) {} + virtual void getBaserels(std::vector<Baserel> *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. @@ -139,39 +187,41 @@ public: 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<uint8_t> 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<Baserel> *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<Baserel> *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<RuntimePseudoReloc> &Res); + void getRuntimePseudoRelocs(std::vector<RuntimePseudoReloc> &res); // Called if the garbage collector decides to not include this chunk // in a final output. It's supposed to print out a log message to stdout. @@ -179,69 +229,169 @@ public: // 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<symbol_iterator> 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<coff_relocation> getRelocs() const { + return llvm::makeArrayRef(relocsData, relocsSize); } + // Reloc setter used by ARM range extension thunk insertion. + void setRelocs(ArrayRef<coff_relocation> 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<SectionChunk *> children() const { return AssocChildren; } + llvm::iterator_range<AssociatedIterator> 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<uint8_t> 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<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> data, + StringRef sectionName); - const coff_section *Header; + static SectionChunk *findByName(ArrayRef<SectionChunk *> 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<coff_relocation> 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; + + // 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; - // When inserting a thunk, we need to adjust a relocation to point to - // the thunk instead of the actual original target Symbol. - std::vector<Symbol *> RelocTargets; + // 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<SectionChunk *> 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<SectionChunk>(this)) + return static_cast<const SectionChunk *>(this)->getSize(); + else + return static_cast<const NonSectionChunk *>(this)->getSize(); +} + +inline uint32_t Chunk::getOutputCharacteristics() const { + if (isa<SectionChunk>(this)) + return static_cast<const SectionChunk *>(this)->getOutputCharacteristics(); + else + return static_cast<const NonSectionChunk *>(this) + ->getOutputCharacteristics(); +} + +inline void Chunk::writeTo(uint8_t *buf) const { + if (isa<SectionChunk>(this)) + static_cast<const SectionChunk *>(this)->writeTo(buf); + else + static_cast<const NonSectionChunk *>(this)->writeTo(buf); +} + +inline StringRef Chunk::getSectionName() const { + if (isa<SectionChunk>(this)) + return static_cast<const SectionChunk *>(this)->getSectionName(); + else + return static_cast<const NonSectionChunk *>(this)->getSectionName(); +} + +inline void Chunk::getBaserels(std::vector<Baserel> *res) { + if (isa<SectionChunk>(this)) + static_cast<SectionChunk *>(this)->getBaserels(res); + else + static_cast<NonSectionChunk *>(this)->getBaserels(res); +} + +inline StringRef Chunk::getDebugName() const { + if (isa<SectionChunk>(this)) + return static_cast<const SectionChunk *>(this)->getDebugName(); + else + return static_cast<const NonSectionChunk *>(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. @@ -251,60 +401,60 @@ private: // 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<uint32_t, MergeChunk *> Instances; - std::vector<SectionChunk *> Sections; + static MergeChunk *instances[Log2MaxSectionAlignment + 1]; + std::vector<SectionChunk *> 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 @@ -313,78 +463,85 @@ static const uint8_t ImportThunkARM64[] = { // 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<Baserel> *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<Baserel> *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<Baserel> *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<Baserel> *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<Baserel> *Res) override; - void writeTo(uint8_t *Buf) const override; + void getBaserels(std::vector<Baserel> *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() { @@ -393,12 +550,12 @@ struct ChunkAndOffset { static ChunkAndOffset getTombstoneKey() { return {llvm::DenseMapInfo<Chunk *>::getTombstoneKey(), 0}; } - static unsigned getHashValue(const ChunkAndOffset &CO) { + static unsigned getHashValue(const ChunkAndOffset &co) { return llvm::DenseMapInfo<std::pair<Chunk *, uint32_t>>::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; } }; }; @@ -406,48 +563,48 @@ struct ChunkAndOffset { using SymbolRVASet = llvm::DenseSet<ChunkAndOffset>; // 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<uint8_t> Data; + std::vector<uint8_t> 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. @@ -456,17 +613,17 @@ public: // 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<RuntimePseudoReloc> &Relocs) - : Relocs(std::move(Relocs)) { - Alignment = 4; + PseudoRelocTableChunk(std::vector<RuntimePseudoReloc> &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<RuntimePseudoReloc> Relocs; + std::vector<RuntimePseudoReloc> relocs; }; // MinGW specific; information about one individual location in the image @@ -474,37 +631,48 @@ private: // 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<SectionChunk>(this)) + return sc->file->hotPatchable; + else if (isa<ImportThunkChunk>(this)) + return true; + return false; +} + +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); +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 diff --git a/COFF/Config.h b/COFF/Config.h index 8915b6a3bdd8..1b0e24042710 100644 --- a/COFF/Config.h +++ b/COFF/Config.h @@ -1,9 +1,8 @@ //===- Config.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 // //===----------------------------------------------------------------------===// @@ -29,6 +28,7 @@ class DefinedAbsolute; class DefinedRelative; class StringChunk; class Symbol; +class InputFile; // Short aliases. static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64; @@ -38,30 +38,30 @@ 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 operator==(const Export &E) { - return (Name == E.Name && ExtName == E.ExtName && - Ordinal == E.Ordinal && Noname == E.Noname && - Data == E.Data && Private == E.Private); + 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 && isPrivate == e.isPrivate); } }; @@ -81,130 +81,149 @@ enum class GuardCFLevel { // Global configuration. struct Configuration { enum ManifestKind { SideBySide, Embed, No }; - 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<unsigned>(DebugType::None); - std::vector<std::string> NatvisFiles; - llvm::SmallString<128> PDBAltPath; - llvm::SmallString<128> PDBPath; - llvm::SmallString<128> PDBSourcePath; - std::vector<llvm::StringRef> Argv; + 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 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<unsigned>(DebugType::None); + std::vector<std::string> natvisFiles; + llvm::SmallString<128> pdbAltPath; + llvm::SmallString<128> pdbPath; + llvm::SmallString<128> pdbSourcePath; + std::vector<llvm::StringRef> argv; // Symbols in this set are considered as live by the garbage collector. - std::vector<Symbol *> GCRoot; + std::vector<Symbol *> gcroot; - std::set<StringRef> NoDefaultLibs; - bool NoDefaultLibAll = false; + std::set<std::string> noDefaultLibs; + bool noDefaultLibAll = false; // True if we are creating a DLL. - bool DLL = false; - StringRef Implib; - std::vector<Export> Exports; - std::set<std::string> DelayLoads; - std::map<std::string, int> DLLOrder; - Symbol *DelayLoadHelper = nullptr; + bool dll = false; + StringRef implib; + std::vector<Export> exports; + std::set<std::string> delayLoads; + std::map<std::string, int> 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<StringRef, StringRef> Merge; + std::map<StringRef, StringRef> merge; // Used for /section=.name,{DEKPRSW} to set section attributes. - std::map<StringRef, uint32_t> Section; + std::map<StringRef, uint32_t> section; // Options for manifest files. - ManifestKind Manifest = No; - int ManifestID = 1; - StringRef ManifestDependency; - bool ManifestUAC = true; - std::vector<std::string> ManifestInput; - StringRef ManifestLevel = "'asInvoker'"; - StringRef ManifestUIAccess = "'false'"; - StringRef ManifestFile; + ManifestKind manifest = No; + int manifestID = 1; + StringRef manifestDependency; + bool manifestUAC = true; + std::vector<std::string> manifestInput; + StringRef manifestLevel = "'asInvoker'"; + StringRef manifestUIAccess = "'false'"; + StringRef manifestFile; // Used for /aligncomm. - std::map<std::string, int> AlignComm; + std::map<std::string, int> alignComm; // Used for /failifmismatch. - std::map<StringRef, StringRef> MustMatch; + std::map<StringRef, std::pair<StringRef, InputFile *>> mustMatch; // Used for /alternatename. - std::map<StringRef, StringRef> AlternateNames; + std::map<StringRef, StringRef> alternateNames; // Used for /order. - llvm::StringMap<int> Order; + llvm::StringMap<int> order; // Used for /lldmap. - 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; + std::string mapFile; + + // Used for /thinlto-index-only: + llvm::StringRef thinLTOIndexOnlyArg; + + // Used for /thinlto-object-prefix-replace: + std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace; + + // Used for /thinlto-object-suffix-replace: + std::pair<llvm::StringRef, llvm::StringRef> 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 diff --git a/COFF/DLL.cpp b/COFF/DLL.cpp index 599cc5892a16..40d1f463aa3f 100644 --- a/COFF/DLL.cpp +++ b/COFF/DLL.cpp @@ -1,9 +1,8 @@ //===- DLL.cpp ------------------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// // @@ -36,149 +35,167 @@ 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<std::vector<DefinedImportData *>> -binImports(const std::vector<DefinedImportData *> &Imports) { +binImports(const std::vector<DefinedImportData *> &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<std::string, std::vector<DefinedImportData *>, - 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<std::vector<DefinedImportData *>> V; - for (auto &KV : M) { + std::vector<std::vector<DefinedImportData *>> v; + for (auto &kv : m) { // Sort symbols by name for each group. - std::vector<DefinedImportData *> &Syms = KV.second; - std::sort(Syms.begin(), Syms.end(), - [](DefinedImportData *A, DefinedImportData *B) { - return A->getName() < B->getName(); + std::vector<DefinedImportData *> &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_<FUNCNAME>] + 0xE9, 0, 0, 0, 0, // jmp __tailMerge_<lib> +}; + +static const uint8_t tailMergeX64[] = { 0x51, // push rcx 0x52, // push rdx 0x41, 0x50, // push r8 @@ -188,7 +205,7 @@ static const uint8_t ThunkX64[] = { 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_<FUNCNAME>] + 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] @@ -203,10 +220,15 @@ static const uint8_t ThunkX64[] = { 0xFF, 0xE0, // jmp rax }; -static const uint8_t ThunkX86[] = { +static const uint8_t thunkX86[] = { + 0xB8, 0, 0, 0, 0, // mov eax, offset ___imp__<FUNCNAME> + 0xE9, 0, 0, 0, 0, // jmp __tailMerge_<lib> +}; + +static const uint8_t tailMergeX86[] = { 0x51, // push ecx 0x52, // push edx - 0x68, 0, 0, 0, 0, // push offset ___imp__<FUNCNAME> + 0x50, // push eax 0x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR_<DLLNAME>_dll 0xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@8 0x5A, // pop edx @@ -214,9 +236,13 @@ static const uint8_t ThunkX86[] = { 0xFF, 0xE0, // jmp eax }; -static const uint8_t ThunkARM[] = { +static const uint8_t thunkARM[] = { 0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 __imp_<FUNCNAME> 0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 __imp_<FUNCNAME> + 0x00, 0xf0, 0x00, 0xb8, // b.w __tailMerge_<lib> +}; + +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} @@ -230,9 +256,13 @@ static const uint8_t ThunkARM[] = { 0x60, 0x47, // bx ip }; -static const uint8_t ThunkARM64[] = { +static const uint8_t thunkARM64[] = { 0x11, 0x00, 0x00, 0x90, // adrp x17, #0 __imp_<FUNCNAME> 0x31, 0x02, 0x00, 0x91, // add x17, x17, #0 :lo12:__imp_<FUNCNAME> + 0x00, 0x00, 0x00, 0x14, // b __tailMerge_<lib> +}; + +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] @@ -261,370 +291,445 @@ static const uint8_t ThunkARM64[] = { }; // A chunk for the delay import thunk. -class ThunkChunkX64 : public Chunk { +class ThunkChunkX64 : public NonSectionChunk { +public: + ThunkChunkX64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} + + size_t getSize() const override { return sizeof(thunkX64); } + + 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 *tailMerge = nullptr; +}; + +class TailMergeChunkX64 : public NonSectionChunk { +public: + TailMergeChunkX64(Chunk *d, Defined *h) : desc(d), helper(h) {} + + size_t getSize() const override { return sizeof(tailMergeX64); } + + 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); + } + + 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); + } + + void getBaserels(std::vector<Baserel> *res) override { + res->emplace_back(rva + 1); + } + + Defined *imp = nullptr; + Chunk *tailMerge = nullptr; +}; + +class TailMergeChunkX86 : public NonSectionChunk { public: - ThunkChunkX64(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(ThunkX64); } + size_t getSize() const override { return sizeof(tailMergeX86); } - 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, tailMergeX86, sizeof(tailMergeX86)); + write32le(buf + 4, desc->getRVA() + config->imageBase); + write32le(buf + 9, helper->getRVA() - rva - 13); } - Defined *Imp = nullptr; - Chunk *Desc = nullptr; - Defined *Helper = nullptr; + void getBaserels(std::vector<Baserel> *res) override { + res->emplace_back(rva + 4); + } + + Chunk *desc = nullptr; + Defined *helper = nullptr; }; -class ThunkChunkX86 : public Chunk { +class ThunkChunkARM : public NonSectionChunk { public: - ThunkChunkX86(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(ThunkX86); } + size_t getSize() const override { return sizeof(thunkARM); } - 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, thunkARM, sizeof(thunkARM)); + applyMOV32T(buf + 0, imp->getRVA() + config->imageBase); + applyBranch24T(buf + 8, tailMerge->getRVA() - rva - 12); } - void getBaserels(std::vector<Baserel> *Res) override { - Res->emplace_back(RVA + 3); - Res->emplace_back(RVA + 8); + void getBaserels(std::vector<Baserel> *res) override { + res->emplace_back(rva + 0, IMAGE_REL_BASED_ARM_MOV32T); } - Defined *Imp = nullptr; - Chunk *Desc = nullptr; - Defined *Helper = nullptr; + Defined *imp = nullptr; + Chunk *tailMerge = nullptr; }; -class ThunkChunkARM : public Chunk { +class TailMergeChunkARM : public NonSectionChunk { public: - ThunkChunkARM(Defined *I, Chunk *D, Defined *H) - : Imp(I), Desc(D), Helper(H) {} + TailMergeChunkARM(Chunk *d, Defined *h) : desc(d), helper(h) {} - size_t getSize() const override { return sizeof(ThunkARM); } + size_t getSize() const override { return sizeof(tailMergeARM); } - 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, tailMergeARM, sizeof(tailMergeARM)); + applyMOV32T(buf + 14, desc->getRVA() + config->imageBase); + applyBranch24T(buf + 22, helper->getRVA() - rva - 26); } - void getBaserels(std::vector<Baserel> *Res) override { - Res->emplace_back(RVA + 0, IMAGE_REL_BASED_ARM_MOV32T); - Res->emplace_back(RVA + 22, IMAGE_REL_BASED_ARM_MOV32T); + void getBaserels(std::vector<Baserel> *res) override { + res->emplace_back(rva + 14, IMAGE_REL_BASED_ARM_MOV32T); } - Defined *Imp = nullptr; - Chunk *Desc = nullptr; - Defined *Helper = nullptr; + Chunk *desc = nullptr; + Defined *helper = nullptr; }; -class ThunkChunkARM64 : public Chunk { +class ThunkChunkARM64 : public NonSectionChunk { public: - ThunkChunkARM64(Defined *I, Chunk *D, Defined *H) - : Imp(I), Desc(D), Helper(H) {} + ThunkChunkARM64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {} - size_t getSize() const override { return sizeof(ThunkARM64); } + size_t getSize() const override { return sizeof(thunkARM64); } - void writeTo(uint8_t *Buf) const override { - memcpy(Buf + OutputSectionOff, ThunkARM64, sizeof(ThunkARM64)); - applyArm64Addr(Buf + OutputSectionOff + 0, Imp->getRVA(), RVA + 0, 12); - applyArm64Imm(Buf + OutputSectionOff + 4, Imp->getRVA() & 0xfff, 0); - applyArm64Addr(Buf + OutputSectionOff + 52, Desc->getRVA(), RVA + 52, 12); - applyArm64Imm(Buf + OutputSectionOff + 56, Desc->getRVA() & 0xfff, 0); - applyArm64Branch26(Buf + OutputSectionOff + 60, - Helper->getRVA() - RVA - 60); + 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 *Desc = nullptr; - Defined *Helper = nullptr; + 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<Baserel> *Res) override { - Res->emplace_back(RVA); + void getBaserels(std::vector<Baserel> *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<Defined>(E.Sym)->getRVA() | Bit); + write32le(p, cast<Defined>(e.sym)->getRVA() | bit); } } } private: - size_t Size; + size_t size; }; -class NamePointersChunk : public Chunk { +class NamePointersChunk : public NonSectionChunk { public: - explicit NamePointersChunk(std::vector<Chunk *> &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; + explicit NamePointersChunk(std::vector<Chunk *> &v) : chunks(v) {} + size_t getSize() const override { return chunks.size() * 4; } + + void writeTo(uint8_t *buf) const override { + for (Chunk *c : chunks) { + write32le(buf, c->getRVA()); + buf += 4; } } private: - std::vector<Chunk *> Chunks; + std::vector<Chunk *> 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<std::vector<DefinedImportData *>> V = binImports(Imports); + std::vector<std::vector<DefinedImportData *>> v = binImports(imports); // Create .idata contents for each DLL. - for (std::vector<DefinedImportData *> &Syms : V) { + for (std::vector<DefinedImportData *> &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<OrdinalOnlyChunk>(Ord)); - Addresses.push_back(make<OrdinalOnlyChunk>(Ord)); + size_t base = lookups.size(); + for (DefinedImportData *s : syms) { + uint16_t ord = s->getOrdinal(); + if (s->getExternalName().empty()) { + lookups.push_back(make<OrdinalOnlyChunk>(ord)); + addresses.push_back(make<OrdinalOnlyChunk>(ord)); continue; } - auto *C = make<HintNameChunk>(S->getExternalName(), Ord); - Lookups.push_back(make<LookupChunk>(C)); - Addresses.push_back(make<LookupChunk>(C)); - Hints.push_back(C); + auto *c = make<HintNameChunk>(s->getExternalName(), ord); + lookups.push_back(make<LookupChunk>(c)); + addresses.push_back(make<LookupChunk>(c)); + hints.push_back(c); } // Terminate with null values. - Lookups.push_back(make<NullChunk>(Config->Wordsize)); - Addresses.push_back(make<NullChunk>(Config->Wordsize)); + lookups.push_back(make<NullChunk>(config->wordsize)); + addresses.push_back(make<NullChunk>(config->wordsize)); - for (int I = 0, E = Syms.size(); I < E; ++I) - Syms[I]->setLocation(Addresses[Base + I]); + 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<StringChunk>(Syms[0]->getDLLName())); - auto *Dir = make<ImportDirectoryChunk>(DLLNames.back()); - Dir->LookupTab = Lookups[Base]; - Dir->AddressTab = Addresses[Base]; - Dirs.push_back(Dir); + dllNames.push_back(make<StringChunk>(syms[0]->getDLLName())); + auto *dir = make<ImportDirectoryChunk>(dllNames.back()); + dir->lookupTab = lookups[base]; + dir->addressTab = addresses[base]; + dirs.push_back(dir); } // Add null terminator. - Dirs.push_back(make<NullChunk>(sizeof(ImportDirectoryTableEntry))); + dirs.push_back(make<NullChunk>(sizeof(ImportDirectoryTableEntry))); } std::vector<Chunk *> DelayLoadContents::getChunks() { - std::vector<Chunk *> 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<Chunk *> 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<Chunk *> DelayLoadContents::getDataChunks() { - std::vector<Chunk *> V; - V.insert(V.end(), ModuleHandles.begin(), ModuleHandles.end()); - V.insert(V.end(), Addresses.begin(), Addresses.end()); - return V; + std::vector<Chunk *> 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<std::vector<DefinedImportData *>> V = binImports(Imports); +void DelayLoadContents::create(Defined *h) { + helper = h; + std::vector<std::vector<DefinedImportData *>> v = binImports(imports); // Create .didat contents for each DLL. - for (std::vector<DefinedImportData *> &Syms : V) { + for (std::vector<DefinedImportData *> &syms : v) { // Create the delay import table header. - DLLNames.push_back(make<StringChunk>(Syms[0]->getDLLName())); - auto *Dir = make<DelayDirectoryChunk>(DLLNames.back()); - - size_t Base = Addresses.size(); - for (DefinedImportData *S : Syms) { - Chunk *T = newThunkChunk(S, Dir); - auto *A = make<DelayAddressChunk>(T); - Addresses.push_back(A); - Thunks.push_back(T); - StringRef ExtName = S->getExternalName(); - if (ExtName.empty()) { - Names.push_back(make<OrdinalOnlyChunk>(S->getOrdinal())); + dllNames.push_back(make<StringChunk>(syms[0]->getDLLName())); + auto *dir = make<DelayDirectoryChunk>(dllNames.back()); + + size_t base = addresses.size(); + Chunk *tm = newTailMergeChunk(dir); + for (DefinedImportData *s : syms) { + Chunk *t = newThunkChunk(s, tm); + auto *a = make<DelayAddressChunk>(t); + addresses.push_back(a); + thunks.push_back(t); + StringRef extName = s->getExternalName(); + if (extName.empty()) { + names.push_back(make<OrdinalOnlyChunk>(s->getOrdinal())); } else { - auto *C = make<HintNameChunk>(ExtName, 0); - Names.push_back(make<LookupChunk>(C)); - HintNames.push_back(C); + auto *c = make<HintNameChunk>(extName, 0); + names.push_back(make<LookupChunk>(c)); + hintNames.push_back(c); } } + thunks.push_back(tm); // Terminate with null values. - Addresses.push_back(make<NullChunk>(8)); - Names.push_back(make<NullChunk>(8)); + addresses.push_back(make<NullChunk>(8)); + names.push_back(make<NullChunk>(8)); - for (int I = 0, E = Syms.size(); I < E; ++I) - Syms[I]->setLocation(Addresses[Base + I]); - auto *MH = make<NullChunk>(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<NullChunk>(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<NullChunk>(sizeof(delay_import_directory_table_entry))); + dirs.push_back(make<NullChunk>(sizeof(delay_import_directory_table_entry))); +} + +Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) { + switch (config->machine) { + case AMD64: + return make<TailMergeChunkX64>(dir, helper); + case I386: + return make<TailMergeChunkX86>(dir, helper); + case ARMNT: + return make<TailMergeChunkARM>(dir, helper); + case ARM64: + return make<TailMergeChunkARM64>(dir, helper); + default: + llvm_unreachable("unsupported machine type"); + } } -Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *S, Chunk *Dir) { - switch (Config->Machine) { +Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s, + Chunk *tailMerge) { + switch (config->machine) { case AMD64: - return make<ThunkChunkX64>(S, Dir, Helper); + return make<ThunkChunkX64>(s, tailMerge); case I386: - return make<ThunkChunkX86>(S, Dir, Helper); + return make<ThunkChunkX86>(s, tailMerge); case ARMNT: - return make<ThunkChunkARM>(S, Dir, Helper); + return make<ThunkChunkARM>(s, tailMerge); case ARM64: - return make<ThunkChunkARM64>(S, Dir, Helper); + return make<ThunkChunkARM64>(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); - - auto *DLLName = make<StringChunk>(sys::path::filename(Config->OutputFile)); - auto *AddressTab = make<AddressTableChunk>(MaxOrdinal); - std::vector<Chunk *> Names; - for (Export &E : Config->Exports) - if (!E.Noname) - Names.push_back(make<StringChunk>(E.ExportName)); - - std::vector<Chunk *> Forwards; - for (Export &E : Config->Exports) { - if (E.ForwardTo.empty()) + uint16_t maxOrdinal = 0; + for (Export &e : config->exports) + maxOrdinal = std::max(maxOrdinal, e.ordinal); + + auto *dllName = make<StringChunk>(sys::path::filename(config->outputFile)); + auto *addressTab = make<AddressTableChunk>(maxOrdinal); + std::vector<Chunk *> names; + for (Export &e : config->exports) + if (!e.noname) + names.push_back(make<StringChunk>(e.exportName)); + + std::vector<Chunk *> forwards; + for (Export &e : config->exports) { + if (e.forwardTo.empty()) continue; - E.ForwardChunk = make<StringChunk>(E.ForwardTo); - Forwards.push_back(E.ForwardChunk); - } - - auto *NameTab = make<NamePointersChunk>(Names); - auto *OrdinalTab = make<ExportOrdinalChunk>(Names.size()); - auto *Dir = make<ExportDirectoryChunk>(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()); + e.forwardChunk = make<StringChunk>(e.forwardTo); + forwards.push_back(e.forwardChunk); + } + + auto *nameTab = make<NamePointersChunk>(names); + auto *ordinalTab = make<ExportOrdinalChunk>(names.size()); + auto *dir = make<ExportDirectoryChunk>(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 diff --git a/COFF/DLL.h b/COFF/DLL.h index a298271e2c0d..ce0ee01c4a3d 100644 --- a/COFF/DLL.h +++ b/COFF/DLL.h @@ -1,9 +1,8 @@ //===- DLL.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 // //===----------------------------------------------------------------------===// @@ -22,45 +21,46 @@ namespace coff { // 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<DefinedImportData *> Imports; - std::vector<Chunk *> Dirs; - std::vector<Chunk *> Lookups; - std::vector<Chunk *> Addresses; - std::vector<Chunk *> Hints; - std::vector<Chunk *> DLLNames; + std::vector<DefinedImportData *> imports; + std::vector<Chunk *> dirs; + std::vector<Chunk *> lookups; + std::vector<Chunk *> addresses; + std::vector<Chunk *> hints; + std::vector<Chunk *> 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<Chunk *> getChunks(); std::vector<Chunk *> getDataChunks(); - ArrayRef<Chunk *> getCodeChunks() { return Thunks; } + ArrayRef<Chunk *> 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<DefinedImportData *> Imports; - std::vector<Chunk *> Dirs; - std::vector<Chunk *> ModuleHandles; - std::vector<Chunk *> Addresses; - std::vector<Chunk *> Names; - std::vector<Chunk *> HintNames; - std::vector<Chunk *> Thunks; - std::vector<Chunk *> DLLNames; + Defined *helper; + std::vector<DefinedImportData *> imports; + std::vector<Chunk *> dirs; + std::vector<Chunk *> moduleHandles; + std::vector<Chunk *> addresses; + std::vector<Chunk *> names; + std::vector<Chunk *> hintNames; + std::vector<Chunk *> thunks; + std::vector<Chunk *> dllNames; }; // Windows-specific. @@ -68,11 +68,11 @@ private: class EdataContents { public: EdataContents(); - std::vector<Chunk *> Chunks; + std::vector<Chunk *> 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(); } }; diff --git a/COFF/DebugTypes.cpp b/COFF/DebugTypes.cpp new file mode 100644 index 000000000000..78c1c78b408d --- /dev/null +++ b/COFF/DebugTypes.cpp @@ -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<TypeServerSource *> getInstance(MemoryBufferRef m); + + // Fetch the PDB instance loaded for a corresponding dependent OBJ. + static Expected<TypeServerSource *> + findFromFile(const ObjFile *dependentFile); + + static std::map<std::string, std::pair<std::string, TypeServerSource *>> + instances; + + // The interface to the PDB (if it was opened successfully) + std::unique_ptr<llvm::pdb::NativeSession> 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<std::unique_ptr<TpiSource>> GC; + +TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) { + GC.push_back(std::unique_ptr<TpiSource>(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<std::string, std::pair<std::string, TypeServerSource *>> + 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<std::string> 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 *> +TypeServerSource::findFromFile(const ObjFile *dependentFile) { + const TypeServer2Record &ts = + retrieveDependencyInfo<TypeServer2Record>(dependentFile->debugTypesObj); + + Optional<std::string> 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<std::string, TypeServerSource *> &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::PDBError>(pdb::pdb_error_code::signature_out_of_date)); + + return pdb.second; +} + +// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is +// moved here. +Expected<llvm::pdb::NativeSession *> +lld::coff::findTypeServerSource(const ObjFile *f) { + Expected<TypeServerSource *> 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<std::string> p = findPdbPath(ts.Name, dependentFile); + if (!p) + return; + auto it = TypeServerSource::instances.emplace( + *p, std::pair<std::string, TypeServerSource *>{}); + 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<TypeServerSource *> ts = TypeServerSource::getInstance(m); + if (!ts) + TypeServerSource::instances[path] = {toString(ts.takeError()), nullptr}; + else + TypeServerSource::instances[path] = {{}, *ts}; +} + +Expected<TypeServerSource *> TypeServerSource::getInstance(MemoryBufferRef m) { + std::unique_ptr<llvm::pdb::IPDBSession> iSession; + Error err = pdb::NativeSession::createFromPdb( + MemoryBuffer::getMemBuffer(m, false), iSession); + if (err) + return std::move(err); + + std::unique_ptr<llvm::pdb::NativeSession> session( + static_cast<pdb::NativeSession *>(iSession.release())); + + pdb::PDBFile &pdbFile = session->getPDBFile(); + Expected<pdb::InfoStream &> info = pdbFile.getPDBInfoStream(); + // All PDB Files should have an Info stream. + if (!info) + return info.takeError(); + return new TypeServerSource(m, session.release()); +} diff --git a/COFF/DebugTypes.h b/COFF/DebugTypes.h new file mode 100644 index 000000000000..e37c727232d0 --- /dev/null +++ b/COFF/DebugTypes.h @@ -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 <typename T> const T &retrieveDependencyInfo(const TpiSource *source); + +// Temporary interface until we move PDBLinker::maybeMergeTypeServerPDB here +llvm::Expected<llvm::pdb::NativeSession *> +findTypeServerSource(const ObjFile *f); + +} // namespace coff +} // namespace lld + +#endif
\ No newline at end of file diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp index 2e4b1e6d3147..d7af50b9318f 100644 --- a/COFF/Driver.cpp +++ b/COFF/Driver.cpp @@ -1,14 +1,14 @@ //===- Driver.cpp ---------------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// #include "Driver.h" #include "Config.h" +#include "DebugTypes.h" #include "ICF.h" #include "InputFiles.h" #include "MarkLive.h" @@ -19,7 +19,9 @@ #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" @@ -28,6 +30,7 @@ #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" @@ -51,43 +54,68 @@ 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<const char *> 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<const char *> 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<Configuration>(); + errorHandler().exitEarly = canExitEarly; + config = make<Configuration>(); - Symtab = make<SymbolTable>(); + symtab = make<SymbolTable>(); - Driver = make<LinkerDriver>(); - Driver->link(Args); + driver = make<LinkerDriver>(); + 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<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args, + unsigned id) { + auto *arg = args.getLastArg(id); + if (!arg) + return {"", ""}; + + StringRef s = arg->getValue(); + std::pair<StringRef, StringRef> ret = s.split(';'); + if (ret.second.empty()) + error(arg->getSpelling() + " expects 'old;new' format, but got " + s); + return ret; +} + // 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 @@ -95,347 +123,401 @@ static std::string getOutputPath(StringRef Path) { // 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::unique_ptr<MemoryBuffer>, std::error_code> MBErrPair; +using MBErrPair = std::pair<std::unique_ptr<MemoryBuffer>, std::error_code>; // Create a std::future that opens and maps a file using the best strategy for // the host platform. -static std::future<MBErrPair> createFutureForFile(std::string Path) { +static std::future<MBErrPair> 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<Undefined>(Symtab->find(Entry)); +static bool findUnderscoreMangle(StringRef sym) { + Symbol *s = symtab->findMangle(mangle(sym)); + return s && !isa<Undefined>(s); } -MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> MB) { - MemoryBufferRef MBRef = *MB; - make<std::unique_ptr<MemoryBuffer>>(std::move(MB)); // take ownership +MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) { + MemoryBufferRef mbref = *mb; + make<std::unique_ptr<MemoryBuffer>>(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<MemoryBuffer> MB, - bool WholeArchive) { - StringRef Filename = MB->getBufferIdentifier(); +void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> 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<Archive> File = - CHECK(Archive::create(MBRef), Filename + ": failed to parse archive"); + if (wholeArchive) { + std::unique_ptr<Archive> file = + CHECK(Archive::create(mbref), filename + ": failed to parse archive"); - for (MemoryBufferRef M : getArchiveMembers(File.get())) - addArchiveBuffer(M, "<whole-archive>", Filename); + for (MemoryBufferRef m : getArchiveMembers(file.get())) + addArchiveBuffer(m, "<whole-archive>", filename, 0); return; } - Symtab->addFile(make<ArchiveFile>(MBRef)); + symtab->addFile(make<ArchiveFile>(mbref)); break; case file_magic::bitcode: - Symtab->addFile(make<BitcodeFile>(MBRef)); + symtab->addFile(make<BitcodeFile>(mbref, "", 0)); break; case file_magic::coff_object: case file_magic::coff_import_library: - Symtab->addFile(make<ObjFile>(MBRef)); + symtab->addFile(make<ObjFile>(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<std::future<MBErrPair>>(createFutureForFile(Path)); - std::string PathStr = Path; +void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) { + auto future = + std::make_shared<std::future<MBErrPair>>(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<ImportFile>(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<ImportFile>(mb); + imp->parentName = parentName; + symtab->addFile(imp); return; } - InputFile *Obj; - if (Magic == file_magic::coff_object) { - Obj = make<ObjFile>(MB); - } else if (Magic == file_magic::bitcode) { - Obj = make<BitcodeFile>(MB); + InputFile *obj; + if (magic == file_magic::coff_object) { + obj = make<ObjFile>(mb); + } else if (magic == file_magic::bitcode) { + obj = make<BitcodeFile>(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<MemoryBufferRef> 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<std::future<MBErrPair>>(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<std::future<MBErrPair>>( + 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<StringRef> Exports; - std::tie(Args, Exports) = Parser.parseDirectives(S); + opt::InputArgList args; + std::vector<StringRef> 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<StringRef> Path = findLib(Arg->getValue())) - enqueuePath(*Path, false); + if (Optional<StringRef> 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<sys::fs::UniqueID> getUniqueID(StringRef Path) { - sys::fs::UniqueID Ret; - if (sys::fs::getUniqueID(Path, Ret)) +static Optional<sys::fs::UniqueID> 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<StringRef> LinkerDriver::findFile(StringRef Filename) { - StringRef Path = doFindFile(Filename); +Optional<StringRef> LinkerDriver::findFile(StringRef filename) { + StringRef path = doFindFile(filename); - if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path)) { - bool Seen = !VisitedFiles.insert(*ID).second; - if (Seen) + if (Optional<sys::fs::UniqueID> id = getUniqueID(path)) { + bool seen = !visitedFiles.insert(*id).second; + if (seen) return None; } - if (Path.endswith_lower(".lib")) - VisitedLibs.insert(sys::path::filename(Path)); - return Path; + 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; - - SmallString<128> S = Filename; - sys::path::replace_extension(S, ".a"); - StringRef LibName = Saver.save("lib" + S.str()); - return doFindFile(LibName); +StringRef LinkerDriver::doFindLibMinGW(StringRef filename) { + if (filename.contains('/') || filename.contains('\\')) + return filename; + + SmallString<128> s = filename; + sys::path::replace_extension(s, ".a"); + StringRef libName = saver.save("lib" + s.str()); + return doFindFile(libName); } // Find library file from search path. -StringRef LinkerDriver::doFindLib(StringRef Filename) { +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<StringRef> LinkerDriver::findLib(StringRef Filename) { - if (Config->NoDefaultLibAll) +Optional<StringRef> 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<sys::fs::UniqueID> ID = getUniqueID(Path)) - if (!VisitedFiles.insert(*ID).second) + if (Optional<sys::fs::UniqueID> 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<std::string> EnvOpt = Process::GetEnv("LIB"); - if (!EnvOpt.hasValue()) + Optional<std::string> 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<Undefined>(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. @@ -444,15 +526,15 @@ Symbol *LinkerDriver::addUndefined(StringRef Name) { // 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"); @@ -469,44 +551,44 @@ StringRef LinkerDriver::findDefaultEntry() { } 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<StringRef> FilePaths, - ArrayRef<StringRef> SearchPaths) { - SmallString<0> Data; - raw_svector_ostream OS(Data); +static std::string createResponseFile(const opt::InputArgList &args, + ArrayRef<StringRef> filePaths, + ArrayRef<StringRef> 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: @@ -518,32 +600,37 @@ static std::string createResponseFile(const opt::InputArgList &Args, 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<DebugKind>(A->getValue()) + DebugKind debug = StringSwitch<DebugKind>(a->getValue()) .CaseLower("none", DebugKind::None) .CaseLower("full", DebugKind::Full) .CaseLower("fastlink", DebugKind::FastLink) @@ -553,67 +640,68 @@ static DebugKind parseDebugKind(const opt::InputArgList &Args) { .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<unsigned>(DebugType::None); +static unsigned parseDebugTypes(const opt::InputArgList &args) { + unsigned debugTypes = static_cast<unsigned>(DebugType::None); - if (auto *A = Args.getLastArg(OPT_debugtype)) { - SmallVector<StringRef, 3> Types; - A->getSpelling().split(Types, ',', /*KeepEmpty=*/false); + if (auto *a = args.getLastArg(OPT_debugtype)) { + SmallVector<StringRef, 3> types; + StringRef(a->getValue()) + .split(types, ',', /*MaxSplit=*/-1, /*KeepEmpty=*/false); - for (StringRef Type : Types) { - unsigned V = StringSwitch<unsigned>(Type.lower()) + for (StringRef type : types) { + unsigned v = StringSwitch<unsigned>(type.lower()) .Case("cv", static_cast<unsigned>(DebugType::CV)) .Case("pdata", static_cast<unsigned>(DebugType::PData)) .Case("fixup", static_cast<unsigned>(DebugType::Fixup)) .Default(0); - if (V == 0) { - warn("/debugtype: unknown option: " + Twine(A->getValue())); + if (v == 0) { + warn("/debugtype: unknown option '" + type + "'"); continue; } - DebugTypes |= V; + debugTypes |= v; } - return DebugTypes; + return debugTypes; } // Default debug types - DebugTypes = static_cast<unsigned>(DebugType::CV); - if (Args.hasArg(OPT_driver)) - DebugTypes |= static_cast<unsigned>(DebugType::PData); - if (Args.hasArg(OPT_profile)) - DebugTypes |= static_cast<unsigned>(DebugType::Fixup); + debugTypes = static_cast<unsigned>(DebugType::CV); + if (args.hasArg(OPT_driver)) + debugTypes |= static_cast<unsigned>(DebugType::PData); + if (args.hasArg(OPT_profile)) + debugTypes |= static_cast<unsigned>(DebugType::Fixup); - return DebugTypes; + 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(); } // @@ -624,289 +712,346 @@ static std::string getImplibPath() { // 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<COFFShortExport> 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); - } - - auto HandleError = [](Error &&E) { - handleAllErrors(std::move(E), - [](ErrorInfoBase &EIB) { error(EIB.message()); }); +static void createImportLibrary(bool asLib) { + std::vector<COFFShortExport> 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()); }); }; - 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<std::unique_ptr<MemoryBuffer>> OldBuf = MemoryBuffer::getFile( - Path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false); - if (!OldBuf) { - HandleError(writeImportLibrary(LibName, Path, Exports, Config->Machine, - Config->MinGW)); + ErrorOr<std::unique_ptr<MemoryBuffer>> oldBuf = MemoryBuffer::getFile( + path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false); + if (!oldBuf) { + handleError(writeImportLibrary(libName, path, exports, config->machine, + config->mingw)); return; } - SmallString<128> TmpName; - if (std::error_code EC = - sys::fs::createUniqueFile(Path + ".tmp-%%%%%%%%.lib", TmpName)) - fatal("cannot create temporary file for import library " + Path + ": " + - EC.message()); + 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<MemoryBuffer> 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<MemoryBuffer> newBuf = check(MemoryBuffer::getFile( + tmpName, /*FileSize*/ -1, /*RequiresNullTerminator*/ false)); + if ((*oldBuf)->getBuffer() != newBuf->getBuffer()) { + oldBuf->reset(); + handleError(errorCodeToError(sys::fs::rename(tmpName, path))); } else { - sys::fs::remove(TmpName); + sys::fs::remove(tmpName); } } -static void parseModuleDefs(StringRef Path) { - std::unique_ptr<MemoryBuffer> MB = CHECK( - MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); - COFFModuleDefinition M = check(parseCOFFModuleDefinition( - MB->getMemBufferRef(), Config->Machine, 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; - - for (COFFShortExport E1 : M.Exports) { - Export E2; +static void parseModuleDefs(StringRef path) { + std::unique_ptr<MemoryBuffer> mb = CHECK( + MemoryBuffer::getFile(path, -1, false, true), "could not open " + path); + COFFModuleDefinition m = check(parseCOFFModuleDefinition( + mb->getMemBufferRef(), config->machine, 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; + + 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<void()> Task) { - TaskQueue.push_back(std::move(Task)); +void LinkerDriver::enqueueTask(std::function<void()> 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<StringRef> Set; - for (Chunk *C : Symtab->getChunks()) - if (auto *Sec = dyn_cast<SectionChunk>(C)) - if (Sec->Sym) - Set.insert(Sec->Sym->getName()); + DenseSet<StringRef> set; + for (Chunk *c : symtab->getChunks()) + if (auto *sec = dyn_cast<SectionChunk>(c)) + if (sec->sym) + set.insert(sec->sym->getName()); // Open a file. - StringRef Path = Arg.substr(1); - std::unique_ptr<MemoryBuffer> MB = CHECK( - MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); + StringRef path = arg.substr(1); + std::unique_ptr<MemoryBuffer> mb = CHECK( + MemoryBuffer::getFile(path, -1, false, true), "could not open " + path); // Parse a file. An order file contains one symbol per line. // All symbols that were not present in a given order file are // considered to have the lowest priority 0 and are placed at // end of an output section. - for (std::string S : args::getLines(MB->getMemBufferRef())) { - if (Config->Machine == I386 && !isDecorated(S)) - S = "_" + S; + 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<Defined>(S)) - if (Chunk *C = D->getChunk()) - C->KeepUnique = true; +static void markAddrsig(Symbol *s) { + if (auto *d = dyn_cast_or_null<Defined>(s)) + if (SectionChunk *c = dyn_cast_or_null<SectionChunk>(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<Symbol *> Syms = Obj->getSymbols(); - if (Obj->AddrsigSec) { - ArrayRef<uint8_t> Contents; - Obj->getCOFFObj()->getSectionContents(Obj->AddrsigSec, Contents); - const uint8_t *Cur = Contents.begin(); - while (Cur != Contents.end()) { - unsigned Size; - const char *Err; - uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err); - if (Err) - fatal(toString(Obj) + ": could not decode addrsig section: " + Err); - if (SymIndex >= Syms.size()) - fatal(toString(Obj) + ": invalid symbol index in addrsig section"); - markAddrsig(Syms[SymIndex]); - Cur += Size; + for (ObjFile *obj : ObjFile::instances) { + ArrayRef<Symbol *> syms = obj->getSymbols(); + if (obj->addrsigSec) { + ArrayRef<uint8_t> 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<const char *> ArgsArr) { - // If the first command line argument is "/lib", link.exe acts like lib.exe. - // We call our own implementation of lib.exe that understands bitcode files. - 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<StringRef> path = doFindFile(arg->getValue())) + exporter.addWholeArchive(*path); + + symtab->forEachSymbol([&](Symbol *s) { + auto *def = dyn_cast<Defined>(s); + if (!exporter.shouldExport(def)) + return; + + Export e; + e.name = def->getName(); + e.sym = def; + if (Chunk *c = def->getChunk()) + if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) + e.data = true; + config->exports.push_back(e); + }); +} + +void LinkerDriver::link(ArrayRef<const char *> argsArr) { // Needed for LTO. InitializeAllTargetInfos(); InitializeAllTargets(); @@ -914,279 +1059,308 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { 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<const char *> 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<const char *> 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); + + if (args.hasArg(OPT_show_timing)) + config->showTiming = true; - ScopedTimer T(Timer::root()); + 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<std::unique_ptr<TarWriter>> ErrOrWriter = - TarWriter::create(Path, "repro"); + Expected<std::unique_ptr<TarWriter>> 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<StringRef, 8> Vec; - StringRef(Arg->getValue()).split(Vec, ','); - for (StringRef S : Vec) { - if (S == "4037") - Config->WarnMissingOrderSymbol = false; - else if (S == "4099") - Config->WarnDebugInfoUnusable = false; - else if (S == "4217") - Config->WarnLocallyDefinedImported = false; + for (auto *arg : args.filtered(OPT_ignore)) { + SmallVector<StringRef, 8> vec; + StringRef(arg->getValue()).split(vec, ','); + for (StringRef s : vec) { + if (s == "4037") + config->warnMissingOrderSymbol = false; + else if (s == "4099") + config->warnDebugInfoUnusable = false; + else if (s == "4217") + config->warnLocallyDefinedImported = false; // Other warning numbers are ignored. } } // Handle /out - 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:<filename> - 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<StringRef, 1> 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<StringRef, 1> 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); } } @@ -1195,37 +1369,37 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // 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. @@ -1235,130 +1409,137 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { 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->MapFile = getMapFile(Args); - - if (Config->Incremental && Args.hasArg(OPT_profile)) { + 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); + + 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<sys::fs::UniqueID> WholeArchives; - AutoExporter Exporter; - for (auto *Arg : Args.filtered(OPT_wholearchive_file)) { - if (Optional<StringRef> Path = doFindFile(Arg->getValue())) { - if (Optional<sys::fs::UniqueID> ID = getUniqueID(*Path)) - WholeArchives.insert(*ID); - Exporter.addWholeArchive(*Path); - } - } + std::set<sys::fs::UniqueID> wholeArchives; + for (auto *arg : args.filtered(OPT_wholearchive_file)) + if (Optional<StringRef> path = doFindFile(arg->getValue())) + if (Optional<sys::fs::UniqueID> id = getUniqueID(*path)) + wholeArchives.insert(*id); // A predicate returning true if a given path is an argument for // /wholearchive:, or /wholearchive is enabled globally. // This function is a bit tricky because "foo.obj /wholearchive:././foo.obj" // needs to be handled as "/wholearchive:foo.obj foo.obj". - auto IsWholeArchive = [&](StringRef Path) -> bool { - if (Args.hasArg(OPT_wholearchive_flag)) + auto isWholeArchive = [&](StringRef path) -> bool { + if (args.hasArg(OPT_wholearchive_flag)) return true; - if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path)) - return WholeArchives.count(*ID); + if (Optional<sys::fs::UniqueID> id = getUniqueID(path)) + return wholeArchives.count(*id); return false; }; // Create a list of input files. Files can be given as arguments // for /defaultlib option. - for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file)) - if (Optional<StringRef> Path = findFile(Arg->getValue())) - enqueuePath(*Path, IsWholeArchive(*Path)); + for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file)) + if (Optional<StringRef> path = findFile(arg->getValue())) + enqueuePath(*path, isWholeArchive(*path)); - for (auto *Arg : Args.filtered(OPT_defaultlib)) - if (Optional<StringRef> Path = findLib(Arg->getValue())) - enqueuePath(*Path, false); + for (auto *arg : args.filtered(OPT_defaultlib)) + if (Optional<StringRef> 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. @@ -1369,154 +1550,169 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // 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<ObjFile>(convertResToCOFF(Resources))); + if (!resources.empty()) + symtab->addFile(make<ObjFile>(convertResToCOFF(resources))); - if (Tar) - Tar->append("response.txt", - createResponseFile(Args, FilePaths, - ArrayRef<StringRef>(SearchPaths).slice(1))); + if (tar) + tar->append("response.txt", + createResponseFile(args, filePaths, + ArrayRef<StringRef>(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(); - - 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); + 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->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 @@ -1525,33 +1721,33 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { 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<Undefined>(Sym)) - if (!U->WeakAlias) - U->WeakAlias = Symtab->addUndefined(To); + if (auto *u = dyn_cast<Undefined>(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()); @@ -1559,11 +1755,29 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { 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<Lazy>(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. // @@ -1577,95 +1791,91 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // 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; - } - - // 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<Defined>(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); - }); + if (config->mingw) { + // In MinGW, all symbols are automatically exported if no symbols + // are chosen to be exported. + maybeExportMinGWSymbols(args); + + // 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<DefinedCommon>(Sym); - if (!DC) + auto *dc = dyn_cast<DefinedCommon>(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. @@ -1673,7 +1883,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { // Stop early so we can print the results. Timer::root().stop(); - if (Config->ShowTiming) + if (config->showTiming) Timer::root().print(); } diff --git a/COFF/Driver.h b/COFF/Driver.h index e779721ab75d..6100c3ca0c9e 100644 --- a/COFF/Driver.h +++ b/COFF/Driver.h @@ -1,9 +1,8 @@ //===- Driver.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 // //===----------------------------------------------------------------------===// @@ -31,7 +30,7 @@ namespace lld { namespace coff { class LinkerDriver; -extern LinkerDriver *Driver; +extern LinkerDriver *driver; using llvm::COFF::MachineTypes; using llvm::COFF::WindowsSubsystem; @@ -45,65 +44,71 @@ public: class ArgParser { public: // Concatenate LINK environment variable and given arguments and parse them. - llvm::opt::InputArgList parseLINK(std::vector<const char *> Args); + llvm::opt::InputArgList parseLINK(std::vector<const char *> args); // Tokenizes a given string and then parses as command line options. - llvm::opt::InputArgList parse(StringRef S) { return parse(tokenize(S)); } + 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<llvm::opt::InputArgList, std::vector<StringRef>> - parseDirectives(StringRef S); + parseDirectives(StringRef s); private: // Parses command line options. - llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> Args); + llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args); - std::vector<const char *> tokenize(StringRef S); + std::vector<const char *> tokenize(StringRef s); - COFFOptTable Table; + COFFOptTable table; }; class LinkerDriver { public: - void link(llvm::ArrayRef<const char *> Args); + void link(llvm::ArrayRef<const char *> 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<MemoryBuffer> mb); - MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> MB); + void enqueuePath(StringRef path, bool wholeArchive); private: - std::unique_ptr<llvm::TarWriter> Tar; // for /linkrepro + std::unique_ptr<llvm::TarWriter> 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<StringRef> findFile(StringRef Filename); - Optional<StringRef> findLib(StringRef Filename); - StringRef doFindFile(StringRef Filename); - StringRef doFindLib(StringRef Filename); - StringRef doFindLibMinGW(StringRef Filename); + Optional<StringRef> findFile(StringRef filename); + Optional<StringRef> findLib(StringRef filename); + StringRef doFindFile(StringRef filename); + StringRef doFindLib(StringRef filename); + StringRef doFindLibMinGW(StringRef filename); // Parses LIB environment which contains a list of search paths. void addLibSearchPaths(); // Library search path. The first element is always "" (current directory). - std::vector<StringRef> SearchPaths; + std::vector<StringRef> 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<llvm::sys::fs::UniqueID> VisitedFiles; + std::set<llvm::sys::fs::UniqueID> visitedFiles; + + std::set<std::string> visitedLibs; - std::set<std::string> 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}. @@ -115,60 +120,60 @@ private: StringRef findDefaultEntry(); WindowsSubsystem inferSubsystem(); - void addBuffer(std::unique_ptr<MemoryBuffer> MB, bool WholeArchive); - void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName, - StringRef ParentName); + void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive); + void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName, + StringRef parentName, uint64_t offsetInArchive); - void enqueuePath(StringRef Path, bool WholeArchive); - - void enqueueTask(std::function<void()> Task); + void enqueueTask(std::function<void()> task); bool run(); - std::list<std::function<void()>> TaskQueue; - std::vector<StringRef> FilePaths; - std::vector<MemoryBufferRef> Resources; + std::list<std::function<void()>> taskQueue; + std::vector<StringRef> filePaths; + std::vector<MemoryBufferRef> resources; - llvm::StringSet<> DirectivesExports; + llvm::StringSet<> directivesExports; }; // Functions below this line are defined in DriverUtils.cpp. -void printHelp(const char *Argv0); - -// For /machine option. -MachineTypes getMachineType(StringRef Arg); -StringRef machineToStr(MachineTypes MT); +void printHelp(const char *argv0); // Parses a string in the form of "<integer>[,<integer>]". -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 "<integer>[.<integer>]". // 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 "<subsystem>[,<integer>[.<integer>]]". -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 "[:<integer>]" +void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine); + // Parses a string in the form of "EMBED[,=<integer>]|NO". -void parseManifest(StringRef Arg); +void parseManifest(StringRef arg); // Parses a string in the form of "level=<string>|uiAccess=<string>" -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<MemoryBuffer> createManifestRes(); void createSideBySideManifest(); // Used for dllexported symbols. -Export parseExport(StringRef Arg); +Export parseExport(StringRef arg); void fixupExports(); void assignExportOrdinals(); @@ -176,12 +181,12 @@ void assignExportOrdinals(); // 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<MemoryBufferRef> MBs); +MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs); -void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects); +void runMSVCLinker(std::string rsp, ArrayRef<StringRef> objects); // Create enum with OPT_xxx values for each option in Options.td enum { diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp index 3a11895497a4..4360ac23b262 100644 --- a/COFF/DriverUtils.cpp +++ b/COFF/DriverUtils.cpp @@ -1,9 +1,8 @@ //===- DriverUtils.cpp ----------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// // @@ -48,104 +47,78 @@ 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<std::string> 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<std::string> 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<StringRef> Args; + StringRef prog; + std::vector<StringRef> args; }; } // anonymous namespace -// Returns /machine's value. -MachineTypes getMachineType(StringRef S) { - MachineTypes MT = StringSwitch<MachineTypes>(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 "<integer>[,<integer>]". -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 "<integer>[.<integer>]". // 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 parseGuard(StringRef FullArg) { - SmallVector<StringRef, 1> SplitArgs; - FullArg.split(SplitArgs, ","); - for (StringRef Arg : SplitArgs) { - if (Arg.equals_lower("no")) - Config->GuardCF = GuardCFLevel::Off; - else if (Arg.equals_lower("nolongjmp")) - Config->GuardCF = GuardCFLevel::NoLongJmp; - else if (Arg.equals_lower("cf") || Arg.equals_lower("longjmp")) - Config->GuardCF = GuardCFLevel::Full; +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<StringRef, 1> splitArgs; + fullArg.split(splitArgs, ","); + for (StringRef arg : splitArgs) { + if (arg.equals_lower("no")) + config->guardCF = GuardCFLevel::Off; + else if (arg.equals_lower("nolongjmp")) + config->guardCF = GuardCFLevel::NoLongJmp; + else if (arg.equals_lower("cf") || arg.equals_lower("longjmp")) + config->guardCF = GuardCFLevel::Full; else - fatal("invalid argument to /guard: " + Arg); + fatal("invalid argument to /guard: " + arg); } } // Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]". -void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major, - uint32_t *Minor) { - StringRef SysStr, Ver; - std::tie(SysStr, Ver) = Arg.split(','); - *Sys = StringSwitch<WindowsSubsystem>(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<WindowsSubsystem>(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) @@ -154,175 +127,217 @@ void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major, .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 "<from>=<to>". // 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 "<from>=<to>". // 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); return; } - int V; - if (Align.getAsInteger(0, V)) { - error("/aligncomm: invalid argument: " + S); + 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; } - Config->AlignComm[Name] = std::max(Config->AlignComm[Name], 1 << V); + // 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[,=<integer>]|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=<string>|uiAccess=<string>|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(); - - 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(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; } } - 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. @@ -330,387 +345,390 @@ public: // 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<MemoryBuffer> 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 << "<?xml version=\"1.0\" standalone=\"yes\"?>\n" + os << "<?xml version=\"1.0\" standalone=\"yes\"?>\n" << "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n" << " manifestVersion=\"1.0\">\n"; - if (Config->ManifestUAC) { - OS << " <trustInfo>\n" + if (config->manifestUAC) { + os << " <trustInfo>\n" << " <security>\n" << " <requestedPrivileges>\n" - << " <requestedExecutionLevel level=" << Config->ManifestLevel - << " uiAccess=" << Config->ManifestUIAccess << "/>\n" + << " <requestedExecutionLevel level=" << config->manifestLevel + << " uiAccess=" << config->manifestUIAccess << "/>\n" << " </requestedPrivileges>\n" << " </security>\n" << " </trustInfo>\n"; } - if (!Config->ManifestDependency.empty()) { - OS << " <dependency>\n" + if (!config->manifestDependency.empty()) { + os << " <dependency>\n" << " <dependentAssembly>\n" - << " <assemblyIdentity " << Config->ManifestDependency << " />\n" + << " <assemblyIdentity " << config->manifestDependency << " />\n" << " </dependentAssembly>\n" << " </dependency>\n"; } - OS << "</assembly>\n"; - return OS.str(); + os << "</assembly>\n"; + return os.str(); } -static std::string createManifestXmlWithInternalMt(StringRef DefaultXml) { - std::unique_ptr<MemoryBuffer> DefaultXmlCopy = - MemoryBuffer::getMemBufferCopy(DefaultXml); +static std::string createManifestXmlWithInternalMt(StringRef defaultXml) { + std::unique_ptr<MemoryBuffer> defaultXmlCopy = + MemoryBuffer::getMemBufferCopy(defaultXml); - windows_manifest::WindowsManifestMerger Merger; - if (auto E = Merger.merge(*DefaultXmlCopy.get())) + 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<MemoryBuffer> Manifest = - check(MemoryBuffer::getFile(Filename)); - if (auto E = Merger.merge(*Manifest.get())) - fatal("internal manifest tool failed on file " + Filename + ": " + - toString(std::move(E))); + for (StringRef filename : config->manifestInput) { + std::unique_ptr<MemoryBuffer> manifest = + check(MemoryBuffer::getFile(filename)); + if (auto e = merger.merge(*manifest.get())) + fatal("internal manifest tool failed on file " + filename + ": " + + toString(std::move(e))); } - return Merger.getMergedManifest().get()->getBuffer(); + 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<WritableMemoryBuffer> -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<object::WinResHeaderPrefix *>(Buf); - Prefix->DataSize = ManifestSize; - Prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) + + auto *prefix = reinterpret_cast<object::WinResHeaderPrefix *>(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<object::WinResIDs *>(Buf); - IDs->setType(RT_MANIFEST); - IDs->setName(Config->ManifestID); - Buf += sizeof(object::WinResIDs); + auto *iDs = reinterpret_cast<object::WinResIDs *>(buf); + iDs->setType(RT_MANIFEST); + iDs->setName(config->manifestID); + buf += sizeof(object::WinResIDs); // Write the suffix. - auto *Suffix = reinterpret_cast<object::WinResHeaderSuffix *>(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<object::WinResHeaderSuffix *>(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<MemoryBuffer> createManifestRes() { - std::string Manifest = createManifestXml(); + std::string manifest = createManifestXml(); - std::unique_ptr<WritableMemoryBuffer> Res = - createMemoryBufferForManifestRes(Manifest.size()); + std::unique_ptr<WritableMemoryBuffer> 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 // "<name>[=<internalname>][,@ordinal[,NONAME]][,DATA][,PRIVATE]" // or "<name>=<dllname>.<name>". // 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 "<name>=<dllname>.<name>". - 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 "<name>=<internalname>[,@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<uint16_t> Ords; - for (Export &E : Config->Exports) { - if (E.Ordinal == 0) + std::set<uint16_t> ords; + for (Export &e : config->exports) { + if (e.ordinal == 0) continue; - 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; - } else { - if (auto *U = dyn_cast<Undefined>(Sym)) - if (U->WeakAlias) - Sym = U->WeakAlias; - E.SymbolName = Sym->getName(); - } + if (!ords.insert(e.ordinal).second) + fatal("duplicate export ordinal: " + e.name); } - for (Export &E : Config->Exports) { - if (!E.ForwardTo.empty()) { - E.ExportName = undecorate(E.Name); + 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); + 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<StringRef, Export *> Map(Config->Exports.size()); - std::vector<Export> V; - for (Export &E : Config->Exports) { - auto Pair = Map.insert(std::make_pair(E.ExportName, &E)); - bool Inserted = Pair.second; - if (Inserted) { - V.push_back(E); + DenseMap<StringRef, Export *> map(config->exports.size()); + std::vector<Export> v; + for (Export &e : config->exports) { + auto pair = map.insert(std::make_pair(e.exportName, &e)); + 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<StringRef, InputFile *> 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<MemoryBufferRef> MBs) { - object::WindowsResourceParser Parser; - - for (MemoryBufferRef MB : MBs) { - std::unique_ptr<object::Binary> Bin = check(object::createBinary(MB)); - object::WindowsResource *RF = dyn_cast<object::WindowsResource>(Bin.get()); - if (!RF) +// Does what cvtres.exe does, but in-process and cross-platform. +MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs) { + object::WindowsResourceParser parser; + + for (MemoryBufferRef mb : mbs) { + std::unique_ptr<object::Binary> bin = check(object::createBinary(mb)); + object::WindowsResource *rf = dyn_cast<object::WindowsResource>(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<std::string> 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<std::unique_ptr<MemoryBuffer>> E = - llvm::object::writeWindowsResourceCOFF(Config->Machine, Parser); - if (!E) - fatal("failed to write .res to COFF: " + toString(E.takeError())); + Expected<std::unique_ptr<MemoryBuffer>> 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::unique_ptr<MemoryBuffer>>(std::move(*E)); // take ownership - return MBRef; + MemoryBufferRef mbref = **e; + make<std::unique_ptr<MemoryBuffer>>(std::move(*e)); // take ownership + return mbref; } // Create OptTable @@ -721,7 +739,7 @@ MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> MBs) { #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}, @@ -729,36 +747,36 @@ static const llvm::opt::OptTable::Info InfoTable[] = { #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); - } -} - -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") + 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") return cl::TokenizeWindowsCommandLine; return cl::TokenizeGNUCommandLine; } @@ -767,104 +785,111 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) { } // Parses a given list of options. -opt::InputArgList ArgParser::parse(ArrayRef<const char *> Argv) { +opt::InputArgList ArgParser::parse(ArrayRef<const char *> 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 @<filename>) // and then parse the argument again. - SmallVector<const char *, 256> ExpandedArgv(Argv.data(), Argv.data() + Argv.size()); - cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), ExpandedArgv); - Args = Table.ParseArgs(makeArrayRef(ExpandedArgv).drop_front(), MissingIndex, - MissingCount); + SmallVector<const char *, 256> expandedArgv(argv.data(), + argv.data() + argv.size()); + cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv); + args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex, + missingCount); // Print the real command line if response files are expanded. - if (Args.hasArg(OPT_verbose) && Argv.size() != 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<opt::InputArgList, std::vector<StringRef>> -ArgParser::parseDirectives(StringRef S) { - std::vector<StringRef> Exports; - SmallVector<const char *, 16> Rest; +ArgParser::parseDirectives(StringRef s) { + std::vector<StringRef> exports; + SmallVector<const char *, 16> rest; - for (StringRef Tok : tokenize(S)) { - if (Tok.startswith_lower("/export:") || Tok.startswith_lower("-export:")) - Exports.push_back(Tok.substr(strlen("/export:"))); + 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<const char *> Argv) { +opt::InputArgList ArgParser::parseLINK(std::vector<const char *> argv) { // Concatenate LINK env and command line arguments, and then parse them. - if (Optional<std::string> S = Process::GetEnv("LINK")) { - std::vector<const char *> V = tokenize(*S); - Argv.insert(std::next(Argv.begin()), V.begin(), V.end()); + if (Optional<std::string> s = Process::GetEnv("LINK")) { + std::vector<const char *> v = tokenize(*s); + argv.insert(std::next(argv.begin()), v.begin(), v.end()); } - if (Optional<std::string> S = Process::GetEnv("_LINK_")) { - std::vector<const char *> V = tokenize(*S); - Argv.insert(std::next(Argv.begin()), V.begin(), V.end()); + if (Optional<std::string> s = Process::GetEnv("_LINK_")) { + std::vector<const char *> v = tokenize(*s); + argv.insert(std::next(argv.begin()), v.begin(), v.end()); } - return parse(Argv); + return parse(argv); } -std::vector<const char *> ArgParser::tokenize(StringRef S) { - SmallVector<const char *, 16> Tokens; - cl::TokenizeWindowsCommandLine(S, Saver, Tokens); - return std::vector<const char *>(Tokens.begin(), Tokens.end()); +std::vector<const char *> ArgParser::tokenize(StringRef s) { + SmallVector<const char *, 16> tokens; + cl::TokenizeWindowsCommandLine(s, saver, tokens); + return std::vector<const char *>(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); } diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp index 34ea360fa925..2b2818de3889 100644 --- a/COFF/ICF.cpp +++ b/COFF/ICF.cpp @@ -1,9 +1,8 @@ //===- ICF.cpp ------------------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// // @@ -38,33 +37,32 @@ 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<Chunk *> V); + void run(ArrayRef<Chunk *> 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<void(size_t, size_t)> Fn); + void forEachClassRange(size_t begin, size_t end, + std::function<void(size_t, size_t)> fn); - void forEachClass(std::function<void(size_t, size_t)> Fn); + void forEachClass(std::function<void(size_t, size_t)> fn); - std::vector<SectionChunk *> Chunks; - int Cnt = 0; - std::atomic<bool> Repeat = {false}; + std::vector<SectionChunk *> chunks; + int cnt = 0; + std::atomic<bool> repeat = {false}; }; // Returns true if section S is subject of ICF. @@ -78,143 +76,144 @@ private: // 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<uint32_t> Classes; - for (const SectionChunk *C : SC->children()) - if (!C->SectionName.startswith(".debug") && - C->SectionName != ".gfids$y" && C->SectionName != ".gljmp$y") - Classes.push_back(C->Class[Cnt % 2]); - return Classes; +bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) { + auto childClasses = [&](const SectionChunk *sc) { + std::vector<uint32_t> classes; + for (const SectionChunk &c : sc->children()) + if (!c.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<DefinedRegular>(B1)) - if (auto *D2 = dyn_cast<DefinedRegular>(B2)) - return D1->getValue() == D2->getValue() && - D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2]; + if (auto *d1 = dyn_cast<DefinedRegular>(b1)) + if (auto *d2 = dyn_cast<DefinedRegular>(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<DefinedRegular>(B1)) - if (auto *D2 = dyn_cast<DefinedRegular>(B2)) - return D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2]; + if (auto *d1 = dyn_cast<DefinedRegular>(b1)) + if (auto *d2 = dyn_cast<DefinedRegular>(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<void(size_t, size_t)> 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<void(size_t, size_t)> 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<void(size_t, size_t)> Fn) { +void ICF::forEachClass(std::function<void(size_t, size_t)> 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; } @@ -222,95 +221,97 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> Fn) { // 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<Chunk *> Vec) { - ScopedTimer T(ICFTimer); +void ICF::run(ArrayRef<Chunk *> vec) { + ScopedTimer t(icfTimer); // Collect only mergeable sections and group by hash value. - uint32_t NextId = 1; - for (Chunk *C : Vec) { - if (auto *SC = dyn_cast<SectionChunk>(C)) { - if (isEligible(SC)) - Chunks.push_back(SC); + uint32_t nextId = 1; + for (Chunk *c : vec) { + if (auto *sc = dyn_cast<SectionChunk>(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<DefinedRegular>(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<DefinedRegular>(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<Chunk *> Chunks) { ICF().run(Chunks); } +void doICF(ArrayRef<Chunk *> chunks) { ICF().run(chunks); } } // namespace coff } // namespace lld diff --git a/COFF/ICF.h b/COFF/ICF.h index 9c54e0c9ec2d..0b3c8fa2ff2e 100644 --- a/COFF/ICF.h +++ b/COFF/ICF.h @@ -1,9 +1,8 @@ //===- ICF.h --------------------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// @@ -18,7 +17,7 @@ namespace coff { class Chunk; -void doICF(ArrayRef<Chunk *> Chunks); +void doICF(ArrayRef<Chunk *> chunks); } // namespace coff } // namespace lld diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp index 236c90ef0388..c00d5c5b494e 100644 --- a/COFF/InputFiles.cpp +++ b/COFF/InputFiles.cpp @@ -1,15 +1,15 @@ //===- InputFiles.cpp -----------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Chunks.h" #include "Config.h" +#include "DebugTypes.h" #include "Driver.h" #include "SymbolTable.h" #include "Symbols.h" @@ -20,6 +20,10 @@ #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" @@ -35,6 +39,7 @@ using namespace llvm; using namespace llvm::COFF; +using namespace llvm::codeview; using namespace llvm::object; using namespace llvm::support::endian; @@ -44,80 +49,80 @@ using llvm::support::ulittle32_t; namespace lld { namespace coff { -std::vector<ObjFile *> ObjFile::Instances; -std::vector<ImportFile *> ImportFile::Instances; -std::vector<BitcodeFile *> BitcodeFile::Instances; +std::vector<ObjFile *> ObjFile::instances; +std::vector<ImportFile *> ImportFile::instances; +std::vector<BitcodeFile *> BitcodeFile::instances; /// Checks that Source is compatible with being a weak alias to Target. /// If Source is Undefined and has no weak alias set, makes it a weak /// alias to Target. -static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F, - Symbol *Source, Symbol *Target) { - if (auto *U = dyn_cast<Undefined>(Source)) { - if (U->WeakAlias && U->WeakAlias != Target) { +static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f, + Symbol *source, Symbol *target) { + if (auto *u = dyn_cast<Undefined>(source)) { + if (u->weakAlias && u->weakAlias != target) { // Weak aliases as produced by GCC are named in the form // .weak.<weaksymbol>.<othersymbol>, where <othersymbol> is the name // of another symbol emitted near the weak symbol. // Just use the definition from the first object file that defined // this weak symbol. - if (Config->MinGW) + 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<MemoryBufferRef> getArchiveMembers(Archive *File) { - std::vector<MemoryBufferRef> V; - Error Err = Error::success(); - for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) { - Archive::Child C = - CHECK(COrErr, - File->getFileName() + ": could not get the child of the archive"); - MemoryBufferRef MBRef = - CHECK(C.getMemoryBufferRef(), - File->getFileName() + +std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) { + std::vector<MemoryBufferRef> v; + Error err = Error::success(); + for (const ErrorOr<Archive::Child> &cOrErr : file->children(err)) { + Archive::Child c = + CHECK(cOrErr, + file->getFileName() + ": could not get the child of the archive"); + MemoryBufferRef mbref = + CHECK(c.getMemoryBufferRef(), + file->getFileName() + ": could not get the buffer for a child of the archive"); - V.push_back(MBRef); + 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<Binary> Bin = CHECK(createBinary(MB), this); + std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this); - if (auto *Obj = dyn_cast<COFFObjectFile>(Bin.get())) { - Bin.release(); - COFFObj.reset(Obj); + if (auto *obj = dyn_cast<COFFObjectFile>(bin.get())) { + bin.release(); + coffObj.reset(obj); } else { fatal(toString(this) + " is not a COFF file"); } @@ -125,6 +130,15 @@ void ObjFile::parse() { // 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 @@ -133,45 +147,42 @@ void ObjFile::parse() { // an associative section definition together with the parent comdat's leader, // we set the pointer to either nullptr (to mark the section as discarded) or a // valid SectionChunk for that section. -static SectionChunk *const PendingComdat = reinterpret_cast<SectionChunk *>(1); +static SectionChunk *const pendingComdat = reinterpret_cast<SectionChunk *>(1); void ObjFile::initializeChunks() { - uint32_t NumSections = COFFObj->getNumberOfSections(); - Chunks.reserve(NumSections); - SparseChunks.resize(NumSections + 1); - for (uint32_t I = 1; I < NumSections + 1; ++I) { - const coff_section *Sec; - 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()); - - StringRef Name; - if (auto EC = COFFObj->getSectionName(Sec, Name)) - fatal("getSectionName failed: #" + Twine(SectionNumber) + ": " + - EC.message()); - - if (Name == ".drectve") { - ArrayRef<uint8_t> Data; - COFFObj->getSectionContents(Sec, Data); - Directives = std::string((const char *)Data.data(), Data.size()); +SectionChunk *ObjFile::readSection(uint32_t sectionNumber, + const coff_aux_section_definition *def, + StringRef leaderName) { + const coff_section *sec = getSection(sectionNumber); + + StringRef name; + if (Expected<StringRef> e = coffObj->getSectionName(sec)) + name = *e; + else + fatal("getSectionName failed: #" + Twine(sectionNumber) + ": " + + toString(e.takeError())); + + if (name == ".drectve") { + ArrayRef<uint8_t> 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; } @@ -186,377 +197,648 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber, // 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<SectionChunk>(this, Sec); - if (Def) - C->Checksum = Def->CheckSum; + auto *c = make<SectionChunk>(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(); + + auto diag = [&]() { + StringRef name, parentName; + coffObj->getSymbolName(sym, name); + + const coff_section *parentSec = getSection(parentIndex); + if (Expected<StringRef> 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 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) + 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<StringRef, uint32_t> &PrevailingSectionMap) { + COFFSymbolRef sym, DenseMap<StringRef, uint32_t> &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$<func> 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<StringRef, uint32_t> &PrevailingSectionMap) { - StringRef Name; - COFFObj->getSymbolName(Sym, Name); - if (Name.consume_front(".pdata$") || Name.consume_front(".xdata$")) { - // For MinGW, treat .[px]data$<func> as implicitly associative to - // the symbol <func>. - auto ParentSym = PrevailingSectionMap.find(Name); - if (ParentSym != PrevailingSectionMap.end()) - readAssociativeDefinition(Sym, Def, ParentSym->second); + COFFSymbolRef sym, const coff_aux_section_definition *def, + const DenseMap<StringRef, uint32_t> &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$<func> and .eh_frame$<func> as implicitly + // associative to the symbol <func>. + 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<DefinedRegular>(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); - - SmallVector<std::pair<Symbol *, uint32_t>, 8> WeakAliases; - std::vector<uint32_t> PendingIndexes; - PendingIndexes.reserve(NumSymbols); - - DenseMap<StringRef, uint32_t> PrevailingSectionMap; - std::vector<const coff_aux_section_definition *> 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<coff_aux_weak_external>()->TagIndex; - WeakAliases.emplace_back(Symbols[I], TagIndex); - } else if (Optional<Symbol *> OptSym = - createDefined(COFFSym, ComdatDefs, PrevailingComdat)) { - Symbols[I] = *OptSym; - if (Config->MinGW && PrevailingComdat) - recordPrevailingSymbolForMingw(COFFSym, PrevailingSectionMap); + uint32_t numSymbols = coffObj->getNumberOfSymbols(); + symbols.resize(numSymbols); + + SmallVector<std::pair<Symbol *, uint32_t>, 8> weakAliases; + std::vector<uint32_t> pendingIndexes; + pendingIndexes.reserve(numSymbols); + + DenseMap<StringRef, uint32_t> prevailingSectionMap; + std::vector<const coff_aux_section_definition *> 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<coff_aux_weak_external>()->TagIndex; + weakAliases.emplace_back(symbols[i], tagIndex); + } else if (Optional<Symbol *> 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<DefinedRegular>(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<Symbol *> ObjFile::createDefined( - COFFSymbolRef Sym, - std::vector<const coff_aux_section_definition *> &ComdatDefs, - bool &Prevailing) { - Prevailing = false; - auto GetName = [&]() { - StringRef S; - COFFObj->getSymbolName(Sym, S); - return S; + COFFSymbolRef sym, + std::vector<const coff_aux_section_definition *> &comdatDefs, + bool &prevailing) { + prevailing = false; + auto getName = [&]() { + StringRef s; + coffObj->getSymbolName(sym, s); + return s; }; - if (Sym.isCommon()) { - auto *C = make<CommonChunk>(Sym); - Chunks.push_back(C); - return Symtab->addCommon(this, GetName(), Sym.getValue(), Sym.getGeneric(), - C); + if (sym.isCommon()) { + auto *c = make<CommonChunk>(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<DefinedAbsolute>(Name, Sym); + if (sym.isExternal()) + return symtab->addAbsolute(name, sym); + return make<DefinedAbsolute>(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 ((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()); + 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)); + + // 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<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false, - /*IsExternal*/ false, Sym.getGeneric()); - Prevailing = true; + leader = make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false, + /*IsExternal*/ false, sym.getGeneric()); + prevailing = true; } - if (Prevailing) { - SectionChunk *C = readSection(SectionNumber, Def, GetName()); - SparseChunks[SectionNumber] = C; - C->Sym = cast<DefinedRegular>(Leader); - cast<DefinedRegular>(Leader)->Data = &C->Repl; - } else { - SparseChunks[SectionNumber] = nullptr; + 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)); } - return Leader; - } + COMDATType selection = (COMDATType)def->Selection; - // 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; + if (leader->isCOMDAT) + handleComdatSelection(sym, selection, prevailing, leader); + + if (prevailing) { + SectionChunk *c = readSection(sectionNumber, def, getName()); + sparseChunks[sectionNumber] = c; + c->sym = cast<DefinedRegular>(leader); + c->selection = selection; + cast<DefinedRegular>(leader)->data = &c->repl; + } else { + sparseChunks[sectionNumber] = nullptr; } + return leader; } - // readAssociativeDefinition() writes to SparseChunks, so need to check again. - if (SparseChunks[SectionNumber] == PendingComdat) + // 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; + } - return createRegular(Sym); + return createRegular(sym); } MachineTypes ObjFile::getMachineType() { - if (COFFObj) - return static_cast<MachineTypes>(COFFObj->getMachine()); + if (coffObj) + return static_cast<MachineTypes>(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<uint8_t> 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<uint8_t> 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<CVSymbol> sym = readSymbolFromStream(ss.getRecordData(), offset); + if (!sym) { + consumeError(sym.takeError()); + return; + } + if (sym->kind() == SymbolKind::S_COMPILE3) { + auto cs = + cantFail(SymbolDeserializer::deserializeAs<Compile3Sym>(sym.get())); + hotPatchable = + (cs.Flags & CompileSym3Flags::HotPatch) != CompileSym3Flags::None; + } + if (sym->kind() == SymbolKind::S_OBJNAME) { + auto objName = cantFail(SymbolDeserializer::deserializeAs<ObjNameSym>( + 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<uint8_t> 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<TypeServer2Record>(firstType->data())); + debugTypesObj = makeUseTypeServerSource(this, &ts); + return; + } + + if (firstType->kind() == LF_PRECOMP) { + PrecompRecord precomp = cantFail( + TypeDeserializer::deserializeAs<PrecompRecord>(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<const coff_import_header *>(Buf); + const char *buf = mb.getBufferStart(); + const auto *hdr = reinterpret_cast<const coff_import_header *>(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<void>(Symtab->addImportData(Name, this)); + if (hdr->getType() == llvm::COFF::IMPORT_CONST) + static_cast<void>(symtab->addImportData(name, this)); // If type is function, we need to create a thunk which jump to an // address pointed by the __imp_ symbol. (This allows you to call // DLL functions just like regular non-DLL functions.) - if (Hdr->getType() == llvm::COFF::IMPORT_CODE) - ThunkSym = Symtab->addImportThunk( - Name, cast_or_null<DefinedImportData>(ImpSym), Hdr->Machine); + if (hdr->getType() == llvm::COFF::IMPORT_CODE) + thunkSym = symtab->addImportThunk( + name, cast_or_null<DefinedImportData>(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<std::pair<Symbol *, bool>> Comdat(Obj->getComdatTable().size()); - for (size_t I = 0; I != Obj->getComdatTable().size(); ++I) - Comdat[I] = Symtab->addComdat(this, Saver.save(Obj->getComdatTable()[I])); - for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) { - StringRef SymName = Saver.save(ObjSym.getName()); - int ComdatIndex = ObjSym.getComdatIndex(); - Symbol *Sym; - if (ObjSym.isUndefined()) { - Sym = Symtab->addUndefined(SymName, this, false); - } else if (ObjSym.isCommon()) { - Sym = Symtab->addCommon(this, SymName, ObjSym.getCommonSize()); - } else if (ObjSym.isWeak() && ObjSym.isIndirect()) { + std::vector<std::pair<Symbol *, bool>> 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: @@ -569,22 +851,31 @@ MachineTypes BitcodeFile::getMachineType() { 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 "<internal>"; - 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(); } diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h index ec802f2d0300..dfad9814a397 100644 --- a/COFF/InputFiles.h +++ b/COFF/InputFiles.h @@ -1,9 +1,8 @@ //===- InputFiles.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 // //===----------------------------------------------------------------------===// @@ -33,7 +32,7 @@ class DbiModuleDescriptorBuilder; namespace lld { namespace coff { -std::vector<MemoryBufferRef> getArchiveMembers(llvm::object::Archive *File); +std::vector<MemoryBufferRef> getArchiveMembers(llvm::object::Archive *file); using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; using llvm::COFF::MachineTypes; @@ -47,20 +46,22 @@ 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; @@ -68,158 +69,195 @@ public: // 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<Archive> File; - std::string Filename; - llvm::DenseSet<uint64_t> Seen; + std::unique_ptr<Archive> file; + llvm::DenseSet<uint64_t> 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<Chunk *> getChunks() { return Chunks; } - ArrayRef<SectionChunk *> getDebugChunks() { return DebugChunks; } - ArrayRef<SectionChunk *> getSXDataChunks() { return SXDataChunks; } - ArrayRef<SectionChunk *> getGuardFidChunks() { return GuardFidChunks; } - ArrayRef<SectionChunk *> getGuardLJmpChunks() { return GuardLJmpChunks; } - ArrayRef<Symbol *> getSymbols() { return Symbols; } - - // Returns a Symbol object for the SymbolIndex'th symbol in the + ArrayRef<Chunk *> getChunks() { return chunks; } + ArrayRef<SectionChunk *> getDebugChunks() { return debugChunks; } + ArrayRef<SectionChunk *> getSXDataChunks() { return sXDataChunks; } + ArrayRef<SectionChunk *> getGuardFidChunks() { return guardFidChunks; } + ArrayRef<SectionChunk *> getGuardLJmpChunks() { return guardLJmpChunks; } + ArrayRef<Symbol *> getSymbols() { return symbols; } + + ArrayRef<uint8_t> 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<ObjFile *> Instances; + static std::vector<ObjFile *> 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<uint32_t> PCHSignature; + llvm::Optional<uint32_t> 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<llvm::codeview::CVTypeArray> 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<StringRef, uint32_t> &PrevailingSectionMap); + COFFSymbolRef coffSym, + llvm::DenseMap<StringRef, uint32_t> &prevailingSectionMap); void maybeAssociateSEHForMingw( - COFFSymbolRef Sym, const llvm::object::coff_aux_section_definition *Def, - const llvm::DenseMap<StringRef, uint32_t> &PrevailingSectionMap); + COFFSymbolRef sym, const llvm::object::coff_aux_section_definition *def, + const llvm::DenseMap<StringRef, uint32_t> &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<Symbol *> - createDefined(COFFSymbolRef Sym, + createDefined(COFFSymbolRef sym, std::vector<const llvm::object::coff_aux_section_definition *> - &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<COFFObjectFile> COFFObj; + std::unique_ptr<COFFObjectFile> coffObj; // List of all chunks defined by this file. This includes both section // chunks and non-section chunks for common symbols. - std::vector<Chunk *> Chunks; + std::vector<Chunk *> chunks; // CodeView debug info sections. - std::vector<SectionChunk *> DebugChunks; + std::vector<SectionChunk *> debugChunks; // Chunks containing symbol table indices of exception handlers. Only used for // 32-bit x86. - std::vector<SectionChunk *> SXDataChunks; + std::vector<SectionChunk *> sXDataChunks; // Chunks containing symbol table indices of address taken symbols and longjmp // targets. These are not linked into the final binary when /guard:cf is set. - std::vector<SectionChunk *> GuardFidChunks; - std::vector<SectionChunk *> GuardLJmpChunks; + std::vector<SectionChunk *> guardFidChunks; + std::vector<SectionChunk *> guardLJmpChunks; // This vector contains the same chunks as Chunks, but they are // indexed such that you can get a SectionChunk by section index. // Nonexistent section indices are filled with null pointers. // (Because section number is 1-based, the first slot is always a // null pointer.) - std::vector<SectionChunk *> SparseChunks; + std::vector<SectionChunk *> 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<Symbol *> Symbols; + std::vector<Symbol *> symbols; }; // This type represents import library members that contain DLL names @@ -227,23 +265,23 @@ private: // 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<ImportFile *> Instances; + static std::vector<ImportFile *> 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 @@ -253,28 +291,31 @@ public: // 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<Symbol *> getSymbols() { return Symbols; } + BitcodeFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive); + static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } + ArrayRef<Symbol *> getSymbols() { return symbols; } MachineTypes getMachineType() override; - static std::vector<BitcodeFile *> Instances; - std::unique_ptr<llvm::lto::InputFile> Obj; + static std::vector<BitcodeFile *> instances; + std::unique_ptr<llvm::lto::InputFile> obj; private: void parse() override; - std::vector<Symbol *> Symbols; + std::vector<Symbol *> 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 diff --git a/COFF/LTO.cpp b/COFF/LTO.cpp index 92d9ff0937c0..eb3c60d66077 100644 --- a/COFF/LTO.cpp +++ b/COFF/LTO.cpp @@ -1,9 +1,8 @@ //===- LTO.cpp ------------------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// @@ -11,6 +10,7 @@ #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" @@ -18,6 +18,7 @@ #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" @@ -41,112 +42,165 @@ using namespace llvm::object; using namespace lld; using namespace lld::coff; -static std::unique_ptr<lto::LTO> 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<raw_fd_ostream> openFile(StringRef file) { + std::error_code ec; + auto ret = + llvm::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::F_None); + if (ec) { + error("cannot open " + file + ": " + ec.message()); + return nullptr; + } + return ret; +} + +static 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(); - - if (Config->SaveTemps) - checkError(C.addSaveTemps(std::string(Config->OutputFile) + ".", + 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) + ".", /*UseInputModulePath*/ true)); - lto::ThinBackend Backend; - if (Config->ThinLTOJobs != 0) - Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs); - return llvm::make_unique<lto::LTO>(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<lto::LTO>(createConfig(), backend, + config->ltoPartitions); +} BitcodeCompiler::~BitcodeCompiler() = default; -static void undefine(Symbol *S) { replaceSymbol<Undefined>(S, S->getName()); } +static void undefine(Symbol *s) { replaceSymbol<Undefined>(s, s->getName()); } + +void BitcodeCompiler::add(BitcodeFile &f) { + lto::InputFile &obj = *f.obj; + unsigned symNum = 0; + std::vector<Symbol *> symBodies = f.getSymbols(); + std::vector<lto::SymbolResolution> resols(symBodies.size()); -void BitcodeCompiler::add(BitcodeFile &F) { - lto::InputFile &Obj = *F.Obj; - unsigned SymNum = 0; - std::vector<Symbol *> SymBodies = F.getSymbols(); - std::vector<lto::SymbolResolution> 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<StringRef> 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<MemoryBuffer> 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<MemoryBuffer> mb) { + files[task] = std::move(mb); })); - checkError(LTOObj->run( - [&](size_t Task) { + checkError(ltoObj->run( + [&](size_t task) { return llvm::make_unique<lto::NativeObjectStream>( - llvm::make_unique<raw_svector_ostream>(Buf[Task])); + llvm::make_unique<raw_svector_ostream>(buf[task])); }, - Cache)); + cache)); + + // 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"); + } + + // 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); + if (!config->ltoCache.empty()) + pruneCache(config->ltoCache, config->ltoCachePolicy); - std::vector<StringRef> Ret; - for (unsigned I = 0; I != MaxTasks; ++I) { - if (Buf[I].empty()) + std::vector<StringRef> 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<MemoryBuffer> &File : Files) - if (File) - Ret.push_back(File->getBuffer()); + for (std::unique_ptr<MemoryBuffer> &file : files) + if (file) + ret.push_back(file->getBuffer()); - return Ret; + return ret; } diff --git a/COFF/LTO.h b/COFF/LTO.h index f00924654780..2a0cfa061c95 100644 --- a/COFF/LTO.h +++ b/COFF/LTO.h @@ -1,9 +1,8 @@ //===- LTO.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 // //===----------------------------------------------------------------------===// // @@ -22,7 +21,9 @@ #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 <memory> #include <vector> @@ -43,13 +44,15 @@ public: BitcodeCompiler(); ~BitcodeCompiler(); - void add(BitcodeFile &F); + void add(BitcodeFile &f); std::vector<StringRef> compile(); private: - std::unique_ptr<llvm::lto::LTO> LTOObj; - std::vector<SmallString<0>> Buf; - std::vector<std::unique_ptr<MemoryBuffer>> Files; + std::unique_ptr<llvm::lto::LTO> ltoObj; + std::vector<SmallString<0>> buf; + std::vector<std::unique_ptr<MemoryBuffer>> files; + std::unique_ptr<llvm::raw_fd_ostream> indexFile; + llvm::DenseSet<StringRef> thinIndices; }; } } diff --git a/COFF/MapFile.cpp b/COFF/MapFile.cpp index fd4894250223..f98cf8fa6130 100644 --- a/COFF/MapFile.cpp +++ b/COFF/MapFile.cpp @@ -1,9 +1,8 @@ //===- MapFile.cpp --------------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// // @@ -24,7 +23,7 @@ #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; @@ -33,93 +32,93 @@ using namespace llvm::object; using namespace lld; using namespace lld::coff; -typedef DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>> - SymbolMapTy; +using SymbolMapTy = + DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>; -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<DefinedRegular *> getSymbols() { - std::vector<DefinedRegular *> V; - for (ObjFile *File : ObjFile::Instances) - for (Symbol *B : File->getSymbols()) - if (auto *Sym = dyn_cast_or_null<DefinedRegular>(B)) - if (Sym && !Sym->getCOFFSymbol().isSectionDefinition()) - V.push_back(Sym); - return V; + std::vector<DefinedRegular *> v; + for (ObjFile *file : ObjFile::instances) + for (Symbol *b : file->getSymbols()) + if (auto *sym = dyn_cast_or_null<DefinedRegular>(b)) + if (sym && !sym->getCOFFSymbol().isSectionDefinition()) + v.push_back(sym); + return v; } // Returns a map from sections to their symbols. -static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> Syms) { - SymbolMapTy Ret; - for (DefinedRegular *S : Syms) - Ret[S->getChunk()].push_back(S); +static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) { + SymbolMapTy ret; + for (DefinedRegular *s : syms) + ret[s->getChunk()].push_back(s); // Sort symbols by address. - for (auto &It : Ret) { - SmallVectorImpl<DefinedRegular *> &V = It.second; - std::sort(V.begin(), V.end(), [](DefinedRegular *A, DefinedRegular *B) { - return A->getRVA() < B->getRVA(); + for (auto &it : ret) { + SmallVectorImpl<DefinedRegular *> &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<DefinedRegular *, std::string> -getSymbolStrings(ArrayRef<DefinedRegular *> Syms) { - std::vector<std::string> 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<DefinedRegular *> syms) { + std::vector<std::string> 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<DefinedRegular *, std::string> Ret; - for (size_t I = 0, E = Syms.size(); I < E; ++I) - Ret[Syms[I]] = std::move(Str[I]); - return Ret; + DenseMap<DefinedRegular *, std::string> 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<OutputSection *> OutputSections) { - if (Config->MapFile.empty()) +void coff::writeMapFile(ArrayRef<OutputSection *> 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<DefinedRegular *> Syms = getSymbols(); - SymbolMapTy SectionSyms = getSectionSyms(Syms); - DenseMap<DefinedRegular *, std::string> SymStr = getSymbolStrings(Syms); + std::vector<DefinedRegular *> syms = getSymbols(); + SymbolMapTy sectionSyms = getSectionSyms(syms); + DenseMap<DefinedRegular *, std::string> 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<SectionChunk>(C); - if (!SC) + for (Chunk *c : sec->chunks) { + auto *sc = dyn_cast<SectionChunk>(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'; } } } diff --git a/COFF/MapFile.h b/COFF/MapFile.h index 0d0d68ce3ead..2bf01bd07285 100644 --- a/COFF/MapFile.h +++ b/COFF/MapFile.h @@ -1,9 +1,8 @@ //===- MapFile.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 // //===----------------------------------------------------------------------===// @@ -15,7 +14,7 @@ namespace lld { namespace coff { class OutputSection; -void writeMapFile(llvm::ArrayRef<OutputSection *> OutputSections); +void writeMapFile(llvm::ArrayRef<OutputSection *> outputSections); } } diff --git a/COFF/MarkLive.cpp b/COFF/MarkLive.cpp index 18b1c9c2529f..6d34cb864e3c 100644 --- a/COFF/MarkLive.cpp +++ b/COFF/MarkLive.cpp @@ -1,9 +1,8 @@ //===- MarkLive.cpp -------------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// @@ -16,57 +15,57 @@ 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<Chunk *> Chunks) { - ScopedTimer T(GCTimer); +void markLive(ArrayRef<Chunk *> chunks) { + ScopedTimer t(gctimer); // We build up a worklist of sections which have been marked as live. We only // push into the worklist when we discover an unmarked section, and we mark // as we push, so sections never appear twice in the list. - SmallVector<SectionChunk *, 256> Worklist; + SmallVector<SectionChunk *, 256> worklist; // COMDAT section chunks are dead by default. Add non-COMDAT chunks. - for (Chunk *C : Chunks) - if (auto *SC = dyn_cast<SectionChunk>(C)) - if (SC->Live) - Worklist.push_back(SC); + for (Chunk *c : chunks) + if (auto *sc = dyn_cast<SectionChunk>(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<DefinedRegular>(B)) - Enqueue(Sym->getChunk()); - else if (auto *Sym = dyn_cast<DefinedImportData>(B)) - Sym->File->Live = true; - else if (auto *Sym = dyn_cast<DefinedImportThunk>(B)) - Sym->WrappedSym->File->Live = Sym->WrappedSym->File->ThunkLive = true; + auto addSym = [&](Symbol *b) { + if (auto *sym = dyn_cast<DefinedRegular>(b)) + enqueue(sym->getChunk()); + else if (auto *sym = dyn_cast<DefinedImportData>(b)) + sym->file->live = true; + else if (auto *sym = dyn_cast<DefinedImportThunk>(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); } } diff --git a/COFF/MarkLive.h b/COFF/MarkLive.h index 5b652dd48196..e4e4c31c7c79 100644 --- a/COFF/MarkLive.h +++ b/COFF/MarkLive.h @@ -1,9 +1,8 @@ //===- MarkLive.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 // //===----------------------------------------------------------------------===// @@ -16,7 +15,9 @@ namespace lld { namespace coff { -void markLive(ArrayRef<Chunk *> Chunks); +class Chunk; + +void markLive(ArrayRef<Chunk *> chunks); } // namespace coff } // namespace lld diff --git a/COFF/MinGW.cpp b/COFF/MinGW.cpp index b2c8c4eadca4..2ca8ca0c058c 100644 --- a/COFF/MinGW.cpp +++ b/COFF/MinGW.cpp @@ -1,9 +1,8 @@ //===- MinGW.cpp ----------------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// @@ -19,8 +18,35 @@ 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_", @@ -32,12 +58,14 @@ void AutoExporter::initSymbolExcludes() { // 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", @@ -52,9 +80,9 @@ void AutoExporter::initSymbolExcludes() { "_DllEntryPoint@12", "_DllMainCRTStartup@12", }; - ExcludeSymbolPrefixes.insert("__head_"); + excludeSymbolPrefixes.insert("__head_"); } else { - ExcludeSymbols = { + excludeSymbols = { "__NULL_IMPORT_DESCRIPTOR", "_pei386_runtime_relocator", "do_pseudo_reloc", @@ -69,108 +97,70 @@ void AutoExporter::initSymbolExcludes() { "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<DefinedRegular>(Sym) && !isa<DefinedCommon>(Sym)) + if (!isa<DefinedRegular>(sym) && !isa<DefinedCommon>(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()); - - OS << "EXPORTS\n"; - for (Export &E : Config->Exports) { - OS << " " << E.ExportName << " " - << "@" << E.Ordinal; - if (auto *Def = dyn_cast_or_null<Defined>(E.Sym)) { - if (Def && Def->getChunk() && - !(Def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) - OS << " DATA"; +void coff::writeDefFile(StringRef name) { + std::error_code ec; + raw_fd_ostream os(name, ec, sys::fs::F_None); + if (ec) + fatal("cannot open " + name + ": " + ec.message()); + + os << "EXPORTS\n"; + for (Export &e : config->exports) { + os << " " << e.exportName << " " + << "@" << e.ordinal; + if (auto *def = dyn_cast_or_null<Defined>(e.sym)) { + if (def && def->getChunk() && + !(def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) + os << " DATA"; } - OS << "\n"; + os << "\n"; } } diff --git a/COFF/MinGW.h b/COFF/MinGW.h index f9c5e3e5c2cc..578a277561ad 100644 --- a/COFF/MinGW.h +++ b/COFF/MinGW.h @@ -1,9 +1,8 @@ //===- MinGW.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 // //===----------------------------------------------------------------------===// @@ -23,20 +22,18 @@ 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 diff --git a/COFF/Options.td b/COFF/Options.td index acf1bc5c8b1d..024b7be8f78d 100644 --- a/COFF/Options.td +++ b/COFF/Options.td @@ -3,11 +3,11 @@ include "llvm/Option/OptParser.td" // link.exe accepts options starting with either a dash or a slash. // Flag that takes no arguments. -class F<string name> : Flag<["/", "-", "-?"], name>; +class F<string name> : Flag<["/", "-", "/?", "-?"], name>; // Flag that takes one argument after ":". class P<string name, string help> : - Joined<["/", "-", "-?"], name#":">, HelpText<help>; + Joined<["/", "-", "/?", "-?"], name#":">, HelpText<help>; // Boolean flag which can be suffixed by ":no". Using it unsuffixed turns the // flag on and using it suffixed by ":no" turns it off. @@ -32,6 +32,9 @@ def errorlimit : P<"errorlimit", def export : P<"export", "Export a function">; // No help text because /failifmismatch is not intended to be used by the user. def failifmismatch : P<"failifmismatch", "">; +def 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">; @@ -64,7 +67,8 @@ 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<nodefaultlib>; +def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">, + Alias<nodefaultlib>; def manifest : F<"manifest">, HelpText<"Create .manifest file">; def manifest_colon : P< @@ -82,11 +86,11 @@ def manifestinput : P< // 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">; @@ -101,8 +105,12 @@ def noentry : F<"noentry">, def profile : F<"profile">; def repro : F<"Brepro">, HelpText<"Use a hash of the executable as the PE header timestamp">; -def swaprun_cd : F<"swaprun:cd">; -def swaprun_net : F<"swaprun:net">; +def swaprun : P<"swaprun", + "Comma-separated list of 'cd' or 'net'">; +def swaprun_cd : F<"swaprun:cd">, Alias<swaprun>, AliasArgs<["cd"]>, + HelpText<"Make loader run output binary from swap instead of from CD">; +def swaprun_net : F<"swaprun:net">, Alias<swaprun>, AliasArgs<["net"]>, + HelpText<"Make loader run output binary from swap instead of from network">; def verbose : F<"verbose">; def wholearchive_flag : F<"wholearchive">; @@ -112,6 +120,8 @@ 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)", @@ -147,37 +157,58 @@ defm tsaware : B<"tsaware", "Create non-Terminal Server aware executable">; def help : F<"help">; -def help_q : Flag<["/?", "-?"], "">, Alias<help>; + +// /?? and -?? must be before /? and -? to not confuse lib/Options. +def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>; // 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<string name> : Joined<["/", "-", "-?"], name#":">; +class QF<string name> : Joined<["/", "-", "/?", "-?"], name#":">; -multiclass QB<string name> { - def "" : F<name>; - def _no : F<name#":no">; -} - -def functionpadmin : F<"functionpadmin">; def ignoreidl : F<"ignoreidl">; def nologo : F<"nologo">; def throwingnew : F<"throwingnew">; diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp index 7862b6ce4cc5..a55e5136e040 100644 --- a/COFF/PDB.cpp +++ b/COFF/PDB.cpp @@ -1,21 +1,23 @@ //===- PDB.cpp ------------------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// #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" @@ -53,7 +55,6 @@ #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 <memory> @@ -65,44 +66,34 @@ 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<TypeIndex, 0> TPIMap; - SmallVector<TypeIndex, 0> 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(); @@ -110,10 +101,13 @@ public: /// 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<OutputSection *> 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. @@ -125,20 +119,17 @@ public: /// /// If the object does not use a type server PDB (compiled with /Z7), we merge /// all the type and item records from the .debug$S stream and fill in the - /// caller-provided ObjectIndexMap. - Expected<const CVIndexMap &> mergeDebugT(ObjFile *File, - CVIndexMap *ObjectIndexMap); + /// caller-provided objectIndexMap. + Expected<const CVIndexMap &> mergeDebugT(ObjFile *file, + CVIndexMap *objectIndexMap); /// Reads and makes available a PDB. - Expected<const CVIndexMap &> maybeMergeTypeServerPDB(ObjFile *File, - const CVType &FirstType); + Expected<const CVIndexMap &> 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<const CVIndexMap &> - mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType, - CVIndexMap *ObjectIndexMap); + Error mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *objectIndexMap); /// Reads and makes available a precompiled headers object. /// @@ -149,109 +140,87 @@ public: /// /// If the precompiled headers object was already loaded, this function will /// simply return its (remapped) TPI map. - Expected<const CVIndexMap &> aquirePrecompObj(ObjFile *File, - PrecompRecord Precomp); + Expected<const CVIndexMap &> aquirePrecompObj(ObjFile *file); /// Adds a precompiled headers object signature -> TPI mapping. std::pair<CVIndexMap &, bool /*already there*/> - registerPrecompiledHeaders(uint32_t Signature); + registerPrecompiledHeaders(uint32_t signature); - void mergeSymbolRecords(ObjFile *File, const CVIndexMap &IndexMap, - std::vector<ulittle32_t *> &StringTableRefs, - BinaryStreamRef SymData); + void mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, + std::vector<ulittle32_t *> &stringTableRefs, + BinaryStreamRef symData); /// Add the section map and section contributions to the PDB. - void addSections(ArrayRef<OutputSection *> OutputSections, - ArrayRef<uint8_t> 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; - } + void addSections(ArrayRef<OutputSection *> outputSections, + ArrayRef<uint8_t> sectionTable); /// Write the PDB to disk and store the Guid generated for it in *Guid. - void commit(codeview::GUID *Guid); - -private: - BumpPtrAllocator Alloc; - - SymbolTable *Symtab; + void commit(codeview::GUID *guid); - pdb::PDBFileBuilder Builder; + // Print statistics regarding the final PDB + void printStats(); - /// Type records that will go into the PDB TPI stream. - MergingTypeTableBuilder TypeTable; +private: + BumpPtrAllocator alloc; - /// Item records that will go into the PDB IPI stream. - MergingTypeTableBuilder IDTable; + SymbolTable *symtab; - /// Type records that will go into the PDB TPI stream (for /DEBUG:GHASH) - GlobalTypeTableBuilder GlobalTypeTable; + pdb::PDBFileBuilder builder; - /// Item records that will go into the PDB IPI stream (for /DEBUG:GHASH) - GlobalTypeTableBuilder GlobalIDTable; + TypeMerger tMerger; /// PDBs use a single global string table for filenames in the file checksum /// table. - DebugStringTableSubsection PDBStrTab; - - llvm::SmallString<128> NativePath; + DebugStringTableSubsection pdbStrTab; - /// A list of other PDBs which are loaded during the linking process and which - /// we need to keep around since the linking operation may reference pointers - /// inside of these PDBs. - llvm::SmallVector<std::unique_ptr<pdb::NativeSession>, 2> LoadedPDBs; + llvm::SmallString<128> nativePath; - std::vector<pdb::SecMapEntry> SectionMap; + std::vector<pdb::SecMapEntry> sectionMap; /// Type index mappings of type server PDBs that we've loaded so far. - std::map<codeview::GUID, CVIndexMap> TypeServerIndexMappings; + std::map<codeview::GUID, CVIndexMap> typeServerIndexMappings; /// Type index mappings of precompiled objects type map that we've loaded so /// far. - std::map<uint32_t, CVIndexMap> PrecompTypeIndexMappings; + std::map<uint32_t, CVIndexMap> precompTypeIndexMappings; - /// List of TypeServer PDBs which cannot be loaded. - /// Cached to prevent repeated load attempts. - std::map<codeview::GUID, std::string> 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<DebugFrameDataSubsectionRef> NewFpoFrames; + std::vector<DebugFrameDataSubsectionRef> 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 @@ -259,13 +228,17 @@ class DebugSHandler { /// 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<ulittle32_t *> StringTableReferences; + std::vector<ulittle32_t *> 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); + + std::shared_ptr<DebugInlineeLinesSubsection> + mergeInlineeLines(DebugChecksumsSubsection *newChecksums); - void handleDebugS(lld::coff::SectionChunk &DebugS); void finish(); }; } @@ -273,7 +246,7 @@ public: // 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<char> &FileName) { +static void pdbMakeAbsolute(SmallVectorImpl<char> &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 @@ -281,408 +254,256 @@ static void pdbMakeAbsolute(SmallVectorImpl<char> &FileName) { // 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); -} - -static SectionChunk *findByName(ArrayRef<SectionChunk *> Sections, - StringRef Name) { - for (SectionChunk *C : Sections) - if (C->getSectionName() == Name) - return C; - return nullptr; -} -static ArrayRef<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> 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<uint8_t> getDebugSection(ObjFile *File, StringRef SecName) { - if (SectionChunk *Sec = findByName(File->getDebugChunks(), SecName)) - return consumeDebugMagic(Sec->getContents(), SecName); - return {}; + // 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); + + fileName = std::move(absoluteFileName); } // A COFF .debug$H section is currently a clang extension. This function checks // if a .debug$H section is in a format that we expect / understand, so that we // can ignore any sections which are coincidentally also named .debug$H but do // not contain a format we recognize. -static bool canUseDebugH(ArrayRef<uint8_t> DebugH) { - if (DebugH.size() < sizeof(object::debug_h_header)) +static bool canUseDebugH(ArrayRef<uint8_t> debugH) { + if (debugH.size() < sizeof(object::debug_h_header)) return false; - auto *Header = - reinterpret_cast<const object::debug_h_header *>(DebugH.data()); - DebugH = DebugH.drop_front(sizeof(object::debug_h_header)); - return Header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC && - Header->Version == 0 && - Header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) && - (DebugH.size() % 8 == 0); + auto *header = + reinterpret_cast<const object::debug_h_header *>(debugH.data()); + debugH = debugH.drop_front(sizeof(object::debug_h_header)); + return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC && + header->Version == 0 && + header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) && + (debugH.size() % 8 == 0); } -static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *File) { - SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$H"); - if (!Sec) +static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *file) { + SectionChunk *sec = + SectionChunk::findByName(file->getDebugChunks(), ".debug$H"); + if (!sec) return llvm::None; - ArrayRef<uint8_t> Contents = Sec->getContents(); - if (!canUseDebugH(Contents)) + ArrayRef<uint8_t> contents = sec->getContents(); + if (!canUseDebugH(contents)) return None; - return Contents; + return contents; } static ArrayRef<GloballyHashedType> -getHashesFromDebugH(ArrayRef<uint8_t> DebugH) { - assert(canUseDebugH(DebugH)); +getHashesFromDebugH(ArrayRef<uint8_t> debugH) { + assert(canUseDebugH(debugH)); - DebugH = DebugH.drop_front(sizeof(object::debug_h_header)); - uint32_t Count = DebugH.size() / sizeof(GloballyHashedType); - return {reinterpret_cast<const GloballyHashedType *>(DebugH.data()), Count}; + debugH = debugH.drop_front(sizeof(object::debug_h_header)); + uint32_t count = debugH.size() / sizeof(GloballyHashedType); + return {reinterpret_cast<const GloballyHashedType *>(debugH.data()), count}; } -static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder, - 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); - }); -} - -// 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"; + tpiBuilder.addTypeRecord(type.RecordData, *hash); }); - if (!DbgIt) - return 0; - - ArrayRef<uint8_t> 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<CVSymbol> Sym = readSymbolFromStream(SS.getRecordData(), 0); - if (!Sym) { - consumeError(Sym.takeError()); - continue; - } - if (auto ObjName = SymbolDeserializer::deserializeAs<ObjNameSym>(Sym.get())) - return ObjName->Signature; - } - return 0; } Expected<const CVIndexMap &> -PDBLinker::mergeDebugT(ObjFile *File, CVIndexMap *ObjectIndexMap) { - ScopedTimer T(TypeMergingTimer); +PDBLinker::mergeDebugT(ObjFile *file, CVIndexMap *objectIndexMap) { + ScopedTimer t(typeMergingTimer); - bool IsPrecompiledHeader = false; - - ArrayRef<uint8_t> 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 + if (!file->debugTypesObj) + return *objectIndexMap; // no Types stream // 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(); - - // 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())); + Error e = mergeInPrecompHeaderObj(file, objectIndexMap); + if (e) + return std::move(e); + + // 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<GloballyHashedType> Hashes; - std::vector<GloballyHashedType> OwnedHashes; - if (Optional<ArrayRef<uint8_t>> DebugH = getDebugH(File)) - Hashes = getHashesFromDebugH(*DebugH); + if (config->debugGHashes) { + ArrayRef<GloballyHashedType> hashes; + std::vector<GloballyHashedType> ownedHashes; + if (Optional<ArrayRef<uint8_t>> debugH = getDebugH(file)) + hashes = getHashesFromDebugH(*debugH); else { - OwnedHashes = GloballyHashedType::hashTypes(Types); - Hashes = OwnedHashes; + 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<std::unique_ptr<pdb::NativeSession>> -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())); - - ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFile( - TSPath, /*FileSize=*/-1, /*RequiresNullTerminator=*/false); - if (!MBOrErr) - return errorCodeToError(MBOrErr.getError()); - - std::unique_ptr<pdb::IPDBSession> ThisSession; - if (auto EC = pdb::NativeSession::createFromPdb( - MemoryBuffer::getMemBuffer(Driver->takeBuffer(std::move(*MBOrErr)), - /*RequiresNullTerminator=*/false), - ThisSession)) - return std::move(EC); - - std::unique_ptr<pdb::NativeSession> NS( - static_cast<pdb::NativeSession *>(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::PDBError>(pdb::pdb_error_code::signature_out_of_date); - - return std::move(NS); -} +Expected<const CVIndexMap &> PDBLinker::maybeMergeTypeServerPDB(ObjFile *file) { + Expected<llvm::pdb::NativeSession *> pdbSession = findTypeServerSource(file); + if (!pdbSession) + return pdbSession.takeError(); -Expected<const CVIndexMap &> -PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, const CVType &FirstType) { - TypeServer2Record TS; - if (auto EC = - TypeDeserializer::deserializeAs(const_cast<CVType &>(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<StringError>(PrevErr->second, inconvertibleErrorCode())); + pdb::PDBFile &pdbFile = pdbSession.get()->getPDBFile(); + pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream()); - // 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; + auto it = typeServerIndexMappings.emplace(info.getGuid(), CVIndexMap()); + CVIndexMap &indexMap = it.first->second; + if (!it.second) + return indexMap; // already merged // Mark this map as a type server map. - 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<ECError> 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)); + indexMap.isTypeServerMap = true; + + Expected<pdb::TpiStream &> 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<pdb::TpiStream &> 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<uint32_t> EndPrecomp; + auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray()); + Optional<uint32_t> 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<const CVIndexMap &> -PDBLinker::mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType, - CVIndexMap *ObjectIndexMap) { - PrecompRecord Precomp; - if (auto EC = TypeDeserializer::deserializeAs(const_cast<CVType &>(FirstType), - Precomp)) - fatal("error reading record: " + toString(std::move(EC))); +Error PDBLinker::mergeInPrecompHeaderObj(ObjFile *file, + CVIndexMap *objectIndexMap) { + const PrecompRecord &precomp = + retrieveDependencyInfo<PrecompRecord>(file->debugTypesObj); - auto E = aquirePrecompObj(File, Precomp); - if (!E) - return E.takeError(); + Expected<const CVIndexMap &> 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) { @@ -694,101 +515,103 @@ static bool equals_path(StringRef path1, StringRef path2) { } // 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<CVIndexMap &, bool /*already there*/> -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<const CVIndexMap &> -PDBLinker::aquirePrecompObj(ObjFile *File, PrecompRecord Precomp) { +Expected<const CVIndexMap &> PDBLinker::aquirePrecompObj(ObjFile *file) { + const PrecompRecord &precomp = + retrieveDependencyInfo<PrecompRecord>(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::PDBError>(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::PDBError>(pdb::pdb_error_code::signature_out_of_date)); - return IndexMap; + return indexMap; } -static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) { - if (TI.isSimple()) +static bool remapTypeIndex(TypeIndex &ti, ArrayRef<TypeIndex> 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<uint8_t> RecordBytes, - const CVIndexMap &IndexMap, - ArrayRef<TiReference> TypeRefs) { - MutableArrayRef<uint8_t> 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<uint8_t> recordBytes, + const CVIndexMap &indexMap, + ArrayRef<TiReference> typeRefs) { + MutableArrayRef<uint8_t> 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<TypeIndex> TypeOrItemMap = IndexMap.TPIMap; - bool IsItemIndex = Ref.Kind == TiRefKind::IndexRef; - if (IsItemIndex && IndexMap.IsTypeServerMap) - TypeOrItemMap = IndexMap.IPIMap; - - MutableArrayRef<TypeIndex> TIs( - reinterpret_cast<TypeIndex *>(Contents.data() + Ref.Offset), Ref.Count); - for (TypeIndex &TI : TIs) { - if (!remapTypeIndex(TI, TypeOrItemMap)) { - log("ignoring symbol record of kind 0x" + utohexstr(SymKind) + " in " + - File->getName() + " with bad " + (IsItemIndex ? "item" : "type") + - " index 0x" + utohexstr(TI.getIndex())); - TI = TypeIndex(SimpleTypeKind::NotTranslated); + ArrayRef<TypeIndex> typeOrItemMap = indexMap.tpiMap; + bool isItemIndex = ref.Kind == TiRefKind::IndexRef; + if (isItemIndex && indexMap.isTypeServerMap) + typeOrItemMap = indexMap.ipiMap; + + MutableArrayRef<TypeIndex> tIs( + reinterpret_cast<TypeIndex *>(contents.data() + ref.Offset), ref.Count); + for (TypeIndex &ti : tIs) { + if (!remapTypeIndex(ti, typeOrItemMap)) { + log("ignoring symbol record of kind 0x" + utohexstr(symKind) + " in " + + file->getName() + " with bad " + (isItemIndex ? "item" : "type") + + " index 0x" + utohexstr(ti.getIndex())); + ti = TypeIndex(SimpleTypeKind::NotTranslated); continue; } } @@ -796,26 +619,26 @@ static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind, } static void -recordStringTableReferenceAtOffset(MutableArrayRef<uint8_t> Contents, - uint32_t Offset, - std::vector<ulittle32_t *> &StrTableRefs) { - Contents = - Contents.drop_front(Offset).take_front(sizeof(support::ulittle32_t)); - ulittle32_t *Index = reinterpret_cast<ulittle32_t *>(Contents.data()); - StrTableRefs.push_back(Index); +recordStringTableReferenceAtOffset(MutableArrayRef<uint8_t> contents, + uint32_t offset, + std::vector<ulittle32_t *> &strTableRefs) { + contents = + contents.drop_front(offset).take_front(sizeof(support::ulittle32_t)); + ulittle32_t *index = reinterpret_cast<ulittle32_t *>(contents.data()); + strTableRefs.push_back(index); } static void -recordStringTableReferences(SymbolKind Kind, MutableArrayRef<uint8_t> Contents, - std::vector<ulittle32_t *> &StrTableRefs) { +recordStringTableReferences(SymbolKind kind, MutableArrayRef<uint8_t> contents, + std::vector<ulittle32_t *> &strTableRefs) { // For now we only handle S_FILESTATIC, but we may need the same logic for // S_DEFRANGE and S_DEFRANGE_SUBFIELD. However, I cannot seem to generate any // PDBs that contain these types of records, so because of the uncertainty // they are omitted here until we can prove that it's necessary. - switch (Kind) { + 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: @@ -827,21 +650,21 @@ recordStringTableReferences(SymbolKind Kind, MutableArrayRef<uint8_t> Contents, } } -static SymbolKind symbolKind(ArrayRef<uint8_t> RecordData) { - const RecordPrefix *Prefix = - reinterpret_cast<const RecordPrefix *>(RecordData.data()); - return static_cast<SymbolKind>(uint16_t(Prefix->RecordKind)); +static SymbolKind symbolKind(ArrayRef<uint8_t> recordData) { + const RecordPrefix *prefix = + reinterpret_cast<const RecordPrefix *>(recordData.data()); + return static_cast<SymbolKind>(uint16_t(prefix->RecordKind)); } /// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32 -static void translateIdSymbols(MutableArrayRef<uint8_t> &RecordData, - TypeCollection &IDTable) { - RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(RecordData.data()); +static void translateIdSymbols(MutableArrayRef<uint8_t> &recordData, + TypeCollection &iDTable) { + RecordPrefix *prefix = reinterpret_cast<RecordPrefix *>(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; } @@ -850,89 +673,89 @@ static void translateIdSymbols(MutableArrayRef<uint8_t> &RecordData, // to the PDB file's ID stream index space, but we need to convert this to a // symbol that refers to the type stream index space. So we remap again from // ID index space to type index space. - if (Kind == SymbolKind::S_GPROC32_ID || Kind == SymbolKind::S_LPROC32_ID) { - SmallVector<TiReference, 1> Refs; - auto Content = RecordData.drop_front(sizeof(RecordPrefix)); - CVSymbol Sym(Kind, RecordData); - discoverTypeIndicesInSymbol(Sym, Refs); - assert(Refs.size() == 1); - assert(Refs.front().Count == 1); - - TypeIndex *TI = - reinterpret_cast<TypeIndex *>(Content.data() + Refs[0].Offset); - // `TI` is the index of a FuncIdRecord or MemberFuncIdRecord which lives in + if (kind == SymbolKind::S_GPROC32_ID || kind == SymbolKind::S_LPROC32_ID) { + SmallVector<TiReference, 1> 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<TypeIndex *>(content.data() + refs[0].Offset); + // `ti` is the index of a FuncIdRecord or MemberFuncIdRecord which lives in // the IPI stream, whose `FunctionType` member refers to the TPI stream. // Note that LF_FUNC_ID and LF_MEMFUNC_ID have the same record layout, and // in both cases we just need the second type index. - if (!TI->isSimple() && !TI->isNoneType()) { - CVType FuncIdData = IDTable.getType(*TI); - SmallVector<TypeIndex, 2> Indices; - discoverTypeIndices(FuncIdData, Indices); - assert(Indices.size() == 2); - *TI = Indices[1]; + if (!ti->isSimple() && !ti->isNoneType()) { + CVType funcIdData = iDTable.getType(*ti); + SmallVector<TypeIndex, 2> indices; + discoverTypeIndices(funcIdData, indices); + assert(indices.size() == 2); + *ti = indices[1]; } - Kind = (Kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32 + 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<uint8_t> -copyAndAlignSymbol(const CVSymbol &Sym, MutableArrayRef<uint8_t> &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<uint8_t> &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<uint8_t> 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<uint8_t> 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<RecordPrefix *>(NewData.data()); - Prefix->RecordLen = Size - 2; - return NewData; + auto *prefix = reinterpret_cast<RecordPrefix *>(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<SymbolScope> &Stack, - uint32_t CurOffset, CVSymbol &Sym) { - assert(symbolOpensScope(Sym.kind())); - SymbolScope S; - S.ScopeOffset = CurOffset; - S.OpeningRecord = const_cast<ScopeRecord *>( - reinterpret_cast<const ScopeRecord *>(Sym.content().data())); - S.OpeningRecord->PtrParent = Stack.empty() ? 0 : Stack.back().ScopeOffset; - Stack.push_back(S); +static void scopeStackOpen(SmallVectorImpl<SymbolScope> &stack, + uint32_t curOffset, CVSymbol &sym) { + assert(symbolOpensScope(sym.kind())); + SymbolScope s; + s.scopeOffset = curOffset; + s.openingRecord = const_cast<ScopeRecord *>( + reinterpret_cast<const ScopeRecord *>(sym.content().data())); + s.openingRecord->ptrParent = stack.empty() ? 0 : stack.back().scopeOffset; + stack.push_back(s); } -static void scopeStackClose(SmallVectorImpl<SymbolScope> &Stack, - uint32_t CurOffset, ObjFile *File) { - if (Stack.empty()) { - warn("symbol scopes are not balanced in " + File->getName()); +static void scopeStackClose(SmallVectorImpl<SymbolScope> &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 @@ -944,7 +767,7 @@ static bool symbolGoesInModuleStream(const CVSymbol &Sym, bool IsGlobalScope) { 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: @@ -952,8 +775,8 @@ static bool symbolGoesInModuleStream(const CVSymbol &Sym, bool IsGlobalScope) { } } -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. @@ -968,36 +791,36 @@ static bool symbolGoesInGlobalsStream(const CVSymbol &Sym, bool IsGlobalScope) { 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: @@ -1005,229 +828,304 @@ static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, uint16_t ModIndex, } } -void PDBLinker::mergeSymbolRecords(ObjFile *File, const CVIndexMap &IndexMap, - std::vector<ulittle32_t *> &StringTableRefs, - BinaryStreamRef SymData) { - ArrayRef<uint8_t> SymsBuffer; - cantFail(SymData.readBytes(0, SymData.getLength(), SymsBuffer)); - SmallVector<SymbolScope, 4> Scopes; +void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, + std::vector<ulittle32_t *> &stringTableRefs, + BinaryStreamRef symData) { + ArrayRef<uint8_t> symsBuffer; + cantFail(symData.readBytes(0, symData.getLength(), symsBuffer)); + SmallVector<SymbolScope, 4> 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<CVSymbol>( - 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<CVSymbol>( + 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<uint8_t> AlignedSymbolMem; - if (NeedsRealignment) { - void *AlignedData = - Alloc.Allocate(TotalRealignedSize, alignOf(CodeViewContainer::Pdb)); - AlignedSymbolMem = makeMutableArrayRef( - reinterpret_cast<uint8_t *>(AlignedData), TotalRealignedSize); + MutableArrayRef<uint8_t> alignedSymbolMem; + if (needsRealignment) { + void *alignedData = + alloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb)); + alignedSymbolMem = makeMutableArrayRef( + reinterpret_cast<uint8_t *>(alignedData), totalRealignedSize); } // Iterate again, this time doing the real work. - unsigned CurSymOffset = File->ModuleDBI->getNextSymbolOffset(); - ArrayRef<uint8_t> BulkSymbols; + unsigned curSymOffset = file->moduleDBI->getNextSymbolOffset(); + ArrayRef<uint8_t> bulkSymbols; cantFail(forEachCodeViewRecord<CVSymbol>( - SymsBuffer, [&](CVSymbol Sym) -> llvm::Error { + symsBuffer, [&](CVSymbol sym) -> llvm::Error { // Align the record if required. - MutableArrayRef<uint8_t> RecordBytes; - if (NeedsRealignment) { - RecordBytes = copyAndAlignSymbol(Sym, AlignedSymbolMem); - Sym = CVSymbol(Sym.kind(), RecordBytes); + MutableArrayRef<uint8_t> 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<uint8_t *>(Sym.data().data()), Sym.length()); + recordBytes = makeMutableArrayRef( + const_cast<uint8_t *>(sym.data().data()), sym.length()); } // Discover type index references in the record. Skip it if we don't // know where they are. - SmallVector<TiReference, 32> TypeRefs; - if (!discoverTypeIndicesInSymbol(Sym, TypeRefs)) { + SmallVector<TiReference, 32> 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<uint8_t> relocateDebugChunk(BumpPtrAllocator &Alloc, - SectionChunk &DebugChunk) { - uint8_t *Buffer = Alloc.Allocate<uint8_t>(DebugChunk.getSize()); - assert(DebugChunk.OutputSectionOff == 0 && +static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &alloc, + SectionChunk &debugChunk) { + uint8_t *buffer = alloc.Allocate<uint8_t>(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<SectionChunk>(C)) { - SC.Characteristics = SecChunk->Header->Characteristics; - SC.Imod = SecChunk->File->ModuleDBI->getModuleIndex(); - ArrayRef<uint8_t> Contents = SecChunk->getContents(); - JamCRC CRC(0); - ArrayRef<char> CharContents = makeArrayRef( - reinterpret_cast<const char *>(Contents.data()), Contents.size()); - CRC.update(CharContents); - SC.DataCrc = CRC.getCRC(); +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<SectionChunk>(c)) { + sc.Characteristics = secChunk->header->Characteristics; + sc.Imod = secChunk->file->moduleDBI->getModuleIndex(); + ArrayRef<uint8_t> contents = secChunk->getContents(); + JamCRC crc(0); + ArrayRef<char> charContents = makeArrayRef( + reinterpret_cast<const char *>(contents.data()), contents.size()); + crc.update(charContents); + sc.DataCrc = crc.getCRC(); } else { - SC.Characteristics = OS->Header.Characteristics; - // FIXME: When we start creating DBI for import libraries, use those here. - SC.Imod = Modi; + sc.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<uint8_t> relocatedDebugContents = SectionChunk::consumeDebugMagic( + relocateDebugChunk(linker.alloc, debugS), debugS.getSectionName()); - ArrayRef<uint8_t> RelocatedDebugContents = 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) { + // 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; - for (const DebugSubsectionRecord &SS : Subsections) { - switch (SS.kind()) { + 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<StringRef> +getFileName(const DebugStringTableSubsectionRef &strings, + const DebugChecksumsSubsectionRef &checksums, uint32_t fileID) { + auto iter = checksums.getArray().at(fileID); + if (iter == checksums.getArray().end()) + return make_error<CodeViewError>(cv_error_code::no_records); + uint32_t offset = iter->FileNameOffset; + return strings.getString(offset); +} + +std::shared_ptr<DebugInlineeLinesSubsection> +DebugSHandler::mergeInlineeLines(DebugChecksumsSubsection *newChecksums) { + auto newInlineeLines = std::make_shared<DebugInlineeLinesSubsection>( + *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<TypeIndex> 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; @@ -1235,186 +1133,231 @@ void DebugSHandler::finish() { // 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<DebugChecksumsSubsection>(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<DebugChecksumsSubsection>(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); - - 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<SectionChunk>(C); - if (!SecChunk || !SecChunk->Live) - continue; - pdb::SectionContrib SC = createSectionContrib(SecChunk, Modi); - File->ModuleDBI->setFirstSectionContrib(SC); - break; - } + file->mergedIntoPDB = true; // 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<uint8_t> RelocatedDebugContents = - relocateDebugChunk(Alloc, *DebugChunk); + if (debugChunk->getSectionName() == ".debug$F") { + ArrayRef<uint8_t> relocatedDebugContents = + relocateDebugChunk(alloc, *debugChunk); - FixedStreamArray<object::FpoData> FpoRecords; - BinaryStreamReader Reader(RelocatedDebugContents, support::little); - uint32_t Count = RelocatedDebugContents.size() / sizeof(object::FpoData); - ExitOnErr(Reader.readArray(FpoRecords, Count)); + FixedStreamArray<object::FpoData> 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<DefinedCOFF>(Def)) { - if (D->getCOFFSymbol().isFunctionDefinition()) - Pub.Flags = PublicSymFlags::Function; - } else if (isa<DefinedImportThunk>(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<Chunk *> chunks = file->getChunks(); + uint32_t modi = file->moduleDBI->getModuleIndex(); + + for (Chunk *c : chunks) { + auto *secChunk = dyn_cast<SectionChunk>(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<DefinedCOFF>(def)) { + if (d->getCOFFSymbol().isFunctionDefinition()) + pub.Flags = PublicSymFlags::Function; + } else if (isa<DefinedImportThunk>(def)) { + pub.Flags = PublicSymFlags::Function; + } + + OutputSection *os = def->getChunk()->getOutputSection(); + assert(os && "all publics should be in final image"); + pub.Offset = def->getRVA() - os->getRVA(); + pub.Segment = os->sectionIndex; + return pub; } // Add all object files to the PDB. Merge .debug$T sections into IpiData and // TpiData. void PDBLinker::addObjectsToPDB() { - ScopedTimer T1(AddObjectsTimer); - for (ObjFile *File : ObjFile::Instances) - addObjFile(File); + ScopedTimer t1(addObjectsTimer); + + createModuleDBI(builder); - Builder.getStringTableBuilder().setStrings(PDBStrTab); - T1.stop(); + 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<PublicSym32> Publics; - Symtab->forEachSymbol([&Publics](Symbol *S) { + auto &gsiBuilder = builder.getGsiBuilder(); + std::vector<PublicSym32> publics; + symtab->forEachSymbol([&publics](Symbol *s) { // Only emit defined, live symbols that have a chunk. - auto *Def = dyn_cast<Defined>(S); - if (Def && Def->isLive() && Def->getChunk()) - Publics.push_back(createPublic(Def)); + auto *def = dyn_cast<Defined>(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<std::unique_ptr<MemoryBuffer>> DataOrErr = - MemoryBuffer::getFile(File); - if (!DataOrErr) { - warn("Cannot open input file: " + File); + for (StringRef file : config->natvisFiles) { + ErrorOr<std::unique_ptr<MemoryBuffer>> dataOrErr = + MemoryBuffer::getFile(file); + if (!dataOrErr) { + warn("Cannot open input file: " + file); continue; } - Builder.addInjectedSource(File, std::move(*DataOrErr)); + 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: @@ -1433,201 +1376,329 @@ static codeview::CPUType toCodeViewMachine(COFF::MachineTypes Machine) { // 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<StringRef> 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<StringRef, 4> S; - A.split(S, '"'); - R.append(join(S, "\"\"")); +static std::string quote(ArrayRef<StringRef> 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<StringRef, 4> 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); - - ArrayRef<StringRef> Args = makeArrayRef(Config->Argv).drop_front(); - std::string ArgStr = quote(Args); - EBS.Fields.push_back("cwd"); + cs.VersionFrontendBuild = 0; + cs.VersionFrontendMajor = 0; + cs.VersionFrontendMinor = 0; + cs.VersionFrontendQFE = 0; + cs.Version = "LLVM Linker"; + cs.setLanguage(SourceLanguage::Link); +} + +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<StringRef> 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 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); + } } -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)); +// Add all import files as modules to the PDB. +void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) { + if (ImportFile::instances.empty()) + return; + + std::map<std::string, llvm::pdb::DbiModuleDescriptorBuilder *> 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<DefinedImportThunk>(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<SymbolScope, 4> 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<OutputSection *> OutputSections, - ArrayRef<uint8_t> SectionTable, - llvm::codeview::DebugInfo *BuildId) { - ScopedTimer T1(TotalPdbLinkTimer); - PDBLinker PDB(Symtab); - - PDB.initialize(BuildId); - PDB.addObjectsToPDB(); - PDB.addSections(OutputSections, SectionTable); - PDB.addNatvisFiles(); - - ScopedTimer T2(DiskCommitTimer); - codeview::GUID Guid; - PDB.commit(&Guid); - memcpy(&BuildId->PDB70.Signature, &Guid, 16); +void coff::createPDB(SymbolTable *symtab, + ArrayRef<OutputSection *> outputSections, + ArrayRef<uint8_t> sectionTable, + llvm::codeview::DebugInfo *buildId) { + ScopedTimer t1(totalPdbLinkTimer); + PDBLinker pdb(symtab); + + 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); + + 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<OutputSection *> OutputSections, - ArrayRef<uint8_t> SectionTable) { +void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections, + ArrayRef<uint8_t> sectionTable) { // It's not entirely clear what this is, but the * Linker * module uses it. - pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder(); - 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<object::coff_section> Sections = { - (const object::coff_section *)SectionTable.data(), - SectionTable.size() / sizeof(object::coff_section)}; - SectionMap = pdb::DbiStreamBuilder::createSectionMap(Sections); - DbiBuilder.setSectionMap(SectionMap); + ArrayRef<object::coff_section> 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)); -} - -static Expected<StringRef> -getFileName(const DebugStringTableSubsectionRef &Strings, - const DebugChecksumsSubsectionRef &Checksums, uint32_t FileID) { - auto Iter = Checksums.getArray().at(FileID); - if (Iter == Checksums.getArray().end()) - return make_error<CodeViewError>(cv_error_code::no_records); - uint32_t Offset = Iter->FileNameOffset; - return Strings.getString(Offset); + exitOnErr(builder.commit(config->pdbPath, guid)); } static uint32_t getSecrelReloc() { - switch (Config->Machine) { + switch (config->machine) { case AMD64: return COFF::IMAGE_REL_AMD64_SECREL; case I386: @@ -1646,78 +1717,78 @@ static uint32_t getSecrelReloc() { // that are used to interpret the line table, and the offset of Addr in the line // table are stored in the output arguments. Returns whether a line table was // found. -static bool findLineTable(const SectionChunk *C, uint32_t Addr, - DebugStringTableSubsectionRef &CVStrTab, - DebugChecksumsSubsectionRef &Checksums, - DebugLinesSubsectionRef &Lines, - uint32_t &OffsetInLinetable) { - ExitOnError ExitOnErr; - uint32_t SecrelReloc = getSecrelReloc(); - - for (SectionChunk *DbgC : C->File->getDebugChunks()) { - if (DbgC->getSectionName() != ".debug$S") +static bool findLineTable(const SectionChunk *c, uint32_t addr, + DebugStringTableSubsectionRef &cVStrTab, + DebugChecksumsSubsectionRef &checksums, + DebugLinesSubsectionRef &lines, + uint32_t &offsetInLinetable) { + ExitOnError exitOnErr; + uint32_t secrelReloc = getSecrelReloc(); + + for (SectionChunk *dbgC : c->file->getDebugChunks()) { + if (dbgC->getSectionName() != ".debug$S") continue; - // Build a mapping of SECREL relocations in DbgC that refer to C. - DenseMap<uint32_t, uint32_t> Secrels; - for (const coff_relocation &R : DbgC->Relocs) { - if (R.Type != SecrelReloc) + // Build a mapping of SECREL relocations in dbgC that refer to `c`. + DenseMap<uint32_t, uint32_t> secrels; + for (const coff_relocation &r : dbgC->getRelocs()) { + if (r.Type != secrelReloc) continue; - if (auto *S = dyn_cast_or_null<DefinedRegular>( - C->File->getSymbols()[R.SymbolTableIndex])) - if (S->getChunk() == C) - Secrels[R.VirtualAddress] = S->getValue(); + if (auto *s = dyn_cast_or_null<DefinedRegular>( + c->file->getSymbols()[r.SymbolTableIndex])) + if (s->getChunk() == c) + secrels[r.VirtualAddress] = s->getValue(); } - ArrayRef<uint8_t> Contents = - consumeDebugMagic(DbgC->getContents(), ".debug$S"); - DebugSubsectionArray Subsections; - BinaryStreamReader Reader(Contents, support::little); - ExitOnErr(Reader.readArray(Subsections, Contents.size())); + ArrayRef<uint8_t> 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<uint8_t> Bytes; - auto Ref = SS.getRecordData(); - ExitOnErr(Ref.readLongestContiguousChunk(0, Bytes)); - size_t OffsetInDbgC = Bytes.data() - DbgC->getContents().data(); + ArrayRef<uint8_t> bytes; + auto ref = ss.getRecordData(); + exitOnErr(ref.readLongestContiguousChunk(0, bytes)); + size_t offsetInDbgC = bytes.data() - dbgC->getContents().data(); // Check whether this line table refers to C. - auto I = Secrels.find(OffsetInDbgC); - if (I == Secrels.end()) + 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; } } @@ -1728,38 +1799,38 @@ static bool findLineTable(const SectionChunk *C, uint32_t Addr, // Use CodeView line tables to resolve a file and line number for the given // offset into the given chunk and return them, or {"", 0} if a line table was // not found. -std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *C, - uint32_t Addr) { - ExitOnError ExitOnErr; +std::pair<StringRef, uint32_t> 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<uint32_t> NameIndex; - Optional<uint32_t> 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<uint32_t> nameIndex; + Optional<uint32_t> 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}; } diff --git a/COFF/PDB.h b/COFF/PDB.h index ea7a9996f415..3ac1adc85c5d 100644 --- a/COFF/PDB.h +++ b/COFF/PDB.h @@ -1,9 +1,8 @@ //===- PDB.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 // //===----------------------------------------------------------------------===// @@ -25,13 +24,13 @@ class OutputSection; class SectionChunk; class SymbolTable; -void createPDB(SymbolTable *Symtab, - llvm::ArrayRef<OutputSection *> OutputSections, - llvm::ArrayRef<uint8_t> SectionTable, - llvm::codeview::DebugInfo *BuildId); +void createPDB(SymbolTable *symtab, + llvm::ArrayRef<OutputSection *> outputSections, + llvm::ArrayRef<uint8_t> sectionTable, + llvm::codeview::DebugInfo *buildId); -std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *C, - uint32_t Addr); +std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *c, + uint32_t addr); } } diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp index 1a9e0455dc1d..0aff164ee567 100644 --- a/COFF/SymbolTable.cpp +++ b/COFF/SymbolTable.cpp @@ -1,9 +1,8 @@ //===- SymbolTable.cpp ----------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// @@ -17,6 +16,7 @@ #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 <utility> @@ -26,521 +26,576 @@ 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<ObjFile>(File)) { - ObjFile::Instances.push_back(F); - } else if (auto *F = dyn_cast<BitcodeFile>(File)) { - BitcodeFile::Instances.push_back(F); - } else if (auto *F = dyn_cast<ImportFile>(File)) { - ImportFile::Instances.push_back(F); + if (auto *f = dyn_cast<ObjFile>(file)) { + ObjFile::instances.push_back(f); + } else if (auto *f = dyn_cast<BitcodeFile>(file)) { + BitcodeFile::instances.push_back(f); + } else if (auto *f = dyn_cast<ImportFile>(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<DefinedRegular>(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<DefinedRegular>(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<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) { struct Location { - Symbol *Sym; - std::pair<StringRef, uint32_t> FileLine; + Symbol *sym; + std::pair<StringRef, uint32_t> fileLine; }; - std::vector<Location> Locations; + std::vector<Location> locations; - for (Chunk *C : File->getChunks()) { - auto *SC = dyn_cast<SectionChunk>(C); - if (!SC) + for (Chunk *c : file->getChunks()) { + auto *sc = dyn_cast<SectionChunk>(c); + if (!sc) continue; - for (const coff_relocation &R : SC->Relocs) { - if (R.SymbolTableIndex != SymIndex) + for (const coff_relocation &r : sc->getRelocs()) { + if (r.SymbolTableIndex != symIndex) continue; - std::pair<StringRef, uint32_t> FileLine = - getFileLine(SC, R.VirtualAddress); - Symbol *Sym = getSymbol(SC, R.VirtualAddress); - if (!FileLine.first.empty() || Sym) - Locations.push_back({Sym, FileLine}); + std::pair<StringRef, uint32_t> 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<std::string>({"\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<std::string> 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 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<File> 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<std::string> symbolLocations = + getSymbolLocations(ref.oFile, ref.symIndex); + numRefs += symbolLocations.size(); + for (const std::string &s : symbolLocations) { + if (i >= maxUndefReferences) + break; + os << s; + i++; + } } - return OS.str(); + 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<Undefined>(Sym); - if (!Undef) + for (auto &i : symMap) { + Symbol *sym = i.second; + auto *undef = dyn_cast<Undefined>(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<Lazy>(find(("__imp_" + Name).str())); - if (!L || L->PendingArchiveLoad) + Lazy *l = dyn_cast_or_null<Lazy>(find(("__imp_" + name).str())); + if (!l || l->pendingArchiveLoad) continue; - log("Loading lazy " + L->getName() + " from " + L->File->getName() + + 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<Defined>(find(("__imp_" + Name).str())); - if (!Imp) + Defined *imp = dyn_cast_or_null<Defined>(find(("__imp_" + name).str())); + if (!imp) return false; // Replace the reference directly to a variable with a reference // to the import address table instead. This obviously isn't right, - // but we mark the symbol as IsRuntimePseudoReloc, and a later pass + // but we mark the symbol as isRuntimePseudoReloc, and a later pass // will add runtime pseudo relocations for every relocation against // this Symbol. The runtime pseudo relocation framework expects the // reference itself to point at the IAT entry. - size_t ImpSize = 0; - if (isa<DefinedImportData>(Imp)) { - log("Automatically importing " + Name + " from " + - cast<DefinedImportData>(Imp)->getDLLName()); - ImpSize = sizeof(DefinedImportData); - } else if (isa<DefinedRegular>(Imp)) { - log("Automatically importing " + Name + " from " + - toString(cast<DefinedRegular>(Imp)->File)); - ImpSize = sizeof(DefinedRegular); + size_t impSize = 0; + if (isa<DefinedImportData>(imp)) { + log("Automatically importing " + name + " from " + + cast<DefinedImportData>(imp)->getDLLName()); + impSize = sizeof(DefinedImportData); + } else if (isa<DefinedRegular>(imp)) { + log("Automatically importing " + name + " from " + + toString(cast<DefinedRegular>(imp)->file)); + impSize = sizeof(DefinedRegular); } else { - warn("unable to automatically import " + Name + " from " + Imp->getName() + - " from " + toString(cast<DefinedRegular>(Imp)->File) + + warn("unable to automatically import " + name + " from " + imp->getName() + + " from " + toString(cast<DefinedRegular>(imp)->file) + "; unexpected symbol type"); return false; } - Sym->replaceKeepingName(Imp, ImpSize); - Sym->IsRuntimePseudoReloc = true; + sym->replaceKeepingName(imp, impSize); + sym->isRuntimePseudoReloc = true; // There may exist symbols named .refptr.<name> which only consist // of a single pointer to <name>. If it turns out <name> is // automatically imported, we don't need to keep the .refptr.<name> // pointer at all, but redirect all accesses to it to the IAT entry // for __imp_<name> instead, and drop the whole .refptr.<name> chunk. - DefinedRegular *Refptr = - dyn_cast_or_null<DefinedRegular>(find((".refptr." + Name).str())); - if (Refptr && Refptr->getChunk()->getSize() == Config->Wordsize) { - SectionChunk *SC = dyn_cast_or_null<SectionChunk>(Refptr->getChunk()); - if (SC && SC->Relocs.size() == 1 && *SC->symbols().begin() == Sym) { - log("Replacing .refptr." + Name + " with " + Imp->getName()); - Refptr->getChunk()->Live = false; - Refptr->replaceKeepingName(Imp, ImpSize); + DefinedRegular *refptr = + dyn_cast_or_null<DefinedRegular>(find((".refptr." + name).str())); + if (refptr && refptr->getChunk()->getSize() == config->wordsize) { + SectionChunk *sc = dyn_cast_or_null<SectionChunk>(refptr->getChunk()); + if (sc && sc->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<Symbol *, 8> Undefs; - DenseMap<Symbol *, Symbol *> LocalImports; + SmallPtrSet<Symbol *, 8> undefs; + DenseMap<Symbol *, Symbol *> localImports; - for (auto &I : SymMap) { - Symbol *Sym = I.second; - auto *Undef = dyn_cast<Undefined>(Sym); - if (!Undef) + for (auto &i : symMap) { + Symbol *sym = i.second; + auto *undef = dyn_cast<Undefined>(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<DefinedRegular>(D)) - memcpy(Sym, D, sizeof(DefinedRegular)); - else if (isa<DefinedAbsolute>(D)) - memcpy(Sym, D, sizeof(DefinedAbsolute)); + if (isa<DefinedRegular>(d)) + memcpy(sym, d, sizeof(DefinedRegular)); + else if (isa<DefinedAbsolute>(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<Defined>(Imp)) { - auto *D = cast<Defined>(Imp); - replaceSymbol<DefinedLocalImport>(Sym, Name, D); - LocalImportChunks.push_back(cast<DefinedLocalImport>(Sym)->getChunk()); - LocalImports[Sym] = D; + if (name.startswith("__imp_")) { + Symbol *imp = find(name.substr(strlen("__imp_"))); + if (imp && isa<Defined>(imp)) { + auto *d = cast<Defined>(imp); + replaceSymbol<DefinedLocalImport>(sym, name, d); + localImportChunks.push_back(cast<DefinedLocalImport>(sym)->getChunk()); + localImports[sym] = d; continue; } } // 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<DefinedAbsolute>(Sym, Name, 0); - Undefs.insert(Sym); + if (config->forceUnresolved) + replaceSymbol<DefinedAbsolute>(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("<root>: undefined symbol: " + toString(*B)); - if (Config->WarnLocallyDefinedImported) - if (Symbol *Imp = LocalImports.lookup(B)) - warn("<root>: locally defined symbol imported: " + toString(*Imp) + - " (defined in " + toString(Imp->getFile()) + ") [LNK4217]"); + for (Symbol *b : config->gcroot) { + if (undefs.count(b)) + errorOrWarn("<root>: undefined symbol: " + toString(*b)); + if (config->warnLocallyDefinedImported) + if (Symbol *imp = localImports.lookup(b)) + warn("<root>: 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<UndefinedDiag> undefDiags; + DenseMap<Symbol *, int> 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<Symbol *, bool> SymbolTable::insert(StringRef Name) { - bool Inserted = false; - Symbol *&Sym = SymMap[CachedHashStringRef(Name)]; - if (!Sym) { - Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>()); - Sym->IsUsedInRegularObj = false; - Sym->PendingArchiveLoad = false; - Inserted = true; +std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) { + bool inserted = false; + Symbol *&sym = symMap[CachedHashStringRef(name)]; + if (!sym) { + sym = reinterpret_cast<Symbol *>(make<SymbolUnion>()); + sym->isUsedInRegularObj = false; + sym->pendingArchiveLoad = false; + inserted = true; } - return {Sym, Inserted}; + return {sym, inserted}; } -std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name, InputFile *File) { - std::pair<Symbol *, bool> Result = insert(Name); - if (!File || !isa<BitcodeFile>(File)) - Result.first->IsUsedInRegularObj = true; - return Result; +std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, InputFile *file) { + std::pair<Symbol *, bool> result = insert(name); + if (!file || !isa<BitcodeFile>(file)) + result.first->isUsedInRegularObj = true; + return result; } -Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F, - bool IsWeakAlias) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(Name, F); - if (WasInserted || (isa<Lazy>(S) && IsWeakAlias)) { - replaceSymbol<Undefined>(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<Lazy>(s) && isWeakAlias)) { + replaceSymbol<Undefined>(s, name); + return s; } - if (auto *L = dyn_cast<Lazy>(S)) { - if (!S->PendingArchiveLoad) { - S->PendingArchiveLoad = true; - L->File->addMember(&L->Sym); + if (auto *l = dyn_cast<Lazy>(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<Lazy>(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<Lazy>(s, f, sym); return; } - auto *U = dyn_cast<Undefined>(S); - if (!U || U->WeakAlias || S->PendingArchiveLoad) + auto *u = dyn_cast<Undefined>(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<Undefined>(S) || isa<Lazy>(S)) - replaceSymbol<DefinedAbsolute>(S, N, Sym); - else if (!isa<DefinedCOFF>(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<Undefined>(s) || isa<Lazy>(s)) + replaceSymbol<DefinedAbsolute>(s, n, sym); + else if (!isa<DefinedCOFF>(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<Undefined>(S) || isa<Lazy>(S)) - replaceSymbol<DefinedAbsolute>(S, N, VA); - else if (!isa<DefinedCOFF>(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<Undefined>(s) || isa<Lazy>(s)) + replaceSymbol<DefinedAbsolute>(s, n, va); + else if (!isa<DefinedCOFF>(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<Undefined>(S) || isa<Lazy>(S)) - replaceSymbol<DefinedSynthetic>(S, N, C); - else if (!isa<DefinedCOFF>(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<Undefined>(s) || isa<Lazy>(s)) + replaceSymbol<DefinedSynthetic>(s, n, c); + else if (!isa<DefinedCOFF>(s)) + reportDuplicate(s, nullptr); + return s; } -Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, - const coff_symbol_generic *Sym, - SectionChunk *C) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, F); - if (WasInserted || !isa<DefinedRegular>(S)) - replaceSymbol<DefinedRegular>(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<DefinedRegular>(s)) + replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ false, + /*IsExternal*/ true, sym, c); else - reportDuplicate(S, F); - return S; + reportDuplicate(s, f); + return s; } -std::pair<Symbol *, bool> -SymbolTable::addComdat(InputFile *F, StringRef N, - const coff_symbol_generic *Sym) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, F); - if (WasInserted || !isa<DefinedRegular>(S)) { - replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ true, - /*IsExternal*/ true, Sym, nullptr); - return {S, true}; +std::pair<DefinedRegular *, bool> +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<DefinedRegular>(s)) { + replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ true, + /*IsExternal*/ true, sym, nullptr); + return {cast<DefinedRegular>(s), true}; } - if (!cast<DefinedRegular>(S)->isCOMDAT()) - reportDuplicate(S, F); - return {S, false}; + auto *existingSymbol = cast<DefinedRegular>(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<DefinedCOFF>(S)) - replaceSymbol<DefinedCommon>(S, F, N, Size, Sym, C); - else if (auto *DC = dyn_cast<DefinedCommon>(S)) - if (Size > DC->getSize()) - replaceSymbol<DefinedCommon>(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<DefinedCOFF>(s)) + replaceSymbol<DefinedCommon>(s, f, n, size, sym, c); + else if (auto *dc = dyn_cast<DefinedCommon>(s)) + if (size > dc->getSize()) + replaceSymbol<DefinedCommon>(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<Undefined>(S) || isa<Lazy>(S)) { - replaceSymbol<DefinedImportData>(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<Undefined>(s) || isa<Lazy>(s)) { + replaceSymbol<DefinedImportData>(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<Undefined>(S) || isa<Lazy>(S)) { - replaceSymbol<DefinedImportThunk>(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<Undefined>(s) || isa<Lazy>(s)) { + replaceSymbol<DefinedImportThunk>(s, name, id, machine); + return s; } - reportDuplicate(S, ID->File); + reportDuplicate(s, id->file); return nullptr; } std::vector<Chunk *> SymbolTable::getChunks() { - std::vector<Chunk *> Res; - for (ObjFile *File : ObjFile::Instances) { - ArrayRef<Chunk *> V = File->getChunks(); - Res.insert(Res.end(), V.begin(), V.end()); + std::vector<Chunk *> res; + for (ObjFile *file : ObjFile::instances) { + ArrayRef<Chunk *> 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<Symbol *> SymbolTable::getSymsWithPrefix(StringRef prefix) { + std::vector<Symbol *> 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<Undefined>(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<Undefined>(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<Symbol *> 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()); -} - -void SymbolTable::mangleMaybe(Symbol *B) { - auto *U = dyn_cast<Undefined>(B); - if (!U || U->WeakAlias) - return; - StringRef Alias = findMangle(U->getName()); - if (!Alias.empty()) { - log(U->getName() + " aliased to " + Alias); - U->WeakAlias = addUndefined(Alias); - } + return findByPrefix("?" + name.substr(1) + "@@Y"); } -Symbol *SymbolTable::addUndefined(StringRef Name) { - return addUndefined(Name, nullptr, false); +Symbol *SymbolTable::addUndefined(StringRef name) { + return addUndefined(name, nullptr, false); } std::vector<StringRef> 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<ObjFile>(MemoryBufferRef(Object, "lto.tmp")); - Obj->parse(); - ObjFile::Instances.push_back(Obj); + ScopedTimer t(ltoTimer); + for (StringRef object : compileBitcodeFiles()) { + auto *obj = make<ObjFile>(MemoryBufferRef(object, "lto.tmp")); + obj->parse(); + ObjFile::instances.push_back(obj); } } diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h index 00e55dbb7a02..88f47cbe9e78 100644 --- a/COFF/SymbolTable.h +++ b/COFF/SymbolTable.h @@ -1,9 +1,8 @@ //===- SymbolTable.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 // //===----------------------------------------------------------------------===// @@ -28,6 +27,7 @@ class Chunk; class CommonChunk; class Defined; class DefinedAbsolute; +class DefinedRegular; class DefinedRelative; class Lazy; class SectionChunk; @@ -47,7 +47,7 @@ class Symbol; // 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 @@ -55,21 +55,20 @@ public: 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<Chunk *> 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 @@ -78,52 +77,53 @@ public: std::vector<StringRef> compileBitcodeFiles(); // Creates an Undefined symbol for a given name. - Symbol *addUndefined(StringRef Name); - - 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<Symbol *, bool> - addComdat(InputFile *F, StringRef N, - const llvm::object::coff_symbol_generic *S = nullptr); - Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size, - const llvm::object::coff_symbol_generic *S = nullptr, - CommonChunk *C = nullptr); - Symbol *addImportData(StringRef N, ImportFile *F); - Symbol *addImportThunk(StringRef Name, DefinedImportData *S, - uint16_t Machine); - - void reportDuplicate(Symbol *Existing, InputFile *NewFile); + Symbol *addUndefined(StringRef name); + + 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<DefinedRegular *, bool> + addComdat(InputFile *f, StringRef n, + const llvm::object::coff_symbol_generic *s = nullptr); + Symbol *addCommon(InputFile *f, StringRef n, uint64_t size, + const llvm::object::coff_symbol_generic *s = nullptr, + CommonChunk *c = nullptr); + Symbol *addImportData(StringRef n, ImportFile *f); + Symbol *addImportThunk(StringRef name, DefinedImportData *s, + uint16_t machine); + + void reportDuplicate(Symbol *existing, InputFile *newFile); // A list of chunks which to be added to .rdata. - std::vector<Chunk *> LocalImportChunks; + std::vector<Chunk *> localImportChunks; // Iterates symbols in non-determinstic hash table order. - template <typename T> void forEachSymbol(T Callback) { - for (auto &Pair : SymMap) - Callback(Pair.second); + template <typename T> void forEachSymbol(T callback) { + for (auto &pair : symMap) + callback(pair.second); } private: /// Inserts symbol if not already present. - std::pair<Symbol *, bool> insert(StringRef Name); - /// Same as insert(Name), but also sets IsUsedInRegularObj. - std::pair<Symbol *, bool> insert(StringRef Name, InputFile *F); - StringRef findByPrefix(StringRef Prefix); + std::pair<Symbol *, bool> insert(StringRef name); + /// Same as insert(Name), but also sets isUsedInRegularObj. + std::pair<Symbol *, bool> insert(StringRef name, InputFile *f); + + std::vector<Symbol *> getSymsWithPrefix(StringRef prefix); - llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> SymMap; - std::unique_ptr<BitcodeCompiler> LTO; + llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> symMap; + std::unique_ptr<BitcodeCompiler> lto; }; -extern SymbolTable *Symtab; +extern SymbolTable *symtab; -std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex); +std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex); } // namespace coff } // namespace lld diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp index ccaf86417f10..3583d4cb28c1 100644 --- a/COFF/Symbols.cpp +++ b/COFF/Symbols.cpp @@ -1,9 +1,8 @@ //===- Symbols.cpp --------------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// @@ -19,11 +18,17 @@ 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<std::string> 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<std::string> s = lld::demangleMSVC(b.getName())) + return *s; + return b.getName(); } namespace lld { @@ -37,70 +42,75 @@ StringRef Symbol::getName() { // 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<DefinedCOFF>(this); - cast<ObjFile>(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name); + if (nameData == nullptr) { + auto *d = cast<DefinedCOFF>(this); + StringRef nameStr; + cast<ObjFile>(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<DefinedCOFF>(this)) - return Sym->File; - if (auto *Sym = dyn_cast<Lazy>(this)) - return Sym->File; + if (auto *sym = dyn_cast<DefinedCOFF>(this)) + return sym->file; + if (auto *sym = dyn_cast<Lazy>(this)) + return sym->file; return nullptr; } bool Symbol::isLive() const { - if (auto *R = dyn_cast<DefinedRegular>(this)) - return R->getChunk()->Live; - if (auto *Imp = dyn_cast<DefinedImportData>(this)) - return Imp->File->Live; - if (auto *Imp = dyn_cast<DefinedImportThunk>(this)) - return Imp->WrappedSym->File->ThunkLive; + if (auto *r = dyn_cast<DefinedRegular>(this)) + return r->getChunk()->live; + if (auto *imp = dyn_cast<DefinedImportData>(this)) + return imp->file->live; + if (auto *imp = dyn_cast<DefinedImportThunk>(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<ObjFile>(File)->getCOFFObj()->getSymbolTableEntrySize(); - if (SymSize == sizeof(coff_symbol16)) - return COFFSymbolRef(reinterpret_cast<const coff_symbol16 *>(Sym)); - assert(SymSize == sizeof(coff_symbol32)); - return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(Sym)); + size_t symSize = cast<ObjFile>(file)->getCOFFObj()->getSymbolTableEntrySize(); + if (symSize == sizeof(coff_symbol16)) + return COFFSymbolRef(reinterpret_cast<const coff_symbol16 *>(sym)); + assert(symSize == sizeof(coff_symbol32)); + return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(sym)); } -uint16_t DefinedAbsolute::NumOutputSections; +uint16_t DefinedAbsolute::numOutputSections; -static Chunk *makeImportThunk(DefinedImportData *S, uint16_t Machine) { - if (Machine == AMD64) - return make<ImportThunkChunkX64>(S); - if (Machine == I386) - return make<ImportThunkChunkX86>(S); - if (Machine == ARM64) - return make<ImportThunkChunkARM64>(S); - assert(Machine == ARMNT); - return make<ImportThunkChunkARM>(S); +static Chunk *makeImportThunk(DefinedImportData *s, uint16_t machine) { + if (machine == AMD64) + return make<ImportThunkChunkX64>(s); + if (machine == I386) + return make<ImportThunkChunkX86>(s); + if (machine == ARM64) + return make<ImportThunkChunkARM64>(s); + assert(machine == ARMNT); + return make<ImportThunkChunkARM>(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<Undefined>(A)->WeakAlias) - if (auto *D = dyn_cast<Defined>(A)) - return D; + for (Symbol *a = weakAlias; a; a = cast<Undefined>(a)->weakAlias) + if (auto *d = dyn_cast<Defined>(a)) + return d; return nullptr; } } // namespace coff diff --git a/COFF/Symbols.h b/COFF/Symbols.h index 4a8693e22e3c..86cd4f585e50 100644 --- a/COFF/Symbols.h +++ b/COFF/Symbols.h @@ -1,9 +1,8 @@ //===- Symbols.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 // //===----------------------------------------------------------------------===// @@ -58,15 +57,12 @@ public: LastDefinedKind = DefinedSyntheticKind, }; - Kind kind() const { return static_cast<Kind>(SymbolKind); } - - // Returns true if this is an external symbol. - bool isExternal() { return IsExternal; } + Kind kind() const { return static_cast<Kind>(symbolKind); } // 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(); @@ -77,46 +73,50 @@ public: 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. @@ -130,120 +130,119 @@ public: // 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 @@ -253,32 +252,32 @@ private: // 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 @@ -294,23 +293,23 @@ public: // 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 @@ -320,19 +319,19 @@ public: // 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 @@ -342,18 +341,18 @@ private: // This is here just for compatibility with MSVC. class DefinedLocalImport : public Defined { public: - DefinedLocalImport(StringRef N, Defined *S) - : Defined(DefinedLocalImportKind, N), Data(make<LocalImportChunk>(S)) {} + DefinedLocalImport(StringRef n, Defined *s) + : Defined(DefinedLocalImportKind, n), data(make<LocalImportChunk>(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() { @@ -406,19 +405,19 @@ inline Chunk *Defined::getChunk() { // 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 <typename T, typename... ArgT> -void replaceSymbol(Symbol *S, ArgT &&... Arg) { +void replaceSymbol(Symbol *s, ArgT &&... arg) { static_assert(std::is_trivially_destructible<T>(), "Symbol types must be trivially destructible"); static_assert(sizeof(T) <= sizeof(SymbolUnion), "Symbol too small"); @@ -426,11 +425,11 @@ void replaceSymbol(Symbol *S, ArgT &&... Arg) { "SymbolUnion not aligned enough"); assert(static_cast<Symbol *>(static_cast<T *>(nullptr)) == nullptr && "Not a Symbol"); - new (S) T(std::forward<ArgT>(Arg)...); + new (s) T(std::forward<ArgT>(arg)...); } } // namespace coff -std::string toString(coff::Symbol &B); +std::string toString(coff::Symbol &b); } // namespace lld #endif diff --git a/COFF/TypeMerger.h b/COFF/TypeMerger.h new file mode 100644 index 000000000000..e2cfe668cfa5 --- /dev/null +++ b/COFF/TypeMerger.h @@ -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<llvm::codeview::TypeIndex, 0> tpiMap; + llvm::SmallVector<llvm::codeview::TypeIndex, 0> ipiMap; + bool isTypeServerMap = false; + bool isPrecompiledTypeMap = false; +}; + +} // namespace coff +} // namespace lld + +#endif
\ No newline at end of file diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp index 258796ea6057..36ef87de4263 100644 --- a/COFF/Writer.cpp +++ b/COFF/Writer.cpp @@ -1,9 +1,8 @@ //===- Writer.cpp ---------------------------------------------------------===// // -// 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 // //===----------------------------------------------------------------------===// @@ -17,6 +16,7 @@ #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" @@ -63,104 +63,129 @@ 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; + +// Global vector of all output sections. After output sections are finalized, +// this can be indexed by Chunk::getOutputSection. +static std::vector<OutputSection *> outputSections; -static const int NumberOfDataDirectory = 16; +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<Chunk *> &R, bool WriteRepro) - : Records(R), WriteRepro(WriteRepro) {} + DebugDirectoryChunk(const std::vector<Chunk *> &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<debug_directory *>(B + OutputSectionOff); + void writeTo(uint8_t *b) const override { + auto *d = reinterpret_cast<debug_directory *>(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; - - TimeDateStamps.push_back(&D->TimeDateStamp); - } - - mutable std::vector<support::ulittle32_t *> TimeDateStamps; - const std::vector<Chunk *> &Records; - bool WriteRepro; + 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); + } + + mutable std::vector<support::ulittle32_t *> timeDateStamps; + const std::vector<Chunk *> &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<codeview::DebugInfo *>(B + OutputSectionOff); + buildId = reinterpret_cast<codeview::DebugInfo *>(b); // variable sized field (PDB Path) - char *P = reinterpret_cast<char *>(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<char *>(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: @@ -168,78 +193,81 @@ private: void createMiscChunks(); void createImportTables(); void appendImportThunks(); - void locateImportTables( - std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &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 <typename PEHeaderTy> void writeHeader(); void createSEHTable(); void createRuntimePseudoRelocs(); void insertCtorDtorSymbols(); void createGuardCFTables(); - void markSymbolsForRVATable(ObjFile *File, - ArrayRef<SectionChunk *> SymIdxChunks, - SymbolRVASet &TableSymbols); - void maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym, - StringRef CountSym); + void markSymbolsForRVATable(ObjFile *file, + ArrayRef<SectionChunk *> symIdxChunks, + SymbolRVASet &tableSymbols); + void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, + StringRef countSym); void setSectionPermissions(); void writeSections(); void writeBuildId(); void sortExceptionTable(); - void sortCRTSectionChunks(std::vector<Chunk *> &Chunks); + void sortCRTSectionChunks(std::vector<Chunk *> &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<coff_symbol16> createSymbol(Defined *D); - size_t addEntryToStringTable(StringRef Str); + llvm::Optional<coff_symbol16> createSymbol(Defined *d); + size_t addEntryToStringTable(StringRef str); - OutputSection *findSection(StringRef Name); + OutputSection *findSection(StringRef name); void addBaserels(); - void addBaserelBlocks(std::vector<Baserel> &V); + void addBaserelBlocks(std::vector<Baserel> &v); uint32_t getSizeOfInitializedData(); - std::map<StringRef, std::vector<DefinedImportData *>> binImports(); - - std::unique_ptr<FileOutputBuffer> &Buffer; - std::vector<OutputSection *> OutputSections; - std::vector<char> Strtab; - std::vector<llvm::object::coff_symbol16> 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<Chunk *> DebugRecords; - CVDebugRecordChunk *BuildId = nullptr; - ArrayRef<uint8_t> SectionTable; - - 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; + + std::unique_ptr<FileOutputBuffer> &buffer; + std::map<PartialSectionKey, PartialSection *> partialSections; + std::vector<char> strtab; + std::vector<llvm::object::coff_symbol16> 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<Chunk *> debugRecords; + CVDebugRecordChunk *buildId = nullptr; + ArrayRef<uint8_t> sectionTable; + + 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; // The first and last .pdata sections in the output file. // @@ -250,87 +278,115 @@ private: // 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<coff_section *>(Buf); - *Hdr = Header; - if (StringTableOff) { +void OutputSection::writeHeaderTo(uint8_t *buf) { + auto *hdr = reinterpret_cast<coff_section *>(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<Defined *, bool> -getThunk(DenseMap<uint64_t, Defined *> &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<RangeExtensionThunk>(Target); - Defined *D = make<DefinedSynthetic>("", C); - LastThunk = D; - return {D, true}; +getThunk(DenseMap<uint64_t, Defined *> &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<RangeExtensionThunkARM>(target); + break; + case ARM64: + c = make<RangeExtensionThunkARM64>(target); + break; + default: + llvm_unreachable("Unexpected architecture"); + } + Defined *d = make<DefinedSynthetic>("", c); + lastThunk = d; + return {d, true}; } // This checks all relocations, and for any relocation which isn't in range @@ -344,81 +400,124 @@ getThunk(DenseMap<uint64_t, Defined *> &LastThunks, Defined *Target, uint64_t P, // 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<Chunk *> &Chunks, int Margin) { - bool AddressesChanged = false; - DenseMap<uint64_t, Defined *> LastThunks; - size_t ThunksSize = 0; +static bool createThunks(OutputSection *os, int margin) { + bool addressesChanged = false; + DenseMap<uint64_t, Defined *> lastThunks; + DenseMap<std::pair<ObjFile *, Defined *>, 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<SectionChunk>(Chunks[I]); - if (!SC) + for (size_t i = 0; i != os->chunks.size(); ++i) { + SectionChunk *sc = dyn_cast_or_null<SectionChunk>(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<std::pair<uint32_t, uint32_t>> relocReplacements; + ArrayRef<coff_relocation> 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<Defined>(RelocTarget); - if (!Sym) + Defined *sym = dyn_cast_or_null<Defined>(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<coff_relocation> curRelocs = sc->getRelocs(); + MutableArrayRef<coff_relocation> newRelocs; + if (originalRelocs.data() == curRelocs.data()) { + newRelocs = makeMutableArrayRef( + bAlloc.Allocate<coff_relocation>(originalRelocs.size()), + originalRelocs.size()); + } else { + newRelocs = makeMutableArrayRef( + const_cast<coff_relocation *>(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<Chunk *> Chunks) { - for (Chunk *C : Chunks) { - SectionChunk *SC = dyn_cast_or_null<SectionChunk>(C); - if (!SC) +static bool verifyRanges(const std::vector<Chunk *> chunks) { + for (Chunk *c : chunks) { + SectionChunk *sc = dyn_cast_or_null<SectionChunk>(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<coff_relocation> 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<Defined>(RelocTarget); - if (!Sym) + Defined *sym = dyn_cast_or_null<Defined>(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; } } @@ -428,71 +527,68 @@ static bool verifyRanges(const std::vector<Chunk *> Chunks) { // 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 verif |