From f1e1c239e31b467e17f1648b1f524fc9ab5b431a Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Aug 20 2019 20:51:32 +0000 Subject: Vendor import of stripped lld trunk r366426 (just before the release_90 branch point): https://llvm.org/svn/llvm-project/lld/trunk@366426 --- diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt index bb241e7..c7ef7c4 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 29131d7..0e43d2b 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 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 symbolLocations = + getSymbolLocations(file, rel.SymbolTableIndex); + + std::string out; + llvm::raw_string_ostream os(out); + os << "relocation against symbol in discarded section: " + name; + for (const std::string &s : symbolLocations) + os << s; + error(os.str()); } -void SectionChunk::writeTo(uint8_t *Buf) const { - if (!hasData()) +void SectionChunk::writeTo(uint8_t *buf) const { + if (!hasData) return; // Copy section contents from source object file to output file. - ArrayRef A = getContents(); - if (!A.empty()) - memcpy(Buf + OutputSectionOff, A.data(), A.size()); + ArrayRef a = getContents(); + if (!a.empty()) + memcpy(buf, a.data(), a.size()); // Apply relocations. - size_t InputSize = getSize(); - for (size_t I = 0, E = Relocs.size(); I < E; I++) { - const coff_relocation &Rel = Relocs[I]; + size_t inputSize = getSize(); + for (size_t i = 0, e = relocsSize; i < e; i++) { + const coff_relocation &rel = relocsData[i]; // Check for an invalid relocation offset. This check isn't perfect, because // we don't have the relocation size, which is only known after checking the // machine and relocation type. As a result, a relocation may overwrite the // beginning of the following input section. - if (Rel.VirtualAddress >= InputSize) { + if (rel.VirtualAddress >= inputSize) { error("relocation points beyond the end of its parent section"); continue; } - uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress; + uint8_t *off = buf + rel.VirtualAddress; - // Use the potentially remapped Symbol instead of the one that the - // relocation points to. - auto *Sym = dyn_cast_or_null(RelocTargets[I]); + auto *sym = + dyn_cast_or_null(file->getSymbol(rel.SymbolTableIndex)); // Get the output section of the symbol for this relocation. The output // section is needed to compute SECREL and SECTION relocations used in debug // info. - Chunk *C = Sym ? Sym->getChunk() : nullptr; - OutputSection *OS = C ? C->getOutputSection() : nullptr; + Chunk *c = sym ? sym->getChunk() : nullptr; + OutputSection *os = c ? c->getOutputSection() : nullptr; // Skip the relocation if it refers to a discarded section, and diagnose it // as an error if appropriate. If a symbol was discarded early, it may be // null. If it was discarded late, the output section will be null, unless // it was an absolute or synthetic symbol. - if (!Sym || - (!OS && !isa(Sym) && !isa(Sym))) { - maybeReportRelocationToDiscarded(this, Sym, Rel); + if (!sym || + (!os && !isa(sym) && !isa(sym))) { + maybeReportRelocationToDiscarded(this, sym, rel); continue; } - uint64_t S = Sym->getRVA(); + uint64_t s = sym->getRVA(); // Compute the RVA of the relocation for relative relocations. - uint64_t P = RVA + Rel.VirtualAddress; - switch (Config->Machine) { + uint64_t p = rva + rel.VirtualAddress; + switch (config->machine) { case AMD64: - applyRelX64(Off, Rel.Type, OS, S, P); + applyRelX64(off, rel.Type, os, s, p); break; case I386: - applyRelX86(Off, Rel.Type, OS, S, P); + applyRelX86(off, rel.Type, os, s, p); break; case ARMNT: - applyRelARM(Off, Rel.Type, OS, S, P); + applyRelARM(off, rel.Type, os, s, p); break; case ARM64: - applyRelARM64(Off, Rel.Type, OS, S, P); + applyRelARM64(off, rel.Type, os, s, p); break; default: llvm_unreachable("unknown machine type"); @@ -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 *Res) { - for (size_t I = 0, E = Relocs.size(); I < E; I++) { - const coff_relocation &Rel = Relocs[I]; - uint8_t Ty = getBaserelType(Rel); - if (Ty == IMAGE_REL_BASED_ABSOLUTE) +void SectionChunk::getBaserels(std::vector *res) { + for (size_t i = 0, e = relocsSize; i < e; i++) { + const coff_relocation &rel = relocsData[i]; + uint8_t ty = getBaserelType(rel); + if (ty == IMAGE_REL_BASED_ABSOLUTE) continue; - // Use the potentially remapped Symbol instead of the one that the - // relocation points to. - Symbol *Target = RelocTargets[I]; - if (!Target || isa(Target)) + Symbol *target = file->getSymbol(rel.SymbolTableIndex); + if (!target || isa(target)) continue; - Res->emplace_back(RVA + Rel.VirtualAddress, Ty); + res->emplace_back(rva + rel.VirtualAddress, ty); } } @@ -464,7 +470,7 @@ void SectionChunk::getBaserels(std::vector *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 &Res) { - for (const coff_relocation &Rel : Relocs) { - auto *Target = - dyn_cast_or_null(File->getSymbol(Rel.SymbolTableIndex)); - if (!Target || !Target->IsRuntimePseudoReloc) + std::vector &res) { + for (const coff_relocation &rel : getRelocs()) { + auto *target = + dyn_cast_or_null(file->getSymbol(rel.SymbolTableIndex)); + if (!target || !target->isRuntimePseudoReloc) continue; - int SizeInBits = getRuntimePseudoRelocSize(Rel.Type); - if (SizeInBits == 0) { - error("unable to automatically import from " + Target->getName() + + int sizeInBits = getRuntimePseudoRelocSize(rel.Type); + if (sizeInBits == 0) { + error("unable to automatically import from " + target->getName() + " with relocation type " + - File->getCOFFObj()->getRelocationTypeName(Rel.Type) + " in " + - toString(File)); + file->getCOFFObj()->getRelocationTypeName(rel.Type) + " in " + + toString(file)); continue; } - // SizeInBits is used to initialize the Flags field; currently no + // sizeInBits is used to initialize the Flags field; currently no // other flags are defined. - Res.emplace_back( - RuntimePseudoReloc(Target, this, Rel.VirtualAddress, SizeInBits)); + res.emplace_back( + RuntimePseudoReloc(target, this, rel.VirtualAddress, sizeInBits)); } } -bool SectionChunk::hasData() const { - return !(Header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA); -} - -uint32_t SectionChunk::getOutputCharacteristics() const { - return Header->Characteristics & (PermMask | TypeMask); -} - bool SectionChunk::isCOMDAT() const { - return Header->Characteristics & IMAGE_SCN_LNK_COMDAT; + return header->Characteristics & IMAGE_SCN_LNK_COMDAT; } void SectionChunk::printDiscardedMessage() const { // Removed by dead-stripping. If it's removed by ICF, ICF already // printed out the name, so don't repeat that here. - if (Sym && this == Repl) - message("Discarded " + Sym->getName()); + if (sym && this == repl) + message("Discarded " + sym->getName()); } -StringRef SectionChunk::getDebugName() { - if (Sym) - return Sym->getName(); +StringRef SectionChunk::getDebugName() const { + if (sym) + return sym->getName(); return ""; } ArrayRef SectionChunk::getContents() const { - ArrayRef A; - File->getCOFFObj()->getSectionContents(Header, A); - return A; + ArrayRef a; + cantFail(file->getCOFFObj()->getSectionContents(header, a)); + return a; } -void SectionChunk::replace(SectionChunk *Other) { - Alignment = std::max(Alignment, Other->Alignment); - Other->Repl = Repl; - Other->Live = false; +ArrayRef SectionChunk::consumeDebugMagic() { + assert(isCodeView()); + return consumeDebugMagic(getContents(), getSectionName()); +} + +ArrayRef SectionChunk::consumeDebugMagic(ArrayRef data, + StringRef sectionName) { + if (data.empty()) + return {}; + + // First 4 bytes are section magic. + if (data.size() < 4) + fatal("the section is too short: " + sectionName); + + if (!sectionName.startswith(".debug$")) + fatal("invalid section: " + sectionName); + + uint32_t magic = support::endian::read32le(data.data()); + uint32_t expectedMagic = sectionName == ".debug$H" + ? DEBUG_HASHES_SECTION_MAGIC + : DEBUG_SECTION_MAGIC; + if (magic != expectedMagic) { + warn("ignoring section " + sectionName + " with unrecognized magic 0x" + + utohexstr(magic)); + return {}; + } + return data.slice(4); +} + +SectionChunk *SectionChunk::findByName(ArrayRef sections, + StringRef name) { + for (SectionChunk *c : sections) + if (c->getSectionName() == name) + return c; + return nullptr; +} + +void SectionChunk::replace(SectionChunk *other) { + p2Align = std::max(p2Align, other->p2Align); + other->repl = repl; + other->live = false; } uint32_t SectionChunk::getSectionNumber() const { - DataRefImpl R; - R.p = reinterpret_cast(Header); - SectionRef S(R, File->getCOFFObj()); - return S.getIndex() + 1; + DataRefImpl r; + r.p = reinterpret_cast(header); + SectionRef s(r, file->getCOFFObj()); + return s.getIndex() + 1; } -CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) { - // Common symbols are aligned on natural boundaries up to 32 bytes. +CommonChunk::CommonChunk(const COFFSymbolRef s) : sym(s) { + // The value of a common symbol is its size. Align all common symbols smaller + // than 32 bytes naturally, i.e. round the size up to the next power of two. // This is what MSVC link.exe does. - Alignment = std::min(uint64_t(32), PowerOf2Ceil(Sym.getValue())); + setAlignment(std::min(32U, uint32_t(PowerOf2Ceil(sym.getValue())))); + hasData = false; } uint32_t CommonChunk::getOutputCharacteristics() const { @@ -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 *Res) { - Res->emplace_back(getRVA() + 2); +void ImportThunkChunkX86::getBaserels(std::vector *res) { + res->emplace_back(getRVA() + 2); } -void ImportThunkChunkX86::writeTo(uint8_t *Buf) const { - memcpy(Buf + OutputSectionOff, ImportThunkX86, sizeof(ImportThunkX86)); +void ImportThunkChunkX86::writeTo(uint8_t *buf) const { + memcpy(buf, importThunkX86, sizeof(importThunkX86)); // The first two bytes is a JMP instruction. Fill its operand. - write32le(Buf + OutputSectionOff + 2, - ImpSymbol->getRVA() + Config->ImageBase); + write32le(buf + 2, + impSymbol->getRVA() + config->imageBase); } -void ImportThunkChunkARM::getBaserels(std::vector *Res) { - Res->emplace_back(getRVA(), IMAGE_REL_BASED_ARM_MOV32T); +void ImportThunkChunkARM::getBaserels(std::vector *res) { + res->emplace_back(getRVA(), IMAGE_REL_BASED_ARM_MOV32T); } -void ImportThunkChunkARM::writeTo(uint8_t *Buf) const { - memcpy(Buf + OutputSectionOff, ImportThunkARM, sizeof(ImportThunkARM)); +void ImportThunkChunkARM::writeTo(uint8_t *buf) const { + memcpy(buf, importThunkARM, sizeof(importThunkARM)); // Fix mov.w and mov.t operands. - applyMOV32T(Buf + OutputSectionOff, ImpSymbol->getRVA() + Config->ImageBase); + applyMOV32T(buf, impSymbol->getRVA() + config->imageBase); } -void ImportThunkChunkARM64::writeTo(uint8_t *Buf) const { - int64_t Off = ImpSymbol->getRVA() & 0xfff; - memcpy(Buf + OutputSectionOff, ImportThunkARM64, sizeof(ImportThunkARM64)); - applyArm64Addr(Buf + OutputSectionOff, ImpSymbol->getRVA(), RVA, 12); - applyArm64Ldr(Buf + OutputSectionOff + 4, Off); +void ImportThunkChunkARM64::writeTo(uint8_t *buf) const { + int64_t off = impSymbol->getRVA() & 0xfff; + memcpy(buf, importThunkARM64, sizeof(importThunkARM64)); + applyArm64Addr(buf, impSymbol->getRVA(), rva, 12); + applyArm64Ldr(buf + 4, off); } // A Thumb2, PIC, non-interworking range extension thunk. -const uint8_t ArmThunk[] = { +const uint8_t armThunk[] = { 0x40, 0xf2, 0x00, 0x0c, // P: movw ip,:lower16:S - (P + (L1-P) + 4) 0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4) 0xe7, 0x44, // L1: add pc, ip }; -size_t RangeExtensionThunk::getSize() const { - assert(Config->Machine == ARMNT); - return sizeof(ArmThunk); +size_t RangeExtensionThunkARM::getSize() const { + assert(config->machine == ARMNT); + return sizeof(armThunk); +} + +void 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 *Res) { - Res->emplace_back(getRVA()); +void LocalImportChunk::getBaserels(std::vector *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(Buf + OutputSectionOff); - size_t Cnt = 0; - for (const ChunkAndOffset &CO : Syms) - Begin[Cnt++] = CO.InputChunk->getRVA() + CO.Offset; - std::sort(Begin, Begin + Cnt); - assert(std::unique(Begin, Begin + Cnt) == Begin + Cnt && +void RVATableChunk::writeTo(uint8_t *buf) const { + ulittle32_t *begin = reinterpret_cast(buf); + size_t cnt = 0; + for (const ChunkAndOffset &co : syms) + begin[cnt++] = co.inputChunk->getRVA() + co.offset; + std::sort(begin, begin + cnt); + assert(std::unique(begin, begin + cnt) == begin + cnt && "RVA tables should be de-duplicated"); } // MinGW specific, for the "automatic import of variables from DLLs" feature. size_t PseudoRelocTableChunk::getSize() const { - if (Relocs.empty()) + if (relocs.empty()) return 0; - return 12 + 12 * Relocs.size(); + return 12 + 12 * relocs.size(); } // MinGW specific. -void PseudoRelocTableChunk::writeTo(uint8_t *Buf) const { - if (Relocs.empty()) +void PseudoRelocTableChunk::writeTo(uint8_t *buf) const { + if (relocs.empty()) return; - ulittle32_t *Table = reinterpret_cast(Buf + OutputSectionOff); + ulittle32_t *table = reinterpret_cast(buf); // This is the list header, to signal the runtime pseudo relocation v2 // format. - Table[0] = 0; - Table[1] = 0; - Table[2] = 1; - - 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 MergeChunk::Instances; +MergeChunk *MergeChunk::instances[Log2MaxSectionAlignment + 1] = {}; -MergeChunk::MergeChunk(uint32_t Alignment) - : Builder(StringTableBuilder::RAW, Alignment) { - this->Alignment = Alignment; +MergeChunk::MergeChunk(uint32_t alignment) + : builder(StringTableBuilder::RAW, alignment) { + setAlignment(alignment); } -void MergeChunk::addSection(SectionChunk *C) { - auto *&MC = Instances[C->Alignment]; - if (!MC) - MC = make(C->Alignment); - MC->Sections.push_back(C); +void MergeChunk::addSection(SectionChunk *c) { + assert(isPowerOf2_32(c->getAlignment())); + uint8_t p2Align = llvm::Log2_32(c->getAlignment()); + assert(p2Align < array_lengthof(instances)); + auto *&mc = instances[p2Align]; + if (!mc) + mc = make(c->getAlignment()); + mc->sections.push_back(c); } void MergeChunk::finalizeContents() { - if (!Finalized) { - for (SectionChunk *C : Sections) - if (C->Live) - Builder.add(toStringRef(C->getContents())); - Builder.finalize(); - Finalized = true; - } + assert(!finalized && "should only finalize once"); + for (SectionChunk *c : sections) + if (c->live) + builder.add(toStringRef(c->getContents())); + builder.finalize(); + finalized = true; +} - for (SectionChunk *C : Sections) { - if (!C->Live) +void MergeChunk::assignSubsectionRVAs() { + for (SectionChunk *c : sections) { + if (!c->live) continue; - size_t Off = Builder.getOffset(toStringRef(C->getContents())); - C->setOutputSection(Out); - C->setRVA(RVA + Off); - C->OutputSectionOff = OutputSectionOff + Off; + size_t off = builder.getOffset(toStringRef(c->getContents())); + c->setRVA(rva + off); } } @@ -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 f8a0ddd..6bb629f 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 *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 *Res) {} + virtual void getBaserels(std::vector *res) {} // Returns a human-readable name of this chunk. Chunks are unnamed chunks of // bytes, so this is used only for logging or debugging. - virtual StringRef getDebugName() { return ""; } + virtual StringRef getDebugName() const { return ""; } - // The alignment of this chunk. The writer uses the value. - uint32_t Alignment = 1; + static bool classof(const Chunk *c) { return c->kind() != SectionKind; } protected: - Chunk(Kind K = OtherKind) : ChunkKind(K) {} - const Kind ChunkKind; - - // The RVA of this chunk in the output. The writer sets a value. - uint64_t RVA = 0; - - // The output section for this chunk. - OutputSection *Out = nullptr; - -public: - // The offset from beginning of the output section. The writer sets a value. - uint64_t OutputSectionOff = 0; - - // Whether this section needs to be kept distinct from other sections during - // ICF. This is set by the driver using address-significance tables. - bool KeepUnique = false; + NonSectionChunk(Kind k = OtherKind) : Chunk(k) {} }; // A chunk corresponding a section of an input file. @@ -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 getContents() const; - void writeTo(uint8_t *Buf) const override; - bool hasData() const override; - uint32_t getOutputCharacteristics() const override; - StringRef getSectionName() const override { return SectionName; } - void getBaserels(std::vector *Res) override; + void writeTo(uint8_t *buf) const; + + uint32_t getOutputCharacteristics() const { + return header->Characteristics & (permMask | typeMask); + } + StringRef getSectionName() const { + return StringRef(sectionNameData, sectionNameSize); + } + void getBaserels(std::vector *res); bool isCOMDAT() const; - void applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S, - uint64_t P) const; - void applyRelX86(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S, - uint64_t P) const; - void applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S, - uint64_t P) const; - void applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S, - uint64_t P) const; + void applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, + uint64_t p) const; + void applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, + uint64_t p) const; + void applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, + uint64_t p) const; + void applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s, + uint64_t p) const; - void getRuntimePseudoRelocs(std::vector &Res); + void getRuntimePseudoRelocs(std::vector &res); // Called if the garbage collector decides to not include this chunk // in a final output. It's supposed to print out a log message to stdout. @@ -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 symbols() const { - return llvm::make_range(symbol_iterator(File, Relocs.begin()), - symbol_iterator(File, Relocs.end())); + return llvm::make_range(symbol_iterator(file, relocsData), + symbol_iterator(file, relocsData + relocsSize)); + } + + ArrayRef getRelocs() const { + return llvm::makeArrayRef(relocsData, relocsSize); } + // Reloc setter used by ARM range extension thunk insertion. + void setRelocs(ArrayRef newRelocs) { + relocsData = newRelocs.data(); + relocsSize = newRelocs.size(); + assert(relocsSize == newRelocs.size() && "reloc size truncation"); + } + + // Single linked list iterator for associated comdat children. + class AssociatedIterator + : public llvm::iterator_facade_base< + AssociatedIterator, std::forward_iterator_tag, SectionChunk> { + public: + AssociatedIterator() = default; + AssociatedIterator(SectionChunk *head) : cur(head) {} + AssociatedIterator &operator=(const AssociatedIterator &r) { + cur = r.cur; + return *this; + } + bool operator==(const AssociatedIterator &r) const { return cur == r.cur; } + const SectionChunk &operator*() const { return *cur; } + SectionChunk &operator*() { return *cur; } + AssociatedIterator &operator++() { + cur = cur->assocChildren; + return *this; + } + + private: + SectionChunk *cur = nullptr; + }; + // Allow iteration over the associated child chunks for this section. - ArrayRef children() const { return AssocChildren; } + llvm::iterator_range children() const { + return llvm::make_range(AssociatedIterator(assocChildren), + AssociatedIterator(nullptr)); + } // The section ID this chunk belongs to in its Obj. uint32_t getSectionNumber() const; - // A pointer pointing to a replacement for this chunk. - // Initially it points to "this" object. If this chunk is merged - // with other chunk by ICF, it points to another chunk, - // and this chunk is considered as dead. - SectionChunk *Repl; + ArrayRef consumeDebugMagic(); - // The CRC of the contents as described in the COFF spec 4.5.5. - // Auxiliary Format 5: Section Definitions. Used for ICF. - uint32_t Checksum = 0; + static ArrayRef consumeDebugMagic(ArrayRef data, + StringRef sectionName); - const coff_section *Header; + static SectionChunk *findByName(ArrayRef sections, + StringRef name); // The file that this chunk was created from. - ObjFile *File; + ObjFile *file; + + // Pointer to the COFF section header in the input file. + const coff_section *header; // The COMDAT leader symbol if this is a COMDAT chunk. - DefinedRegular *Sym = nullptr; + DefinedRegular *sym = nullptr; - ArrayRef Relocs; + // The CRC of the contents as described in the COFF spec 4.5.5. + // Auxiliary Format 5: Section Definitions. Used for ICF. + uint32_t checksum = 0; // Used by the garbage collector. - bool Live; + bool live; + + // 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 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 AssocChildren; + SectionChunk *assocChildren = nullptr; // Used for ICF (Identical COMDAT Folding) - void replace(SectionChunk *Other); - uint32_t Class[2] = {0, 0}; + void replace(SectionChunk *other); + uint32_t eqClass[2] = {0, 0}; + + // Relocations for this section. Size is stored below. + const coff_relocation *relocsData; + + // Section name string. Size is stored below. + const char *sectionNameData; + + uint32_t relocsSize = 0; + uint32_t sectionNameSize = 0; }; +// Inline methods to implement faux-virtual dispatch for SectionChunk. + +inline size_t Chunk::getSize() const { + if (isa(this)) + return static_cast(this)->getSize(); + else + return static_cast(this)->getSize(); +} + +inline uint32_t Chunk::getOutputCharacteristics() const { + if (isa(this)) + return static_cast(this)->getOutputCharacteristics(); + else + return static_cast(this) + ->getOutputCharacteristics(); +} + +inline void Chunk::writeTo(uint8_t *buf) const { + if (isa(this)) + static_cast(this)->writeTo(buf); + else + static_cast(this)->writeTo(buf); +} + +inline StringRef Chunk::getSectionName() const { + if (isa(this)) + return static_cast(this)->getSectionName(); + else + return static_cast(this)->getSectionName(); +} + +inline void Chunk::getBaserels(std::vector *res) { + if (isa(this)) + static_cast(this)->getBaserels(res); + else + static_cast(this)->getBaserels(res); +} + +inline StringRef Chunk::getDebugName() const { + if (isa(this)) + return static_cast(this)->getDebugName(); + else + return static_cast(this)->getDebugName(); +} + // This class is used to implement an lld-specific feature (not implemented in // MSVC) that minimizes the output size by finding string literals sharing tail // parts and merging them. @@ -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 Instances; - std::vector Sections; + static MergeChunk *instances[Log2MaxSectionAlignment + 1]; + std::vector sections; private: - llvm::StringTableBuilder Builder; - bool Finalized = false; + llvm::StringTableBuilder builder; + bool finalized = false; }; // A chunk for common symbols. Common chunks don't have actual data. -class CommonChunk : public Chunk { +class CommonChunk : public NonSectionChunk { public: - CommonChunk(const COFFSymbolRef Sym); - size_t getSize() const override { return Sym.getValue(); } - bool hasData() const override { return false; } + CommonChunk(const COFFSymbolRef sym); + size_t getSize() const override { return sym.getValue(); } uint32_t getOutputCharacteristics() const override; StringRef getSectionName() const override { return ".bss"; } private: - const COFFSymbolRef Sym; + const COFFSymbolRef sym; }; // A chunk for linker-created strings. -class StringChunk : public Chunk { +class StringChunk : public NonSectionChunk { public: - explicit StringChunk(StringRef S) : Str(S) {} - size_t getSize() const override { return Str.size() + 1; } - void writeTo(uint8_t *Buf) const override; + explicit StringChunk(StringRef s) : str(s) {} + size_t getSize() const override { return str.size() + 1; } + void writeTo(uint8_t *buf) const override; private: - StringRef Str; + StringRef str; }; -static const uint8_t ImportThunkX86[] = { +static const uint8_t importThunkX86[] = { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // JMP *0x0 }; -static const uint8_t ImportThunkARM[] = { +static const uint8_t importThunkARM[] = { 0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 0xdc, 0xf8, 0x00, 0xf0, // ldr.w pc, [ip] }; -static const uint8_t ImportThunkARM64[] = { +static const uint8_t importThunkARM64[] = { 0x10, 0x00, 0x00, 0x90, // adrp x16, #0 0x10, 0x02, 0x40, 0xf9, // ldr x16, [x16] 0x00, 0x02, 0x1f, 0xd6, // br x16 @@ -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 *Res) override; - void writeTo(uint8_t *Buf) const override; + explicit ImportThunkChunkX64(Defined *s); + size_t getSize() const override { return sizeof(importThunkX86); } + void writeTo(uint8_t *buf) const override; +}; -private: - Defined *ImpSymbol; +class ImportThunkChunkX86 : public ImportThunkChunk { +public: + explicit ImportThunkChunkX86(Defined *s) : ImportThunkChunk(s) {} + size_t getSize() const override { return sizeof(importThunkX86); } + void getBaserels(std::vector *res) override; + void writeTo(uint8_t *buf) const override; }; -class ImportThunkChunkARM : public Chunk { +class ImportThunkChunkARM : public ImportThunkChunk { public: - explicit ImportThunkChunkARM(Defined *S) : ImpSymbol(S) {} - size_t getSize() const override { return sizeof(ImportThunkARM); } - void getBaserels(std::vector *Res) override; - void writeTo(uint8_t *Buf) const override; + explicit ImportThunkChunkARM(Defined *s) : ImportThunkChunk(s) {} + size_t getSize() const override { return sizeof(importThunkARM); } + void getBaserels(std::vector *res) override; + void writeTo(uint8_t *buf) const override; +}; -private: - Defined *ImpSymbol; +class ImportThunkChunkARM64 : public ImportThunkChunk { +public: + explicit ImportThunkChunkARM64(Defined *s) : ImportThunkChunk(s) {} + size_t getSize() const override { return sizeof(importThunkARM64); } + void writeTo(uint8_t *buf) const override; }; -class ImportThunkChunkARM64 : public Chunk { +class RangeExtensionThunkARM : public NonSectionChunk { public: - explicit ImportThunkChunkARM64(Defined *S) : ImpSymbol(S) {} - size_t getSize() const override { return sizeof(ImportThunkARM64); } - void writeTo(uint8_t *Buf) const override; + explicit RangeExtensionThunkARM(Defined *t) : target(t) {} + size_t getSize() const override; + void writeTo(uint8_t *buf) const override; -private: - Defined *ImpSymbol; + Defined *target; }; -class RangeExtensionThunk : public Chunk { +class RangeExtensionThunkARM64 : public NonSectionChunk { public: - explicit RangeExtensionThunk(Defined *T) : Target(T) {} + explicit RangeExtensionThunkARM64(Defined *t) : target(t) {} size_t getSize() const override; - void writeTo(uint8_t *Buf) const override; + void writeTo(uint8_t *buf) const override; - Defined *Target; + Defined *target; }; // Windows-specific. // See comments for DefinedLocalImport class. -class LocalImportChunk : public Chunk { +class LocalImportChunk : public NonSectionChunk { public: - explicit LocalImportChunk(Defined *S) : Sym(S) { - Alignment = Config->Wordsize; + explicit LocalImportChunk(Defined *s) : sym(s) { + setAlignment(config->wordsize); } size_t getSize() const override; - void getBaserels(std::vector *Res) override; - void writeTo(uint8_t *Buf) const override; + void getBaserels(std::vector *res) override; + void writeTo(uint8_t *buf) const override; private: - Defined *Sym; + Defined *sym; }; // Duplicate RVAs are not allowed in RVA tables, so unique symbols by chunk and // offset into the chunk. Order does not matter as the RVA table will be sorted // later. struct ChunkAndOffset { - Chunk *InputChunk; - uint32_t Offset; + Chunk *inputChunk; + uint32_t offset; struct DenseMapInfo { static ChunkAndOffset getEmptyKey() { @@ -393,12 +550,12 @@ struct ChunkAndOffset { static ChunkAndOffset getTombstoneKey() { return {llvm::DenseMapInfo::getTombstoneKey(), 0}; } - static unsigned getHashValue(const ChunkAndOffset &CO) { + static unsigned getHashValue(const ChunkAndOffset &co) { return llvm::DenseMapInfo>::getHashValue( - {CO.InputChunk, CO.Offset}); + {co.inputChunk, co.offset}); } - static bool isEqual(const ChunkAndOffset &LHS, const ChunkAndOffset &RHS) { - return LHS.InputChunk == RHS.InputChunk && LHS.Offset == RHS.Offset; + static bool isEqual(const ChunkAndOffset &lhs, const ChunkAndOffset &rhs) { + return lhs.inputChunk == rhs.inputChunk && lhs.offset == rhs.offset; } }; }; @@ -406,48 +563,48 @@ struct ChunkAndOffset { using SymbolRVASet = llvm::DenseSet; // Table which contains symbol RVAs. Used for /safeseh and /guard:cf. -class RVATableChunk : public Chunk { +class RVATableChunk : public NonSectionChunk { public: - explicit RVATableChunk(SymbolRVASet S) : Syms(std::move(S)) {} - size_t getSize() const override { return Syms.size() * 4; } - void writeTo(uint8_t *Buf) const override; + explicit RVATableChunk(SymbolRVASet s) : syms(std::move(s)) {} + size_t getSize() const override { return syms.size() * 4; } + void writeTo(uint8_t *buf) const override; private: - SymbolRVASet Syms; + SymbolRVASet syms; }; // Windows-specific. // This class represents a block in .reloc section. // See the PE/COFF spec 5.6 for details. -class BaserelChunk : public Chunk { +class BaserelChunk : public NonSectionChunk { public: - BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End); - size_t getSize() const override { return Data.size(); } - void writeTo(uint8_t *Buf) const override; + BaserelChunk(uint32_t page, Baserel *begin, Baserel *end); + size_t getSize() const override { return data.size(); } + void writeTo(uint8_t *buf) const override; private: - std::vector Data; + std::vector data; }; class Baserel { public: - Baserel(uint32_t V, uint8_t Ty) : RVA(V), Type(Ty) {} - explicit Baserel(uint32_t V) : Baserel(V, getDefaultType()) {} + Baserel(uint32_t v, uint8_t ty) : rva(v), type(ty) {} + explicit Baserel(uint32_t v) : Baserel(v, getDefaultType()) {} uint8_t getDefaultType(); - uint32_t RVA; - uint8_t Type; + uint32_t rva; + uint8_t type; }; // This is a placeholder Chunk, to allow attaching a DefinedSynthetic to a // specific place in a section, without any data. This is used for the MinGW // specific symbol __RUNTIME_PSEUDO_RELOC_LIST_END__, even though the concept // of an empty chunk isn't MinGW specific. -class EmptyChunk : public Chunk { +class EmptyChunk : public NonSectionChunk { public: EmptyChunk() {} size_t getSize() const override { return 0; } - void writeTo(uint8_t *Buf) const override {} + void writeTo(uint8_t *buf) const override {} }; // MinGW specific, for the "automatic import of variables from DLLs" feature. @@ -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 &Relocs) - : Relocs(std::move(Relocs)) { - Alignment = 4; + PseudoRelocTableChunk(std::vector &relocs) + : relocs(std::move(relocs)) { + setAlignment(4); } size_t getSize() const override; - void writeTo(uint8_t *Buf) const override; + void writeTo(uint8_t *buf) const override; private: - std::vector Relocs; + std::vector relocs; }; // MinGW specific; information about one individual location in the image @@ -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(this)) + return sc->file->hotPatchable; + else if (isa(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 8915b6a..1b0e240 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(DebugType::None); - std::vector NatvisFiles; - llvm::SmallString<128> PDBAltPath; - llvm::SmallString<128> PDBPath; - llvm::SmallString<128> PDBSourcePath; - std::vector 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(DebugType::None); + std::vector natvisFiles; + llvm::SmallString<128> pdbAltPath; + llvm::SmallString<128> pdbPath; + llvm::SmallString<128> pdbSourcePath; + std::vector argv; // Symbols in this set are considered as live by the garbage collector. - std::vector GCRoot; + std::vector gcroot; - std::set NoDefaultLibs; - bool NoDefaultLibAll = false; + std::set noDefaultLibs; + bool noDefaultLibAll = false; // True if we are creating a DLL. - bool DLL = false; - StringRef Implib; - std::vector Exports; - std::set DelayLoads; - std::map DLLOrder; - Symbol *DelayLoadHelper = nullptr; + bool dll = false; + StringRef implib; + std::vector exports; + std::set delayLoads; + std::map dllOrder; + Symbol *delayLoadHelper = nullptr; - bool SaveTemps = false; + bool saveTemps = false; // /guard:cf - GuardCFLevel GuardCF = GuardCFLevel::Off; + GuardCFLevel guardCF = GuardCFLevel::Off; // Used for SafeSEH. - Symbol *SEHTable = nullptr; - Symbol *SEHCount = nullptr; + bool safeSEH = false; + Symbol *sehTable = nullptr; + Symbol *sehCount = nullptr; // Used for /opt:lldlto=N - unsigned LTOO = 2; + unsigned ltoo = 2; // Used for /opt:lldltojobs=N - unsigned ThinLTOJobs = 0; + unsigned thinLTOJobs = 0; // Used for /opt:lldltopartitions=N - unsigned LTOPartitions = 1; + unsigned ltoPartitions = 1; // Used for /opt:lldltocache=path - StringRef LTOCache; + StringRef ltoCache; // Used for /opt:lldltocachepolicy=policy - llvm::CachePruningPolicy LTOCachePolicy; + llvm::CachePruningPolicy ltoCachePolicy; // Used for /merge:from=to (e.g. /merge:.rdata=.text) - std::map Merge; + std::map merge; // Used for /section=.name,{DEKPRSW} to set section attributes. - std::map Section; + std::map section; // Options for manifest files. - ManifestKind Manifest = No; - int ManifestID = 1; - StringRef ManifestDependency; - bool ManifestUAC = true; - std::vector ManifestInput; - StringRef ManifestLevel = "'asInvoker'"; - StringRef ManifestUIAccess = "'false'"; - StringRef ManifestFile; + ManifestKind manifest = No; + int manifestID = 1; + StringRef manifestDependency; + bool manifestUAC = true; + std::vector manifestInput; + StringRef manifestLevel = "'asInvoker'"; + StringRef manifestUIAccess = "'false'"; + StringRef manifestFile; // Used for /aligncomm. - std::map AlignComm; + std::map alignComm; // Used for /failifmismatch. - std::map MustMatch; + std::map> mustMatch; // Used for /alternatename. - std::map AlternateNames; + std::map alternateNames; // Used for /order. - llvm::StringMap Order; + llvm::StringMap order; // Used for /lldmap. - std::string MapFile; - - 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 thinLTOPrefixReplace; + + // Used for /thinlto-object-suffix-replace: + std::pair thinLTOObjectSuffixReplace; + + uint64_t imageBase = -1; + uint64_t fileAlign = 512; + uint64_t stackReserve = 1024 * 1024; + uint64_t stackCommit = 4096; + uint64_t heapReserve = 1024 * 1024; + uint64_t heapCommit = 4096; + uint32_t majorImageVersion = 0; + uint32_t minorImageVersion = 0; + uint32_t majorOSVersion = 6; + uint32_t minorOSVersion = 0; + uint32_t timestamp = 0; + uint32_t functionPadMin = 0; + bool dynamicBase = true; + bool allowBind = true; + bool nxCompat = true; + bool allowIsolation = true; + bool terminalServerAware = true; + bool largeAddressAware = false; + bool highEntropyVA = false; + bool appContainer = false; + bool mingw = false; + bool warnMissingOrderSymbol = true; + bool warnLocallyDefinedImported = true; + bool warnDebugInfoUnusable = true; + bool incremental = true; + bool integrityCheck = false; + bool killAt = false; + bool repro = false; + bool swaprunCD = false; + bool swaprunNet = false; + bool thinLTOEmitImportsFiles; + bool thinLTOIndexOnly; }; -extern Configuration *Config; +extern Configuration *config; } // namespace coff } // namespace lld diff --git a/COFF/DLL.cpp b/COFF/DLL.cpp index 599cc58..40d1f46 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> -binImports(const std::vector &Imports) { +binImports(const std::vector &imports) { // Group DLL-imported symbols by DLL name because that's how // symbols are layed out in the import descriptor table. - auto Less = [](const std::string &A, const std::string &B) { - return Config->DLLOrder[A] < Config->DLLOrder[B]; + auto less = [](const std::string &a, const std::string &b) { + return config->dllOrder[a] < config->dllOrder[b]; }; std::map, - bool(*)(const std::string &, const std::string &)> M(Less); - for (DefinedImportData *Sym : Imports) - M[Sym->getDLLName().lower()].push_back(Sym); + bool(*)(const std::string &, const std::string &)> m(less); + for (DefinedImportData *sym : imports) + m[sym->getDLLName().lower()].push_back(sym); - std::vector> V; - for (auto &KV : M) { + std::vector> v; + for (auto &kv : m) { // Sort symbols by name for each group. - std::vector &Syms = KV.second; - std::sort(Syms.begin(), Syms.end(), - [](DefinedImportData *A, DefinedImportData *B) { - return A->getName() < B->getName(); + std::vector &syms = kv.second; + std::sort(syms.begin(), syms.end(), + [](DefinedImportData *a, DefinedImportData *b) { + return a->getName() < b->getName(); }); - V.push_back(std::move(Syms)); + v.push_back(std::move(syms)); } - return V; + return v; } // Export table // See Microsoft PE/COFF spec 4.3 for details. // A chunk for the delay import descriptor table etnry. -class DelayDirectoryChunk : public Chunk { +class DelayDirectoryChunk : public NonSectionChunk { public: - explicit DelayDirectoryChunk(Chunk *N) : DLLName(N) {} + explicit DelayDirectoryChunk(Chunk *n) : dllName(n) {} size_t getSize() const override { return sizeof(delay_import_directory_table_entry); } - void writeTo(uint8_t *Buf) const override { - auto *E = (delay_import_directory_table_entry *)(Buf + OutputSectionOff); - E->Attributes = 1; - E->Name = DLLName->getRVA(); - E->ModuleHandle = ModuleHandle->getRVA(); - E->DelayImportAddressTable = AddressTab->getRVA(); - E->DelayImportNameTable = NameTab->getRVA(); + void writeTo(uint8_t *buf) const override { + memset(buf, 0, getSize()); + + auto *e = (delay_import_directory_table_entry *)(buf); + e->Attributes = 1; + e->Name = dllName->getRVA(); + e->ModuleHandle = moduleHandle->getRVA(); + e->DelayImportAddressTable = addressTab->getRVA(); + e->DelayImportNameTable = nameTab->getRVA(); } - Chunk *DLLName; - Chunk *ModuleHandle; - Chunk *AddressTab; - Chunk *NameTab; + Chunk *dllName; + Chunk *moduleHandle; + Chunk *addressTab; + Chunk *nameTab; }; // Initial contents for delay-loaded functions. // This code calls __delayLoadHelper2 function to resolve a symbol // and then overwrites its jump table slot with the result // for subsequent function calls. -static const uint8_t ThunkX64[] = { +static const uint8_t thunkX64[] = { + 0x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_] + 0xE9, 0, 0, 0, 0, // jmp __tailMerge_ +}; + +static const uint8_t tailMergeX64[] = { 0x51, // push rcx 0x52, // push rdx 0x41, 0x50, // push r8 @@ -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_] + 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__ + 0xE9, 0, 0, 0, 0, // jmp __tailMerge_ +}; + +static const uint8_t tailMergeX86[] = { 0x51, // push ecx 0x52, // push edx - 0x68, 0, 0, 0, 0, // push offset ___imp__ + 0x50, // push eax 0x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR__dll 0xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@8 0x5A, // pop edx @@ -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_ 0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 __imp_ + 0x00, 0xf0, 0x00, 0xb8, // b.w __tailMerge_ +}; + +static const uint8_t tailMergeARM[] = { 0x2d, 0xe9, 0x0f, 0x48, // push.w {r0, r1, r2, r3, r11, lr} 0x0d, 0xf2, 0x10, 0x0b, // addw r11, sp, #16 0x2d, 0xed, 0x10, 0x0b, // vpush {d0, d1, d2, d3, d4, d5, d6, d7} @@ -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_ 0x31, 0x02, 0x00, 0x91, // add x17, x17, #0 :lo12:__imp_ + 0x00, 0x00, 0x00, 0x14, // b __tailMerge_ +}; + +static const uint8_t tailMergeARM64[] = { 0xfd, 0x7b, 0xb3, 0xa9, // stp x29, x30, [sp, #-208]! 0xfd, 0x03, 0x00, 0x91, // mov x29, sp 0xe0, 0x07, 0x01, 0xa9, // stp x0, x1, [sp, #16] @@ -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 *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 *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 *Res) override { - Res->emplace_back(RVA + 3); - Res->emplace_back(RVA + 8); + void getBaserels(std::vector *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 *Res) override { - Res->emplace_back(RVA + 0, IMAGE_REL_BASED_ARM_MOV32T); - Res->emplace_back(RVA + 22, IMAGE_REL_BASED_ARM_MOV32T); + void getBaserels(std::vector *res) override { + res->emplace_back(rva + 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 *Res) override { - Res->emplace_back(RVA); + void getBaserels(std::vector *res) override { + res->emplace_back(rva); } - Chunk *Thunk; + Chunk *thunk; }; // Export table // Read Microsoft PE/COFF spec 5.3 for details. // A chunk for the export descriptor table. -class ExportDirectoryChunk : public Chunk { +class ExportDirectoryChunk : public NonSectionChunk { public: - ExportDirectoryChunk(int I, int J, Chunk *D, Chunk *A, Chunk *N, Chunk *O) - : MaxOrdinal(I), NameTabSize(J), DLLName(D), AddressTab(A), NameTab(N), - OrdinalTab(O) {} + ExportDirectoryChunk(int i, int j, Chunk *d, Chunk *a, Chunk *n, Chunk *o) + : maxOrdinal(i), nameTabSize(j), dllName(d), addressTab(a), nameTab(n), + ordinalTab(o) {} size_t getSize() const override { return sizeof(export_directory_table_entry); } - void writeTo(uint8_t *Buf) const override { - auto *E = (export_directory_table_entry *)(Buf + OutputSectionOff); - E->NameRVA = DLLName->getRVA(); - E->OrdinalBase = 0; - E->AddressTableEntries = MaxOrdinal + 1; - E->NumberOfNamePointers = NameTabSize; - E->ExportAddressTableRVA = AddressTab->getRVA(); - E->NamePointerRVA = NameTab->getRVA(); - E->OrdinalTableRVA = OrdinalTab->getRVA(); + void writeTo(uint8_t *buf) const override { + memset(buf, 0, getSize()); + + auto *e = (export_directory_table_entry *)(buf); + e->NameRVA = dllName->getRVA(); + e->OrdinalBase = 0; + e->AddressTableEntries = maxOrdinal + 1; + e->NumberOfNamePointers = nameTabSize; + e->ExportAddressTableRVA = addressTab->getRVA(); + e->NamePointerRVA = nameTab->getRVA(); + e->OrdinalTableRVA = ordinalTab->getRVA(); } - uint16_t MaxOrdinal; - uint16_t NameTabSize; - Chunk *DLLName; - Chunk *AddressTab; - Chunk *NameTab; - Chunk *OrdinalTab; + uint16_t maxOrdinal; + uint16_t nameTabSize; + Chunk *dllName; + Chunk *addressTab; + Chunk *nameTab; + Chunk *ordinalTab; }; -class AddressTableChunk : public Chunk { +class AddressTableChunk : public NonSectionChunk { public: - explicit AddressTableChunk(size_t MaxOrdinal) : Size(MaxOrdinal + 1) {} - size_t getSize() const override { return Size * 4; } + explicit AddressTableChunk(size_t maxOrdinal) : size(maxOrdinal + 1) {} + size_t getSize() const override { return size * 4; } - void writeTo(uint8_t *Buf) const override { - memset(Buf + OutputSectionOff, 0, getSize()); + void writeTo(uint8_t *buf) const override { + memset(buf, 0, getSize()); - for (const Export &E : Config->Exports) { - uint8_t *P = Buf + OutputSectionOff + E.Ordinal * 4; - uint32_t Bit = 0; + for (const Export &e : config->exports) { + uint8_t *p = buf + e.ordinal * 4; + uint32_t bit = 0; // Pointer to thumb code must have the LSB set, so adjust it. - if (Config->Machine == ARMNT && !E.Data) - Bit = 1; - if (E.ForwardChunk) { - write32le(P, E.ForwardChunk->getRVA() | Bit); + if (config->machine == ARMNT && !e.data) + bit = 1; + if (e.forwardChunk) { + write32le(p, e.forwardChunk->getRVA() | bit); } else { - write32le(P, cast(E.Sym)->getRVA() | Bit); + write32le(p, cast(e.sym)->getRVA() | bit); } } } private: - size_t Size; + size_t size; }; -class NamePointersChunk : public Chunk { +class NamePointersChunk : public NonSectionChunk { public: - explicit NamePointersChunk(std::vector &V) : Chunks(V) {} - size_t getSize() const override { return Chunks.size() * 4; } - - 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 &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 Chunks; + std::vector chunks; }; -class ExportOrdinalChunk : public Chunk { +class ExportOrdinalChunk : public NonSectionChunk { public: - explicit ExportOrdinalChunk(size_t I) : Size(I) {} - size_t getSize() const override { return Size * 2; } + explicit ExportOrdinalChunk(size_t i) : size(i) {} + size_t getSize() const override { return size * 2; } - void writeTo(uint8_t *Buf) const override { - uint8_t *P = Buf + OutputSectionOff; - for (Export &E : Config->Exports) { - if (E.Noname) + void writeTo(uint8_t *buf) const override { + for (Export &e : config->exports) { + if (e.noname) continue; - write16le(P, E.Ordinal); - P += 2; + write16le(buf, e.ordinal); + buf += 2; } } private: - size_t Size; + size_t size; }; } // anonymous namespace void IdataContents::create() { - std::vector> V = binImports(Imports); + std::vector> v = binImports(imports); // Create .idata contents for each DLL. - for (std::vector &Syms : V) { + for (std::vector &syms : v) { // Create lookup and address tables. If they have external names, - // we need to create HintName chunks to store the names. + // we need to create hintName chunks to store the names. // If they don't (if they are import-by-ordinals), we store only // ordinal values to the table. - size_t Base = Lookups.size(); - for (DefinedImportData *S : Syms) { - uint16_t Ord = S->getOrdinal(); - if (S->getExternalName().empty()) { - Lookups.push_back(make(Ord)); - Addresses.push_back(make(Ord)); + size_t base = lookups.size(); + for (DefinedImportData *s : syms) { + uint16_t ord = s->getOrdinal(); + if (s->getExternalName().empty()) { + lookups.push_back(make(ord)); + addresses.push_back(make(ord)); continue; } - auto *C = make(S->getExternalName(), Ord); - Lookups.push_back(make(C)); - Addresses.push_back(make(C)); - Hints.push_back(C); + auto *c = make(s->getExternalName(), ord); + lookups.push_back(make(c)); + addresses.push_back(make(c)); + hints.push_back(c); } // Terminate with null values. - Lookups.push_back(make(Config->Wordsize)); - Addresses.push_back(make(Config->Wordsize)); + lookups.push_back(make(config->wordsize)); + addresses.push_back(make(config->wordsize)); - for (int I = 0, E = Syms.size(); I < E; ++I) - Syms[I]->setLocation(Addresses[Base + I]); + for (int i = 0, e = syms.size(); i < e; ++i) + syms[i]->setLocation(addresses[base + i]); // Create the import table header. - DLLNames.push_back(make(Syms[0]->getDLLName())); - auto *Dir = make(DLLNames.back()); - Dir->LookupTab = Lookups[Base]; - Dir->AddressTab = Addresses[Base]; - Dirs.push_back(Dir); + dllNames.push_back(make(syms[0]->getDLLName())); + auto *dir = make(dllNames.back()); + dir->lookupTab = lookups[base]; + dir->addressTab = addresses[base]; + dirs.push_back(dir); } // Add null terminator. - Dirs.push_back(make(sizeof(ImportDirectoryTableEntry))); + dirs.push_back(make(sizeof(ImportDirectoryTableEntry))); } std::vector DelayLoadContents::getChunks() { - std::vector V; - V.insert(V.end(), Dirs.begin(), Dirs.end()); - V.insert(V.end(), Names.begin(), Names.end()); - V.insert(V.end(), HintNames.begin(), HintNames.end()); - V.insert(V.end(), DLLNames.begin(), DLLNames.end()); - return V; + std::vector v; + v.insert(v.end(), dirs.begin(), dirs.end()); + v.insert(v.end(), names.begin(), names.end()); + v.insert(v.end(), hintNames.begin(), hintNames.end()); + v.insert(v.end(), dllNames.begin(), dllNames.end()); + return v; } std::vector DelayLoadContents::getDataChunks() { - std::vector V; - V.insert(V.end(), ModuleHandles.begin(), ModuleHandles.end()); - V.insert(V.end(), Addresses.begin(), Addresses.end()); - return V; + std::vector v; + v.insert(v.end(), moduleHandles.begin(), moduleHandles.end()); + v.insert(v.end(), addresses.begin(), addresses.end()); + return v; } uint64_t DelayLoadContents::getDirSize() { - return Dirs.size() * sizeof(delay_import_directory_table_entry); + return dirs.size() * sizeof(delay_import_directory_table_entry); } -void DelayLoadContents::create(Defined *H) { - Helper = H; - std::vector> V = binImports(Imports); +void DelayLoadContents::create(Defined *h) { + helper = h; + std::vector> v = binImports(imports); // Create .didat contents for each DLL. - for (std::vector &Syms : V) { + for (std::vector &syms : v) { // Create the delay import table header. - DLLNames.push_back(make(Syms[0]->getDLLName())); - auto *Dir = make(DLLNames.back()); - - size_t Base = Addresses.size(); - for (DefinedImportData *S : Syms) { - Chunk *T = newThunkChunk(S, Dir); - auto *A = make(T); - Addresses.push_back(A); - Thunks.push_back(T); - StringRef ExtName = S->getExternalName(); - if (ExtName.empty()) { - Names.push_back(make(S->getOrdinal())); + dllNames.push_back(make(syms[0]->getDLLName())); + auto *dir = make(dllNames.back()); + + size_t base = addresses.size(); + Chunk *tm = newTailMergeChunk(dir); + for (DefinedImportData *s : syms) { + Chunk *t = newThunkChunk(s, tm); + auto *a = make(t); + addresses.push_back(a); + thunks.push_back(t); + StringRef extName = s->getExternalName(); + if (extName.empty()) { + names.push_back(make(s->getOrdinal())); } else { - auto *C = make(ExtName, 0); - Names.push_back(make(C)); - HintNames.push_back(C); + auto *c = make(extName, 0); + names.push_back(make(c)); + hintNames.push_back(c); } } + thunks.push_back(tm); // Terminate with null values. - Addresses.push_back(make(8)); - Names.push_back(make(8)); + addresses.push_back(make(8)); + names.push_back(make(8)); - for (int I = 0, E = Syms.size(); I < E; ++I) - Syms[I]->setLocation(Addresses[Base + I]); - auto *MH = make(8); - MH->Alignment = 8; - ModuleHandles.push_back(MH); + for (int i = 0, e = syms.size(); i < e; ++i) + syms[i]->setLocation(addresses[base + i]); + auto *mh = make(8); + mh->setAlignment(8); + moduleHandles.push_back(mh); // Fill the delay import table header fields. - Dir->ModuleHandle = MH; - Dir->AddressTab = Addresses[Base]; - Dir->NameTab = Names[Base]; - Dirs.push_back(Dir); + dir->moduleHandle = mh; + dir->addressTab = addresses[base]; + dir->nameTab = names[base]; + dirs.push_back(dir); } // Add null terminator. - Dirs.push_back(make(sizeof(delay_import_directory_table_entry))); + dirs.push_back(make(sizeof(delay_import_directory_table_entry))); +} + +Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) { + switch (config->machine) { + case AMD64: + return make(dir, helper); + case I386: + return make(dir, helper); + case ARMNT: + return make(dir, helper); + case ARM64: + return make(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(S, Dir, Helper); + return make(s, tailMerge); case I386: - return make(S, Dir, Helper); + return make(s, tailMerge); case ARMNT: - return make(S, Dir, Helper); + return make(s, tailMerge); case ARM64: - return make(S, Dir, Helper); + return make(s, tailMerge); default: llvm_unreachable("unsupported machine type"); } } EdataContents::EdataContents() { - uint16_t MaxOrdinal = 0; - for (Export &E : Config->Exports) - MaxOrdinal = std::max(MaxOrdinal, E.Ordinal); - - auto *DLLName = make(sys::path::filename(Config->OutputFile)); - auto *AddressTab = make(MaxOrdinal); - std::vector Names; - for (Export &E : Config->Exports) - if (!E.Noname) - Names.push_back(make(E.ExportName)); - - std::vector Forwards; - for (Export &E : Config->Exports) { - if (E.ForwardTo.empty()) + uint16_t maxOrdinal = 0; + for (Export &e : config->exports) + maxOrdinal = std::max(maxOrdinal, e.ordinal); + + auto *dllName = make(sys::path::filename(config->outputFile)); + auto *addressTab = make(maxOrdinal); + std::vector names; + for (Export &e : config->exports) + if (!e.noname) + names.push_back(make(e.exportName)); + + std::vector forwards; + for (Export &e : config->exports) { + if (e.forwardTo.empty()) continue; - E.ForwardChunk = make(E.ForwardTo); - Forwards.push_back(E.ForwardChunk); - } - - auto *NameTab = make(Names); - auto *OrdinalTab = make(Names.size()); - auto *Dir = make(MaxOrdinal, Names.size(), DLLName, - AddressTab, NameTab, OrdinalTab); - Chunks.push_back(Dir); - Chunks.push_back(DLLName); - Chunks.push_back(AddressTab); - Chunks.push_back(NameTab); - Chunks.push_back(OrdinalTab); - Chunks.insert(Chunks.end(), Names.begin(), Names.end()); - Chunks.insert(Chunks.end(), Forwards.begin(), Forwards.end()); + e.forwardChunk = make(e.forwardTo); + forwards.push_back(e.forwardChunk); + } + + auto *nameTab = make(names); + auto *ordinalTab = make(names.size()); + auto *dir = make(maxOrdinal, names.size(), dllName, + addressTab, nameTab, ordinalTab); + chunks.push_back(dir); + chunks.push_back(dllName); + chunks.push_back(addressTab); + chunks.push_back(nameTab); + chunks.push_back(ordinalTab); + chunks.insert(chunks.end(), names.begin(), names.end()); + chunks.insert(chunks.end(), forwards.begin(), forwards.end()); } } // namespace coff diff --git a/COFF/DLL.h b/COFF/DLL.h index a298271..ce0ee01 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 Imports; - std::vector Dirs; - std::vector Lookups; - std::vector Addresses; - std::vector Hints; - std::vector DLLNames; + std::vector imports; + std::vector dirs; + std::vector lookups; + std::vector addresses; + std::vector hints; + std::vector dllNames; }; // Windows-specific. // DelayLoadContents creates all chunks for the delay-load DLL import table. class DelayLoadContents { public: - void add(DefinedImportData *Sym) { Imports.push_back(Sym); } - bool empty() { return Imports.empty(); } - void create(Defined *Helper); + void add(DefinedImportData *sym) { imports.push_back(sym); } + bool empty() { return imports.empty(); } + void create(Defined *helper); std::vector getChunks(); std::vector getDataChunks(); - ArrayRef getCodeChunks() { return Thunks; } + ArrayRef getCodeChunks() { return thunks; } - uint64_t getDirRVA() { return Dirs[0]->getRVA(); } + uint64_t getDirRVA() { return dirs[0]->getRVA(); } uint64_t getDirSize(); private: - Chunk *newThunkChunk(DefinedImportData *S, Chunk *Dir); + Chunk *newThunkChunk(DefinedImportData *s, Chunk *tailMerge); + Chunk *newTailMergeChunk(Chunk *dir); - Defined *Helper; - std::vector Imports; - std::vector Dirs; - std::vector ModuleHandles; - std::vector Addresses; - std::vector Names; - std::vector HintNames; - std::vector Thunks; - std::vector DLLNames; + Defined *helper; + std::vector imports; + std::vector dirs; + std::vector moduleHandles; + std::vector addresses; + std::vector names; + std::vector hintNames; + std::vector thunks; + std::vector dllNames; }; // Windows-specific. @@ -68,11 +68,11 @@ private: class EdataContents { public: EdataContents(); - std::vector Chunks; + std::vector chunks; - uint64_t getRVA() { return Chunks[0]->getRVA(); } + uint64_t getRVA() { return chunks[0]->getRVA(); } uint64_t getSize() { - return Chunks.back()->getRVA() + Chunks.back()->getSize() - getRVA(); + return chunks.back()->getRVA() + chunks.back()->getSize() - getRVA(); } }; diff --git a/COFF/DebugTypes.cpp b/COFF/DebugTypes.cpp new file mode 100644 index 0000000..78c1c78 --- /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 getInstance(MemoryBufferRef m); + + // Fetch the PDB instance loaded for a corresponding dependent OBJ. + static Expected + findFromFile(const ObjFile *dependentFile); + + static std::map> + instances; + + // The interface to the PDB (if it was opened successfully) + std::unique_ptr session; + +private: + MemoryBufferRef mb; +}; + +// This class represents the debug type stream of an OBJ file that depends on a +// PDB type server (see TypeServerSource). +class UseTypeServerSource : public TpiSource { +public: + UseTypeServerSource(const ObjFile *f, const TypeServer2Record *ts) + : TpiSource(UsingPDB, f), typeServerDependency(*ts) {} + + // Information about the PDB type server dependency, that needs to be loaded + // in before merging this OBJ. + TypeServer2Record typeServerDependency; +}; + +// This class represents the debug type stream of a Microsoft precompiled +// headers OBJ (PCH OBJ). This OBJ kind needs to be merged first in the output +// PDB, before any other OBJs that depend on this. Note that only MSVC generate +// such files, clang does not. +class PrecompSource : public TpiSource { +public: + PrecompSource(const ObjFile *f) : TpiSource(PCH, f) {} +}; + +// This class represents the debug type stream of an OBJ file that depends on a +// Microsoft precompiled headers OBJ (see PrecompSource). +class UsePrecompSource : public TpiSource { +public: + UsePrecompSource(const ObjFile *f, const PrecompRecord *precomp) + : TpiSource(UsingPCH, f), precompDependency(*precomp) {} + + // Information about the Precomp OBJ dependency, that needs to be loaded in + // before merging this OBJ. + PrecompRecord precompDependency; +}; +} // namespace + +static std::vector> GC; + +TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) { + GC.push_back(std::unique_ptr(this)); +} + +TpiSource *lld::coff::makeTpiSource(const ObjFile *f) { + return new TpiSource(TpiSource::Regular, f); +} + +TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *f, + const TypeServer2Record *ts) { + TypeServerSource::enqueue(f, *ts); + return new UseTypeServerSource(f, ts); +} + +TpiSource *lld::coff::makePrecompSource(const ObjFile *f) { + return new PrecompSource(f); +} + +TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *f, + const PrecompRecord *precomp) { + return new UsePrecompSource(f, precomp); +} + +namespace lld { +namespace coff { +template <> +const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) { + assert(source->kind == TpiSource::UsingPCH); + return ((const UsePrecompSource *)source)->precompDependency; +} + +template <> +const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) { + assert(source->kind == TpiSource::UsingPDB); + return ((const UseTypeServerSource *)source)->typeServerDependency; +} +} // namespace coff +} // namespace lld + +std::map> + TypeServerSource::instances; + +// Make a PDB path assuming the PDB is in the same folder as the OBJ +static std::string getPdbBaseName(const ObjFile *file, StringRef tSPath) { + StringRef localPath = + !file->parentName.empty() ? file->parentName : file->getName(); + SmallString<128> path = sys::path::parent_path(localPath); + + // Currently, type server PDBs are only created by MSVC cl, which only runs + // on Windows, so we can assume type server paths are Windows style. + sys::path::append(path, sys::path::filename(tSPath, sys::path::Style::windows)); + return path.str(); +} + +// The casing of the PDB path stamped in the OBJ can differ from the actual path +// on disk. With this, we ensure to always use lowercase as a key for the +// PDBInputFile::Instances map, at least on Windows. +static std::string normalizePdbPath(StringRef path) { +#if defined(_WIN32) + return path.lower(); +#else // LINUX + return path; +#endif +} + +// If existing, return the actual PDB path on disk. +static Optional findPdbPath(StringRef pdbPath, + const ObjFile *dependentFile) { + // Ensure the file exists before anything else. In some cases, if the path + // points to a removable device, Driver::enqueuePath() would fail with an + // error (EAGAIN, "resource unavailable try again") which we want to skip + // silently. + if (llvm::sys::fs::exists(pdbPath)) + return normalizePdbPath(pdbPath); + std::string ret = getPdbBaseName(dependentFile, pdbPath); + if (llvm::sys::fs::exists(ret)) + return normalizePdbPath(ret); + return None; +} + +// Fetch the PDB instance that was already loaded by the COFF Driver. +Expected +TypeServerSource::findFromFile(const ObjFile *dependentFile) { + const TypeServer2Record &ts = + retrieveDependencyInfo(dependentFile->debugTypesObj); + + Optional p = findPdbPath(ts.Name, dependentFile); + if (!p) + return createFileError(ts.Name, errorCodeToError(std::error_code( + ENOENT, std::generic_category()))); + + auto it = TypeServerSource::instances.find(*p); + // The PDB file exists on disk, at this point we expect it to have been + // inserted in the map by TypeServerSource::loadPDB() + assert(it != TypeServerSource::instances.end()); + + std::pair &pdb = it->second; + + if (!pdb.second) + return createFileError( + *p, createStringError(inconvertibleErrorCode(), pdb.first.c_str())); + + pdb::PDBFile &pdbFile = (pdb.second)->session->getPDBFile(); + pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream()); + + // Just because a file with a matching name was found doesn't mean it can be + // used. The GUID must match between the PDB header and the OBJ + // TypeServer2 record. The 'Age' is used by MSVC incremental compilation. + if (info.getGuid() != ts.getGuid()) + return createFileError( + ts.Name, + make_error(pdb::pdb_error_code::signature_out_of_date)); + + return pdb.second; +} + +// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is +// moved here. +Expected +lld::coff::findTypeServerSource(const ObjFile *f) { + Expected ts = TypeServerSource::findFromFile(f); + if (!ts) + return ts.takeError(); + return ts.get()->session.get(); +} + +// Queue a PDB type server for loading in the COFF Driver +void TypeServerSource::enqueue(const ObjFile *dependentFile, + const TypeServer2Record &ts) { + // Start by finding where the PDB is located (either the record path or next + // to the OBJ file) + Optional p = findPdbPath(ts.Name, dependentFile); + if (!p) + return; + auto it = TypeServerSource::instances.emplace( + *p, std::pair{}); + if (!it.second) + return; // another OBJ already scheduled this PDB for load + + driver->enqueuePath(*p, false); +} + +// Create an instance of TypeServerSource or an error string if the PDB couldn't +// be loaded. The error message will be displayed later, when the referring OBJ +// will be merged in. NOTE - a PDB load failure is not a link error: some +// debug info will simply be missing from the final PDB - that is the default +// accepted behavior. +void lld::coff::loadTypeServerSource(llvm::MemoryBufferRef m) { + std::string path = normalizePdbPath(m.getBufferIdentifier()); + + Expected ts = TypeServerSource::getInstance(m); + if (!ts) + TypeServerSource::instances[path] = {toString(ts.takeError()), nullptr}; + else + TypeServerSource::instances[path] = {{}, *ts}; +} + +Expected TypeServerSource::getInstance(MemoryBufferRef m) { + std::unique_ptr iSession; + Error err = pdb::NativeSession::createFromPdb( + MemoryBuffer::getMemBuffer(m, false), iSession); + if (err) + return std::move(err); + + std::unique_ptr session( + static_cast(iSession.release())); + + pdb::PDBFile &pdbFile = session->getPDBFile(); + Expected info = pdbFile.getPDBInfoStream(); + // All PDB Files should have an Info stream. + if (!info) + return info.takeError(); + return new TypeServerSource(m, session.release()); +} diff --git a/COFF/DebugTypes.h b/COFF/DebugTypes.h new file mode 100644 index 0000000..e37c727 --- /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 const T &retrieveDependencyInfo(const TpiSource *source); + +// Temporary interface until we move PDBLinker::maybeMergeTypeServerPDB here +llvm::Expected +findTypeServerSource(const ObjFile *f); + +} // namespace coff +} // namespace lld + +#endif \ No newline at end of file diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp index 2e4b1e6..d7af50b 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 Args, bool CanExitEarly, raw_ostream &Diag) { - errorHandler().LogName = args::getFilenameWithoutExe(Args[0]); - errorHandler().ErrorOS = &Diag; - errorHandler().ColorDiagnostics = Diag.has_colors(); - errorHandler().ErrorLimitExceededMsg = +bool link(ArrayRef args, bool canExitEarly, raw_ostream &diag) { + errorHandler().logName = args::getFilenameWithoutExe(args[0]); + errorHandler().errorOS = &diag; + errorHandler().colorDiagnostics = diag.has_colors(); + errorHandler().errorLimitExceededMsg = "too many errors emitted, stopping now" " (use /errorlimit:0 to see all errors)"; - errorHandler().ExitEarly = CanExitEarly; - Config = make(); + errorHandler().exitEarly = canExitEarly; + config = make(); - Symtab = make(); + symtab = make(); - Driver = make(); - Driver->link(Args); + driver = make(); + driver->link(args); // Call exit() if we can to avoid calling destructors. - if (CanExitEarly) + if (canExitEarly) exitLld(errorCount() ? 1 : 0); freeArena(); - ObjFile::Instances.clear(); - ImportFile::Instances.clear(); - BitcodeFile::Instances.clear(); + ObjFile::instances.clear(); + ImportFile::instances.clear(); + BitcodeFile::instances.clear(); + memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances)); return !errorCount(); } +// Parse options of the form "old;new". +static std::pair getOldNewOptions(opt::InputArgList &args, + unsigned id) { + auto *arg = args.getLastArg(id); + if (!arg) + return {"", ""}; + + StringRef s = arg->getValue(); + std::pair ret = s.split(';'); + if (ret.second.empty()) + error(arg->getSpelling() + " expects 'old;new' format, but got " + s); + return ret; +} + // Drop directory components and replace extension with ".exe" or ".dll". -static std::string getOutputPath(StringRef Path) { - auto P = Path.find_last_of("\\/"); - StringRef S = (P == StringRef::npos) ? Path : Path.substr(P + 1); - const char* E = Config->DLL ? ".dll" : ".exe"; - return (S.substr(0, S.rfind('.')) + E).str(); +static std::string getOutputPath(StringRef path) { + auto p = path.find_last_of("\\/"); + StringRef s = (p == StringRef::npos) ? path : path.substr(p + 1); + const char* e = config->dll ? ".dll" : ".exe"; + return (s.substr(0, s.rfind('.')) + e).str(); +} + +// Returns true if S matches /crtend.?\.o$/. +static bool isCrtend(StringRef s) { + if (!s.endswith(".o")) + return false; + s = s.drop_back(2); + if (s.endswith("crtend")) + return true; + return !s.empty() && s.drop_back().endswith("crtend"); } // ErrorOr is not default constructible, so it cannot be used as the type @@ -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::error_code> MBErrPair; +using MBErrPair = std::pair, std::error_code>; // Create a std::future that opens and maps a file using the best strategy for // the host platform. -static std::future createFutureForFile(std::string Path) { +static std::future createFutureForFile(std::string path) { #if _WIN32 // On Windows, file I/O is relatively slow so it is best to do this // asynchronously. - auto Strategy = std::launch::async; + auto strategy = std::launch::async; #else - auto Strategy = std::launch::deferred; + auto strategy = std::launch::deferred; #endif - return std::async(Strategy, [=]() { - auto MBOrErr = MemoryBuffer::getFile(Path, + return std::async(strategy, [=]() { + auto mbOrErr = MemoryBuffer::getFile(path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false); - if (!MBOrErr) - return MBErrPair{nullptr, MBOrErr.getError()}; - return MBErrPair{std::move(*MBOrErr), std::error_code()}; + if (!mbOrErr) + return MBErrPair{nullptr, mbOrErr.getError()}; + return MBErrPair{std::move(*mbOrErr), std::error_code()}; }); } // Symbol names are mangled by prepending "_" on x86. -static StringRef mangle(StringRef Sym) { - assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN); - if (Config->Machine == I386) - return Saver.save("_" + Sym); - return Sym; +static StringRef mangle(StringRef sym) { + assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN); + if (config->machine == I386) + return saver.save("_" + sym); + return sym; } -static bool findUnderscoreMangle(StringRef Sym) { - StringRef Entry = Symtab->findMangle(mangle(Sym)); - return !Entry.empty() && !isa(Symtab->find(Entry)); +static bool findUnderscoreMangle(StringRef sym) { + Symbol *s = symtab->findMangle(mangle(sym)); + return s && !isa(s); } -MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr MB) { - MemoryBufferRef MBRef = *MB; - make>(std::move(MB)); // take ownership +MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr mb) { + MemoryBufferRef mbref = *mb; + make>(std::move(mb)); // take ownership - if (Driver->Tar) - Driver->Tar->append(relativeToRoot(MBRef.getBufferIdentifier()), - MBRef.getBuffer()); - return MBRef; + if (driver->tar) + driver->tar->append(relativeToRoot(mbref.getBufferIdentifier()), + mbref.getBuffer()); + return mbref; } -void LinkerDriver::addBuffer(std::unique_ptr MB, - bool WholeArchive) { - StringRef Filename = MB->getBufferIdentifier(); +void LinkerDriver::addBuffer(std::unique_ptr mb, + bool wholeArchive) { + StringRef filename = mb->getBufferIdentifier(); - MemoryBufferRef MBRef = takeBuffer(std::move(MB)); - FilePaths.push_back(Filename); + MemoryBufferRef mbref = takeBuffer(std::move(mb)); + filePaths.push_back(filename); // File type is detected by contents, not by file extension. - switch (identify_magic(MBRef.getBuffer())) { + switch (identify_magic(mbref.getBuffer())) { case file_magic::windows_resource: - Resources.push_back(MBRef); + resources.push_back(mbref); break; case file_magic::archive: - if (WholeArchive) { - std::unique_ptr File = - CHECK(Archive::create(MBRef), Filename + ": failed to parse archive"); + if (wholeArchive) { + std::unique_ptr file = + CHECK(Archive::create(mbref), filename + ": failed to parse archive"); - for (MemoryBufferRef M : getArchiveMembers(File.get())) - addArchiveBuffer(M, "", Filename); + for (MemoryBufferRef m : getArchiveMembers(file.get())) + addArchiveBuffer(m, "", filename, 0); return; } - Symtab->addFile(make(MBRef)); + symtab->addFile(make(mbref)); break; case file_magic::bitcode: - Symtab->addFile(make(MBRef)); + symtab->addFile(make(mbref, "", 0)); break; case file_magic::coff_object: case file_magic::coff_import_library: - Symtab->addFile(make(MBRef)); + symtab->addFile(make(mbref)); + break; + case file_magic::pdb: + loadTypeServerSource(mbref); break; case file_magic::coff_cl_gl_object: - error(Filename + ": is not a native COFF file. Recompile without /GL"); + error(filename + ": is not a native COFF file. Recompile without /GL"); break; case file_magic::pecoff_executable: - if (Filename.endswith_lower(".dll")) { - error(Filename + ": bad file type. Did you specify a DLL instead of an " + if (filename.endswith_lower(".dll")) { + error(filename + ": bad file type. Did you specify a DLL instead of an " "import library?"); break; } LLVM_FALLTHROUGH; default: - error(MBRef.getBufferIdentifier() + ": unknown file type"); + error(mbref.getBufferIdentifier() + ": unknown file type"); break; } } -void LinkerDriver::enqueuePath(StringRef Path, bool WholeArchive) { - auto Future = - std::make_shared>(createFutureForFile(Path)); - std::string PathStr = Path; +void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) { + auto future = + std::make_shared>(createFutureForFile(path)); + std::string pathStr = path; enqueueTask([=]() { - auto MBOrErr = Future->get(); - if (MBOrErr.second) - error("could not open " + PathStr + ": " + MBOrErr.second.message()); - else - Driver->addBuffer(std::move(MBOrErr.first), WholeArchive); + auto mbOrErr = future->get(); + if (mbOrErr.second) { + std::string msg = + "could not open '" + pathStr + "': " + mbOrErr.second.message(); + // Check if the filename is a typo for an option flag. OptTable thinks + // that all args that are not known options and that start with / are + // filenames, but e.g. `/nodefaultlibs` is more likely a typo for + // the option `/nodefaultlib` than a reference to a file in the root + // directory. + std::string nearest; + if (COFFOptTable().findNearest(pathStr, nearest) > 1) + error(msg); + else + error(msg + "; did you mean '" + nearest + "'"); + } else + driver->addBuffer(std::move(mbOrErr.first), wholeArchive); }); } -void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName, - StringRef ParentName) { - file_magic Magic = identify_magic(MB.getBuffer()); - if (Magic == file_magic::coff_import_library) { - Symtab->addFile(make(MB)); +void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName, + StringRef parentName, + uint64_t offsetInArchive) { + file_magic magic = identify_magic(mb.getBuffer()); + if (magic == file_magic::coff_import_library) { + InputFile *imp = make(mb); + imp->parentName = parentName; + symtab->addFile(imp); return; } - InputFile *Obj; - if (Magic == file_magic::coff_object) { - Obj = make(MB); - } else if (Magic == file_magic::bitcode) { - Obj = make(MB); + InputFile *obj; + if (magic == file_magic::coff_object) { + obj = make(mb); + } else if (magic == file_magic::bitcode) { + obj = make(mb, parentName, offsetInArchive); } else { - error("unknown file type: " + MB.getBufferIdentifier()); + error("unknown file type: " + mb.getBufferIdentifier()); return; } - Obj->ParentName = ParentName; - Symtab->addFile(Obj); - log("Loaded " + toString(Obj) + " for " + SymName); + obj->parentName = parentName; + symtab->addFile(obj); + log("Loaded " + toString(obj) + " for " + symName); } -void LinkerDriver::enqueueArchiveMember(const Archive::Child &C, - StringRef SymName, - StringRef ParentName) { - if (!C.getParent()->isThin()) { - MemoryBufferRef MB = CHECK( - C.getMemoryBufferRef(), - "could not get the buffer for the member defining symbol " + SymName); - enqueueTask([=]() { Driver->addArchiveBuffer(MB, SymName, ParentName); }); +void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, + StringRef symName, + StringRef parentName) { + + auto reportBufferError = [=](Error &&e, + StringRef childName) { + fatal("could not get the buffer for the member defining symbol " + + symName + ": " + parentName + "(" + childName + "): " + + toString(std::move(e))); + }; + + if (!c.getParent()->isThin()) { + uint64_t offsetInArchive = c.getChildOffset(); + Expected mbOrErr = c.getMemoryBufferRef(); + if (!mbOrErr) + reportBufferError(mbOrErr.takeError(), check(c.getFullName())); + MemoryBufferRef mb = mbOrErr.get(); + enqueueTask([=]() { + driver->addArchiveBuffer(mb, symName, parentName, offsetInArchive); + }); return; } - auto Future = std::make_shared>(createFutureForFile( - CHECK(C.getFullName(), - "could not get the filename for the member defining symbol " + - SymName))); + std::string childName = CHECK( + c.getFullName(), + "could not get the filename for the member defining symbol " + + symName); + auto future = std::make_shared>( + createFutureForFile(childName)); enqueueTask([=]() { - auto MBOrErr = Future->get(); - if (MBOrErr.second) - fatal("could not get the buffer for the member defining " + SymName + - ": " + MBOrErr.second.message()); - Driver->addArchiveBuffer(takeBuffer(std::move(MBOrErr.first)), SymName, - ParentName); + auto mbOrErr = future->get(); + if (mbOrErr.second) + reportBufferError(errorCodeToError(mbOrErr.second), childName); + driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), symName, + parentName, /* OffsetInArchive */ 0); }); } -static bool isDecorated(StringRef Sym) { - return Sym.startswith("@") || Sym.contains("@@") || Sym.startswith("?") || - (!Config->MinGW && Sym.contains('@')); +static bool isDecorated(StringRef sym) { + return sym.startswith("@") || sym.contains("@@") || sym.startswith("?") || + (!config->mingw && sym.contains('@')); } // Parses .drectve section contents and returns a list of files // specified by /defaultlib. -void LinkerDriver::parseDirectives(StringRef S) { - ArgParser Parser; +void LinkerDriver::parseDirectives(InputFile *file) { + StringRef s = file->getDirectives(); + if (s.empty()) + return; + + log("Directives: " + toString(file) + ": " + s); + + ArgParser parser; // .drectve is always tokenized using Windows shell rules. // /EXPORT: option can appear too many times, processing in fastpath. - opt::InputArgList Args; - std::vector Exports; - std::tie(Args, Exports) = Parser.parseDirectives(S); + opt::InputArgList args; + std::vector exports; + std::tie(args, exports) = parser.parseDirectives(s); - for (StringRef E : Exports) { + for (StringRef e : exports) { // If a common header file contains dllexported function // declarations, many object files may end up with having the // same /EXPORT options. In order to save cost of parsing them, // we dedup them first. - if (!DirectivesExports.insert(E).second) + if (!directivesExports.insert(e).second) continue; - Export Exp = parseExport(E); - if (Config->Machine == I386 && Config->MinGW) { - if (!isDecorated(Exp.Name)) - Exp.Name = Saver.save("_" + Exp.Name); - if (!Exp.ExtName.empty() && !isDecorated(Exp.ExtName)) - Exp.ExtName = Saver.save("_" + Exp.ExtName); + Export exp = parseExport(e); + if (config->machine == I386 && config->mingw) { + if (!isDecorated(exp.name)) + exp.name = saver.save("_" + exp.name); + if (!exp.extName.empty() && !isDecorated(exp.extName)) + exp.extName = saver.save("_" + exp.extName); } - Exp.Directives = true; - Config->Exports.push_back(Exp); + exp.directives = true; + config->exports.push_back(exp); } - for (auto *Arg : Args) { - switch (Arg->getOption().getUnaliasedOption().getID()) { + for (auto *arg : args) { + switch (arg->getOption().getID()) { case OPT_aligncomm: - parseAligncomm(Arg->getValue()); + parseAligncomm(arg->getValue()); break; case OPT_alternatename: - parseAlternateName(Arg->getValue()); + parseAlternateName(arg->getValue()); break; case OPT_defaultlib: - if (Optional Path = findLib(Arg->getValue())) - enqueuePath(*Path, false); + if (Optional path = findLib(arg->getValue())) + enqueuePath(*path, false); break; case OPT_entry: - Config->Entry = addUndefined(mangle(Arg->getValue())); + config->entry = addUndefined(mangle(arg->getValue())); break; case OPT_failifmismatch: - checkFailIfMismatch(Arg->getValue()); + checkFailIfMismatch(arg->getValue(), file); break; case OPT_incl: - addUndefined(Arg->getValue()); + addUndefined(arg->getValue()); break; case OPT_merge: - parseMerge(Arg->getValue()); + parseMerge(arg->getValue()); break; case OPT_nodefaultlib: - Config->NoDefaultLibs.insert(doFindLib(Arg->getValue())); + config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower()); break; case OPT_section: - parseSection(Arg->getValue()); + parseSection(arg->getValue()); break; case OPT_subsystem: - parseSubsystem(Arg->getValue(), &Config->Subsystem, - &Config->MajorOSVersion, &Config->MinorOSVersion); + parseSubsystem(arg->getValue(), &config->subsystem, + &config->majorOSVersion, &config->minorOSVersion); break; + // Only add flags here that link.exe accepts in + // `#pragma comment(linker, "/flag")`-generated sections. case OPT_editandcontinue: - case OPT_fastfail: case OPT_guardsym: - case OPT_natvis: case OPT_throwingnew: break; default: - error(Arg->getSpelling() + " is not allowed in .drectve"); + error(arg->getSpelling() + " is not allowed in .drectve"); } } } // Find file from search paths. You can omit ".obj", this function takes // care of that. Note that the returned path is not guaranteed to exist. -StringRef LinkerDriver::doFindFile(StringRef Filename) { - bool HasPathSep = (Filename.find_first_of("/\\") != StringRef::npos); - if (HasPathSep) - return Filename; - bool HasExt = Filename.contains('.'); - for (StringRef Dir : SearchPaths) { - SmallString<128> Path = Dir; - sys::path::append(Path, Filename); - if (sys::fs::exists(Path.str())) - return Saver.save(Path.str()); - if (!HasExt) { - Path.append(".obj"); - if (sys::fs::exists(Path.str())) - return Saver.save(Path.str()); +StringRef LinkerDriver::doFindFile(StringRef filename) { + bool hasPathSep = (filename.find_first_of("/\\") != StringRef::npos); + if (hasPathSep) + return filename; + bool hasExt = filename.contains('.'); + for (StringRef dir : searchPaths) { + SmallString<128> path = dir; + sys::path::append(path, filename); + if (sys::fs::exists(path.str())) + return saver.save(path.str()); + if (!hasExt) { + path.append(".obj"); + if (sys::fs::exists(path.str())) + return saver.save(path.str()); } } - return Filename; + return filename; } -static Optional getUniqueID(StringRef Path) { - sys::fs::UniqueID Ret; - if (sys::fs::getUniqueID(Path, Ret)) +static Optional getUniqueID(StringRef path) { + sys::fs::UniqueID ret; + if (sys::fs::getUniqueID(path, ret)) return None; - return Ret; + return ret; } // Resolves a file path. This never returns the same path // (in that case, it returns None). -Optional LinkerDriver::findFile(StringRef Filename) { - StringRef Path = doFindFile(Filename); +Optional LinkerDriver::findFile(StringRef filename) { + StringRef path = doFindFile(filename); - if (Optional ID = getUniqueID(Path)) { - bool Seen = !VisitedFiles.insert(*ID).second; - if (Seen) + if (Optional id = getUniqueID(path)) { + bool seen = !visitedFiles.insert(*id).second; + if (seen) return None; } - if (Path.endswith_lower(".lib")) - VisitedLibs.insert(sys::path::filename(Path)); - return Path; + if (path.endswith_lower(".lib")) + visitedLibs.insert(sys::path::filename(path)); + return path; } // MinGW specific. If an embedded directive specified to link to // foo.lib, but it isn't found, try libfoo.a instead. -StringRef LinkerDriver::doFindLibMinGW(StringRef Filename) { - if (Filename.contains('/') || Filename.contains('\\')) - return Filename; - - 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 LinkerDriver::findLib(StringRef Filename) { - if (Config->NoDefaultLibAll) +Optional LinkerDriver::findLib(StringRef filename) { + if (config->noDefaultLibAll) return None; - if (!VisitedLibs.insert(Filename.lower()).second) + if (!visitedLibs.insert(filename.lower()).second) return None; - StringRef Path = doFindLib(Filename); - if (Config->NoDefaultLibs.count(Path)) + StringRef path = doFindLib(filename); + if (config->noDefaultLibs.count(path.lower())) return None; - if (Optional ID = getUniqueID(Path)) - if (!VisitedFiles.insert(*ID).second) + if (Optional id = getUniqueID(path)) + if (!visitedFiles.insert(*id).second) return None; - return Path; + return path; } // Parses LIB environment which contains a list of search paths. void LinkerDriver::addLibSearchPaths() { - Optional EnvOpt = Process::GetEnv("LIB"); - if (!EnvOpt.hasValue()) + Optional envOpt = Process::GetEnv("LIB"); + if (!envOpt.hasValue()) return; - StringRef Env = Saver.save(*EnvOpt); - while (!Env.empty()) { - StringRef Path; - std::tie(Path, Env) = Env.split(';'); - SearchPaths.push_back(Path); + StringRef env = saver.save(*envOpt); + while (!env.empty()) { + StringRef path; + std::tie(path, env) = env.split(';'); + searchPaths.push_back(path); } } -Symbol *LinkerDriver::addUndefined(StringRef Name) { - Symbol *B = Symtab->addUndefined(Name); - if (!B->IsGCRoot) { - B->IsGCRoot = true; - Config->GCRoot.push_back(B); +Symbol *LinkerDriver::addUndefined(StringRef name) { + Symbol *b = symtab->addUndefined(name); + if (!b->isGCRoot) { + b->isGCRoot = true; + config->gcroot.push_back(b); } - return B; + return b; +} + +StringRef LinkerDriver::mangleMaybe(Symbol *s) { + // If the plain symbol name has already been resolved, do nothing. + Undefined *unmangled = dyn_cast(s); + if (!unmangled) + return ""; + + // Otherwise, see if a similar, mangled symbol exists in the symbol table. + Symbol *mangled = symtab->findMangle(unmangled->getName()); + if (!mangled) + return ""; + + // If we find a similar mangled symbol, make this an alias to it and return + // its name. + log(unmangled->getName() + " aliased to " + mangled->getName()); + unmangled->weakAlias = symtab->addUndefined(mangled->getName()); + return mangled->getName(); } // Windows specific -- find default entry point name. @@ -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 FilePaths, - ArrayRef SearchPaths) { - SmallString<0> Data; - raw_svector_ostream OS(Data); +static std::string createResponseFile(const opt::InputArgList &args, + ArrayRef filePaths, + ArrayRef searchPaths) { + SmallString<0> data; + raw_svector_ostream os(data); - for (auto *Arg : Args) { - switch (Arg->getOption().getID()) { + for (auto *arg : args) { + switch (arg->getOption().getID()) { case OPT_linkrepro: case OPT_INPUT: case OPT_defaultlib: @@ -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(A->getValue()) + DebugKind debug = StringSwitch(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(DebugType::None); +static unsigned parseDebugTypes(const opt::InputArgList &args) { + unsigned debugTypes = static_cast(DebugType::None); - if (auto *A = Args.getLastArg(OPT_debugtype)) { - SmallVector Types; - A->getSpelling().split(Types, ',', /*KeepEmpty=*/false); + if (auto *a = args.getLastArg(OPT_debugtype)) { + SmallVector types; + StringRef(a->getValue()) + .split(types, ',', /*MaxSplit=*/-1, /*KeepEmpty=*/false); - for (StringRef Type : Types) { - unsigned V = StringSwitch(Type.lower()) + for (StringRef type : types) { + unsigned v = StringSwitch(type.lower()) .Case("cv", static_cast(DebugType::CV)) .Case("pdata", static_cast(DebugType::PData)) .Case("fixup", static_cast(DebugType::Fixup)) .Default(0); - if (V == 0) { - warn("/debugtype: unknown option: " + Twine(A->getValue())); + if (v == 0) { + warn("/debugtype: unknown option '" + type + "'"); continue; } - DebugTypes |= V; + debugTypes |= v; } - return DebugTypes; + return debugTypes; } // Default debug types - DebugTypes = static_cast(DebugType::CV); - if (Args.hasArg(OPT_driver)) - DebugTypes |= static_cast(DebugType::PData); - if (Args.hasArg(OPT_profile)) - DebugTypes |= static_cast(DebugType::Fixup); + debugTypes = static_cast(DebugType::CV); + if (args.hasArg(OPT_driver)) + debugTypes |= static_cast(DebugType::PData); + if (args.hasArg(OPT_profile)) + debugTypes |= static_cast(DebugType::Fixup); - return DebugTypes; + return debugTypes; } -static std::string getMapFile(const opt::InputArgList &Args) { - auto *Arg = Args.getLastArg(OPT_lldmap, OPT_lldmap_file); - if (!Arg) +static std::string getMapFile(const opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_lldmap, OPT_lldmap_file); + if (!arg) return ""; - if (Arg->getOption().getID() == OPT_lldmap_file) - return Arg->getValue(); + if (arg->getOption().getID() == OPT_lldmap_file) + return arg->getValue(); - assert(Arg->getOption().getID() == OPT_lldmap); - StringRef OutFile = Config->OutputFile; - return (OutFile.substr(0, OutFile.rfind('.')) + ".map").str(); + assert(arg->getOption().getID() == OPT_lldmap); + StringRef outFile = config->outputFile; + return (outFile.substr(0, outFile.rfind('.')) + ".map").str(); } static std::string getImplibPath() { - if (!Config->Implib.empty()) - return Config->Implib; - SmallString<128> Out = StringRef(Config->OutputFile); - sys::path::replace_extension(Out, ".lib"); - return Out.str(); + if (!config->implib.empty()) + return config->implib; + SmallString<128> out = StringRef(config->outputFile); + sys::path::replace_extension(out, ".lib"); + return out.str(); } // @@ -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 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 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> OldBuf = MemoryBuffer::getFile( - Path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false); - if (!OldBuf) { - HandleError(writeImportLibrary(LibName, Path, Exports, Config->Machine, - Config->MinGW)); + ErrorOr> oldBuf = MemoryBuffer::getFile( + path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false); + if (!oldBuf) { + handleError(writeImportLibrary(libName, path, exports, config->machine, + config->mingw)); return; } - SmallString<128> TmpName; - if (std::error_code EC = - sys::fs::createUniqueFile(Path + ".tmp-%%%%%%%%.lib", TmpName)) - fatal("cannot create temporary file for import library " + Path + ": " + - EC.message()); + SmallString<128> tmpName; + if (std::error_code ec = + sys::fs::createUniqueFile(path + ".tmp-%%%%%%%%.lib", tmpName)) + fatal("cannot create temporary file for import library " + path + ": " + + ec.message()); - if (Error E = writeImportLibrary(LibName, TmpName, Exports, Config->Machine, - Config->MinGW)) { - HandleError(std::move(E)); + if (Error e = writeImportLibrary(libName, tmpName, exports, config->machine, + config->mingw)) { + handleError(std::move(e)); return; } - std::unique_ptr NewBuf = check(MemoryBuffer::getFile( - TmpName, /*FileSize*/ -1, /*RequiresNullTerminator*/ false)); - if ((*OldBuf)->getBuffer() != NewBuf->getBuffer()) { - OldBuf->reset(); - HandleError(errorCodeToError(sys::fs::rename(TmpName, Path))); + std::unique_ptr newBuf = check(MemoryBuffer::getFile( + tmpName, /*FileSize*/ -1, /*RequiresNullTerminator*/ false)); + if ((*oldBuf)->getBuffer() != newBuf->getBuffer()) { + oldBuf->reset(); + handleError(errorCodeToError(sys::fs::rename(tmpName, path))); } else { - sys::fs::remove(TmpName); + sys::fs::remove(tmpName); } } -static void parseModuleDefs(StringRef Path) { - std::unique_ptr MB = CHECK( - MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); - COFFModuleDefinition M = check(parseCOFFModuleDefinition( - MB->getMemBufferRef(), Config->Machine, Config->MinGW)); - - 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 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 Task) { - TaskQueue.push_back(std::move(Task)); +void LinkerDriver::enqueueTask(std::function task) { + taskQueue.push_back(std::move(task)); } bool LinkerDriver::run() { - ScopedTimer T(InputFileTimer); + ScopedTimer t(inputFileTimer); - bool DidWork = !TaskQueue.empty(); - while (!TaskQueue.empty()) { - TaskQueue.front()(); - TaskQueue.pop_front(); + bool didWork = !taskQueue.empty(); + while (!taskQueue.empty()) { + taskQueue.front()(); + taskQueue.pop_front(); } - return DidWork; + return didWork; } // Parse an /order file. If an option is given, the linker places // COMDAT sections in the same order as their names appear in the // given file. -static void parseOrderFile(StringRef Arg) { +static void parseOrderFile(StringRef arg) { // For some reason, the MSVC linker requires a filename to be // preceded by "@". - if (!Arg.startswith("@")) { + if (!arg.startswith("@")) { error("malformed /order option: '@' missing"); return; } // Get a list of all comdat sections for error checking. - DenseSet Set; - for (Chunk *C : Symtab->getChunks()) - if (auto *Sec = dyn_cast(C)) - if (Sec->Sym) - Set.insert(Sec->Sym->getName()); + DenseSet set; + for (Chunk *c : symtab->getChunks()) + if (auto *sec = dyn_cast(c)) + if (sec->sym) + set.insert(sec->sym->getName()); // Open a file. - StringRef Path = Arg.substr(1); - std::unique_ptr MB = CHECK( - MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); + StringRef path = arg.substr(1); + std::unique_ptr mb = CHECK( + MemoryBuffer::getFile(path, -1, false, true), "could not open " + path); // Parse a file. An order file contains one symbol per line. // All symbols that were not present in a given order file are // considered to have the lowest priority 0 and are placed at // end of an output section. - for (std::string S : args::getLines(MB->getMemBufferRef())) { - if (Config->Machine == I386 && !isDecorated(S)) - S = "_" + S; + for (std::string s : args::getLines(mb->getMemBufferRef())) { + if (config->machine == I386 && !isDecorated(s)) + s = "_" + s; - if (Set.count(S) == 0) { - if (Config->WarnMissingOrderSymbol) - warn("/order:" + Arg + ": missing symbol: " + S + " [LNK4037]"); + if (set.count(s) == 0) { + if (config->warnMissingOrderSymbol) + warn("/order:" + arg + ": missing symbol: " + s + " [LNK4037]"); } else - Config->Order[S] = INT_MIN + Config->Order.size(); + config->order[s] = INT_MIN + config->order.size(); } } -static void markAddrsig(Symbol *S) { - if (auto *D = dyn_cast_or_null(S)) - if (Chunk *C = D->getChunk()) - C->KeepUnique = true; +static void markAddrsig(Symbol *s) { + if (auto *d = dyn_cast_or_null(s)) + if (SectionChunk *c = dyn_cast_or_null(d->getChunk())) + c->keepUnique = true; } static void findKeepUniqueSections() { // Exported symbols could be address-significant in other executables or DSOs, // so we conservatively mark them as address-significant. - for (Export &R : Config->Exports) - markAddrsig(R.Sym); + for (Export &r : config->exports) + markAddrsig(r.sym); // Visit the address-significance table in each object file and mark each // referenced symbol as address-significant. - for (ObjFile *Obj : ObjFile::Instances) { - ArrayRef Syms = Obj->getSymbols(); - if (Obj->AddrsigSec) { - ArrayRef Contents; - Obj->getCOFFObj()->getSectionContents(Obj->AddrsigSec, Contents); - const uint8_t *Cur = Contents.begin(); - while (Cur != Contents.end()) { - unsigned Size; - const char *Err; - uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err); - if (Err) - fatal(toString(Obj) + ": could not decode addrsig section: " + Err); - if (SymIndex >= Syms.size()) - fatal(toString(Obj) + ": invalid symbol index in addrsig section"); - markAddrsig(Syms[SymIndex]); - Cur += Size; + for (ObjFile *obj : ObjFile::instances) { + ArrayRef syms = obj->getSymbols(); + if (obj->addrsigSec) { + ArrayRef contents; + cantFail( + obj->getCOFFObj()->getSectionContents(obj->addrsigSec, contents)); + const uint8_t *cur = contents.begin(); + while (cur != contents.end()) { + unsigned size; + const char *err; + uint64_t symIndex = decodeULEB128(cur, &size, contents.end(), &err); + if (err) + fatal(toString(obj) + ": could not decode addrsig section: " + err); + if (symIndex >= syms.size()) + fatal(toString(obj) + ": invalid symbol index in addrsig section"); + markAddrsig(syms[symIndex]); + cur += size; } } else { // If an object file does not have an address-significance table, // conservatively mark all of its symbols as address-significant. - for (Symbol *S : Syms) - markAddrsig(S); + for (Symbol *s : syms) + markAddrsig(s); } } } -// link.exe replaces each %foo% in AltPath with the contents of environment +// link.exe replaces each %foo% in altPath with the contents of environment // variable foo, and adds the two magic env vars _PDB (expands to the basename // of pdb's output path) and _EXT (expands to the extension of the output // binary). // lld only supports %_PDB% and %_EXT% and warns on references to all other env // vars. -static void parsePDBAltPath(StringRef AltPath) { - SmallString<128> Buf; - StringRef PDBBasename = - sys::path::filename(Config->PDBPath, sys::path::Style::windows); - StringRef BinaryExtension = - sys::path::extension(Config->OutputFile, sys::path::Style::windows); - if (!BinaryExtension.empty()) - BinaryExtension = BinaryExtension.substr(1); // %_EXT% does not include '.'. +static void parsePDBAltPath(StringRef altPath) { + SmallString<128> buf; + StringRef pdbBasename = + sys::path::filename(config->pdbPath, sys::path::Style::windows); + StringRef binaryExtension = + sys::path::extension(config->outputFile, sys::path::Style::windows); + if (!binaryExtension.empty()) + binaryExtension = binaryExtension.substr(1); // %_EXT% does not include '.'. // Invariant: - // +--------- Cursor ('a...' might be the empty string). - // | +----- FirstMark - // | | +- SecondMark + // +--------- cursor ('a...' might be the empty string). + // | +----- firstMark + // | | +- secondMark // v v v // a...%...%... - size_t Cursor = 0; - while (Cursor < AltPath.size()) { - size_t FirstMark, SecondMark; - if ((FirstMark = AltPath.find('%', Cursor)) == StringRef::npos || - (SecondMark = AltPath.find('%', FirstMark + 1)) == StringRef::npos) { + size_t cursor = 0; + while (cursor < altPath.size()) { + size_t firstMark, secondMark; + if ((firstMark = altPath.find('%', cursor)) == StringRef::npos || + (secondMark = altPath.find('%', firstMark + 1)) == StringRef::npos) { // Didn't find another full fragment, treat rest of string as literal. - Buf.append(AltPath.substr(Cursor)); + buf.append(altPath.substr(cursor)); break; } // Found a full fragment. Append text in front of first %, and interpret // text between first and second % as variable name. - Buf.append(AltPath.substr(Cursor, FirstMark - Cursor)); - StringRef Var = AltPath.substr(FirstMark, SecondMark - FirstMark + 1); - if (Var.equals_lower("%_pdb%")) - Buf.append(PDBBasename); - else if (Var.equals_lower("%_ext%")) - Buf.append(BinaryExtension); + buf.append(altPath.substr(cursor, firstMark - cursor)); + StringRef var = altPath.substr(firstMark, secondMark - firstMark + 1); + if (var.equals_lower("%_pdb%")) + buf.append(pdbBasename); + else if (var.equals_lower("%_ext%")) + buf.append(binaryExtension); else { warn("only %_PDB% and %_EXT% supported in /pdbaltpath:, keeping " + - Var + " as literal"); - Buf.append(Var); + var + " as literal"); + buf.append(var); } - Cursor = SecondMark + 1; + cursor = secondMark + 1; } - Config->PDBAltPath = Buf; + config->pdbAltPath = buf; } -void LinkerDriver::link(ArrayRef ArgsArr) { - // If the first command line argument is "/lib", link.exe acts like lib.exe. - // We call our own implementation of lib.exe that understands bitcode files. - if (ArgsArr.size() > 1 && StringRef(ArgsArr[1]).equals_lower("/lib")) { - if (llvm::libDriverMain(ArgsArr.slice(1)) != 0) - fatal("lib failed"); +/// Check that at most one resource obj file was used. +/// Call after ObjFile::Instances is complete. +static void diagnoseMultipleResourceObjFiles() { + // The .rsrc$01 section in a resource obj file contains a tree description + // of resources. Merging multiple resource obj files would require merging + // the trees instead of using usual linker section merging semantics. + // Since link.exe disallows linking more than one resource obj file with + // LNK4078, mirror that. The normal use of resource files is to give the + // linker many .res files, which are then converted to a single resource obj + // file internally, so this is not a big restriction in practice. + ObjFile *resourceObjFile = nullptr; + for (ObjFile *f : ObjFile::instances) { + if (!f->isResourceObjFile) + continue; + + if (!resourceObjFile) { + resourceObjFile = f; + continue; + } + + error(toString(f) + + ": more than one resource obj file not allowed, already got " + + toString(resourceObjFile)); + } +} + +// In MinGW, if no symbols are chosen to be exported, then all symbols are +// automatically exported by default. This behavior can be forced by the +// -export-all-symbols option, so that it happens even when exports are +// explicitly specified. The automatic behavior can be disabled using the +// -exclude-all-symbols option, so that lld-link behaves like link.exe rather +// than MinGW in the case that nothing is explicitly exported. +void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) { + if (!config->dll) return; + + if (!args.hasArg(OPT_export_all_symbols)) { + if (!config->exports.empty()) + return; + if (args.hasArg(OPT_exclude_all_symbols)) + return; } + AutoExporter exporter; + + for (auto *arg : args.filtered(OPT_wholearchive_file)) + if (Optional path = doFindFile(arg->getValue())) + exporter.addWholeArchive(*path); + + symtab->forEachSymbol([&](Symbol *s) { + auto *def = dyn_cast(s); + if (!exporter.shouldExport(def)) + return; + + Export e; + e.name = def->getName(); + e.sym = def; + if (Chunk *c = def->getChunk()) + if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) + e.data = true; + config->exports.push_back(e); + }); +} + +void LinkerDriver::link(ArrayRef argsArr) { // Needed for LTO. InitializeAllTargetInfos(); InitializeAllTargets(); @@ -914,279 +1059,308 @@ void LinkerDriver::link(ArrayRef 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 V; - V.push_back("lld-link (LLVM option parsing)"); - for (auto *Arg : Args.filtered(OPT_mllvm)) - V.push_back(Arg->getValue()); - cl::ParseCommandLineOptions(V.size(), V.data()); + std::vector v; + v.push_back("lld-link (LLVM option parsing)"); + for (auto *arg : args.filtered(OPT_mllvm)) + v.push_back(arg->getValue()); + cl::ParseCommandLineOptions(v.size(), v.data()); // Handle /errorlimit early, because error() depends on it. - if (auto *Arg = Args.getLastArg(OPT_errorlimit)) { - int N = 20; - StringRef S = Arg->getValue(); - if (S.getAsInteger(10, N)) - error(Arg->getSpelling() + " number expected, but got " + S); - errorHandler().ErrorLimit = N; + if (auto *arg = args.getLastArg(OPT_errorlimit)) { + int n = 20; + StringRef s = arg->getValue(); + if (s.getAsInteger(10, n)) + error(arg->getSpelling() + " number expected, but got " + s); + errorHandler().errorLimit = n; } // Handle /help - if (Args.hasArg(OPT_help)) { - printHelp(ArgsArr[0]); + if (args.hasArg(OPT_help)) { + printHelp(argsArr[0]); return; } - if (Args.hasArg(OPT_show_timing)) - Config->ShowTiming = true; + lld::threadsEnabled = args.hasFlag(OPT_threads, OPT_threads_no, true); + + 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> ErrOrWriter = - TarWriter::create(Path, "repro"); + Expected> errOrWriter = + TarWriter::create(path, "repro"); - if (ErrOrWriter) { - Tar = std::move(*ErrOrWriter); + if (errOrWriter) { + tar = std::move(*errOrWriter); } else { - error("/linkrepro: failed to open " + Path + ": " + - toString(ErrOrWriter.takeError())); + error("/linkrepro: failed to open " + path + ": " + + toString(errOrWriter.takeError())); } } - if (!Args.hasArg(OPT_INPUT)) { - if (Args.hasArg(OPT_deffile)) - Config->NoEntry = true; + if (!args.hasArg(OPT_INPUT)) { + if (args.hasArg(OPT_deffile)) + config->noEntry = true; else fatal("no input files"); } // Construct search path list. - SearchPaths.push_back(""); - for (auto *Arg : Args.filtered(OPT_libpath)) - SearchPaths.push_back(Arg->getValue()); + searchPaths.push_back(""); + for (auto *arg : args.filtered(OPT_libpath)) + searchPaths.push_back(arg->getValue()); addLibSearchPaths(); // Handle /ignore - for (auto *Arg : Args.filtered(OPT_ignore)) { - SmallVector Vec; - StringRef(Arg->getValue()).split(Vec, ','); - for (StringRef S : Vec) { - if (S == "4037") - Config->WarnMissingOrderSymbol = false; - else if (S == "4099") - Config->WarnDebugInfoUnusable = false; - else if (S == "4217") - Config->WarnLocallyDefinedImported = false; + for (auto *arg : args.filtered(OPT_ignore)) { + SmallVector vec; + StringRef(arg->getValue()).split(vec, ','); + for (StringRef s : vec) { + if (s == "4037") + config->warnMissingOrderSymbol = false; + else if (s == "4099") + config->warnDebugInfoUnusable = false; + else if (s == "4217") + config->warnLocallyDefinedImported = false; // Other warning numbers are ignored. } } // Handle /out - if (auto *Arg = Args.getLastArg(OPT_out)) - Config->OutputFile = Arg->getValue(); + if (auto *arg = args.getLastArg(OPT_out)) + config->outputFile = arg->getValue(); // Handle /verbose - if (Args.hasArg(OPT_verbose)) - Config->Verbose = true; - errorHandler().Verbose = Config->Verbose; + if (args.hasArg(OPT_verbose)) + config->verbose = true; + errorHandler().verbose = config->verbose; // Handle /force or /force:unresolved - if (Args.hasArg(OPT_force, OPT_force_unresolved)) - Config->ForceUnresolved = true; + if (args.hasArg(OPT_force, OPT_force_unresolved)) + config->forceUnresolved = true; // Handle /force or /force:multiple - if (Args.hasArg(OPT_force, OPT_force_multiple)) - Config->ForceMultiple = true; + if (args.hasArg(OPT_force, OPT_force_multiple)) + config->forceMultiple = true; + + // Handle /force or /force:multipleres + if (args.hasArg(OPT_force, OPT_force_multipleres)) + config->forceMultipleRes = true; // Handle /debug - DebugKind Debug = parseDebugKind(Args); - if (Debug == DebugKind::Full || Debug == DebugKind::Dwarf || - Debug == DebugKind::GHash) { - Config->Debug = true; - Config->Incremental = true; + DebugKind debug = parseDebugKind(args); + if (debug == DebugKind::Full || debug == DebugKind::Dwarf || + debug == DebugKind::GHash) { + config->debug = true; + config->incremental = true; } + // Handle /demangle + config->demangle = args.hasFlag(OPT_demangle, OPT_demangle_no); + // Handle /debugtype - Config->DebugTypes = parseDebugTypes(Args); + config->debugTypes = parseDebugTypes(args); // Handle /pdb - bool ShouldCreatePDB = - (Debug == DebugKind::Full || Debug == DebugKind::GHash); - if (ShouldCreatePDB) { - if (auto *Arg = Args.getLastArg(OPT_pdb)) - Config->PDBPath = Arg->getValue(); - if (auto *Arg = Args.getLastArg(OPT_pdbaltpath)) - Config->PDBAltPath = Arg->getValue(); - if (Args.hasArg(OPT_natvis)) - Config->NatvisFiles = Args.getAllArgValues(OPT_natvis); + bool shouldCreatePDB = + (debug == DebugKind::Full || debug == DebugKind::GHash); + if (shouldCreatePDB) { + if (auto *arg = args.getLastArg(OPT_pdb)) + config->pdbPath = arg->getValue(); + if (auto *arg = args.getLastArg(OPT_pdbaltpath)) + config->pdbAltPath = arg->getValue(); + if (args.hasArg(OPT_natvis)) + config->natvisFiles = args.getAllArgValues(OPT_natvis); - if (auto *Arg = Args.getLastArg(OPT_pdb_source_path)) - Config->PDBSourcePath = Arg->getValue(); + if (auto *arg = args.getLastArg(OPT_pdb_source_path)) + config->pdbSourcePath = arg->getValue(); } // Handle /noentry - if (Args.hasArg(OPT_noentry)) { - if (Args.hasArg(OPT_dll)) - Config->NoEntry = true; + if (args.hasArg(OPT_noentry)) { + if (args.hasArg(OPT_dll)) + config->noEntry = true; else error("/noentry must be specified with /dll"); } // Handle /dll - if (Args.hasArg(OPT_dll)) { - Config->DLL = true; - Config->ManifestID = 2; + if (args.hasArg(OPT_dll)) { + config->dll = true; + config->manifestID = 2; } // Handle /dynamicbase and /fixed. We can't use hasFlag for /dynamicbase // because we need to explicitly check whether that option or its inverse was // present in the argument list in order to handle /fixed. - auto *DynamicBaseArg = Args.getLastArg(OPT_dynamicbase, OPT_dynamicbase_no); - if (DynamicBaseArg && - DynamicBaseArg->getOption().getID() == OPT_dynamicbase_no) - Config->DynamicBase = false; + auto *dynamicBaseArg = args.getLastArg(OPT_dynamicbase, OPT_dynamicbase_no); + if (dynamicBaseArg && + dynamicBaseArg->getOption().getID() == OPT_dynamicbase_no) + config->dynamicBase = false; // MSDN claims "/FIXED:NO is the default setting for a DLL, and /FIXED is the // default setting for any other project type.", but link.exe defaults to // /FIXED:NO for exe outputs as well. Match behavior, not docs. - bool Fixed = Args.hasFlag(OPT_fixed, OPT_fixed_no, false); - if (Fixed) { - if (DynamicBaseArg && - DynamicBaseArg->getOption().getID() == OPT_dynamicbase) { + bool fixed = args.hasFlag(OPT_fixed, OPT_fixed_no, false); + if (fixed) { + if (dynamicBaseArg && + dynamicBaseArg->getOption().getID() == OPT_dynamicbase) { error("/fixed must not be specified with /dynamicbase"); } else { - Config->Relocatable = false; - Config->DynamicBase = false; + config->relocatable = false; + config->dynamicBase = false; } } // Handle /appcontainer - Config->AppContainer = - Args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false); + config->appContainer = + args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false); // Handle /machine - if (auto *Arg = Args.getLastArg(OPT_machine)) - Config->Machine = getMachineType(Arg->getValue()); + if (auto *arg = args.getLastArg(OPT_machine)) { + config->machine = getMachineType(arg->getValue()); + if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) + fatal(Twine("unknown /machine argument: ") + arg->getValue()); + } // Handle /nodefaultlib: - for (auto *Arg : Args.filtered(OPT_nodefaultlib)) - Config->NoDefaultLibs.insert(doFindLib(Arg->getValue())); + for (auto *arg : args.filtered(OPT_nodefaultlib)) + config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower()); // Handle /nodefaultlib - if (Args.hasArg(OPT_nodefaultlib_all)) - Config->NoDefaultLibAll = true; + if (args.hasArg(OPT_nodefaultlib_all)) + config->noDefaultLibAll = true; // Handle /base - if (auto *Arg = Args.getLastArg(OPT_base)) - parseNumbers(Arg->getValue(), &Config->ImageBase); + if (auto *arg = args.getLastArg(OPT_base)) + parseNumbers(arg->getValue(), &config->imageBase); + + // Handle /filealign + if (auto *arg = args.getLastArg(OPT_filealign)) { + parseNumbers(arg->getValue(), &config->fileAlign); + if (!isPowerOf2_64(config->fileAlign)) + error("/filealign: not a power of two: " + Twine(config->fileAlign)); + } // Handle /stack - if (auto *Arg = Args.getLastArg(OPT_stack)) - parseNumbers(Arg->getValue(), &Config->StackReserve, &Config->StackCommit); + if (auto *arg = args.getLastArg(OPT_stack)) + parseNumbers(arg->getValue(), &config->stackReserve, &config->stackCommit); // Handle /guard:cf - if (auto *Arg = Args.getLastArg(OPT_guard)) - parseGuard(Arg->getValue()); + if (auto *arg = args.getLastArg(OPT_guard)) + parseGuard(arg->getValue()); // Handle /heap - if (auto *Arg = Args.getLastArg(OPT_heap)) - parseNumbers(Arg->getValue(), &Config->HeapReserve, &Config->HeapCommit); + if (auto *arg = args.getLastArg(OPT_heap)) + parseNumbers(arg->getValue(), &config->heapReserve, &config->heapCommit); // Handle /version - if (auto *Arg = Args.getLastArg(OPT_version)) - parseVersion(Arg->getValue(), &Config->MajorImageVersion, - &Config->MinorImageVersion); + if (auto *arg = args.getLastArg(OPT_version)) + parseVersion(arg->getValue(), &config->majorImageVersion, + &config->minorImageVersion); // Handle /subsystem - if (auto *Arg = Args.getLastArg(OPT_subsystem)) - parseSubsystem(Arg->getValue(), &Config->Subsystem, &Config->MajorOSVersion, - &Config->MinorOSVersion); + if (auto *arg = args.getLastArg(OPT_subsystem)) + parseSubsystem(arg->getValue(), &config->subsystem, &config->majorOSVersion, + &config->minorOSVersion); // Handle /timestamp - if (llvm::opt::Arg *Arg = Args.getLastArg(OPT_timestamp, OPT_repro)) { - if (Arg->getOption().getID() == OPT_repro) { - Config->Timestamp = 0; - Config->Repro = true; + if (llvm::opt::Arg *arg = args.getLastArg(OPT_timestamp, OPT_repro)) { + if (arg->getOption().getID() == OPT_repro) { + config->timestamp = 0; + config->repro = true; } else { - Config->Repro = false; - StringRef Value(Arg->getValue()); - if (Value.getAsInteger(0, Config->Timestamp)) - fatal(Twine("invalid timestamp: ") + Value + + config->repro = false; + StringRef value(arg->getValue()); + if (value.getAsInteger(0, config->timestamp)) + fatal(Twine("invalid timestamp: ") + value + ". Expected 32-bit integer"); } } else { - Config->Repro = false; - Config->Timestamp = time(nullptr); + config->repro = false; + config->timestamp = time(nullptr); } // Handle /alternatename - for (auto *Arg : Args.filtered(OPT_alternatename)) - parseAlternateName(Arg->getValue()); + for (auto *arg : args.filtered(OPT_alternatename)) + parseAlternateName(arg->getValue()); // Handle /include - for (auto *Arg : Args.filtered(OPT_incl)) - addUndefined(Arg->getValue()); + for (auto *arg : args.filtered(OPT_incl)) + addUndefined(arg->getValue()); // Handle /implib - if (auto *Arg = Args.getLastArg(OPT_implib)) - Config->Implib = Arg->getValue(); + if (auto *arg = args.getLastArg(OPT_implib)) + config->implib = arg->getValue(); // Handle /opt. - bool DoGC = Debug == DebugKind::None || Args.hasArg(OPT_profile); - unsigned ICFLevel = - Args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on - unsigned TailMerge = 1; - for (auto *Arg : Args.filtered(OPT_opt)) { - std::string Str = StringRef(Arg->getValue()).lower(); - SmallVector Vec; - StringRef(Str).split(Vec, ','); - for (StringRef S : Vec) { - if (S == "ref") { - DoGC = true; - } else if (S == "noref") { - DoGC = false; - } else if (S == "icf" || S.startswith("icf=")) { - ICFLevel = 2; - } else if (S == "noicf") { - ICFLevel = 0; - } else if (S == "lldtailmerge") { - TailMerge = 2; - } else if (S == "nolldtailmerge") { - TailMerge = 0; - } else if (S.startswith("lldlto=")) { - StringRef OptLevel = S.substr(7); - if (OptLevel.getAsInteger(10, Config->LTOO) || Config->LTOO > 3) - error("/opt:lldlto: invalid optimization level: " + OptLevel); - } else if (S.startswith("lldltojobs=")) { - StringRef Jobs = S.substr(11); - if (Jobs.getAsInteger(10, Config->ThinLTOJobs) || - Config->ThinLTOJobs == 0) - error("/opt:lldltojobs: invalid job count: " + Jobs); - } else if (S.startswith("lldltopartitions=")) { - StringRef N = S.substr(17); - if (N.getAsInteger(10, Config->LTOPartitions) || - Config->LTOPartitions == 0) - error("/opt:lldltopartitions: invalid partition count: " + N); - } else if (S != "lbr" && S != "nolbr") - error("/opt: unknown option: " + S); + bool doGC = debug == DebugKind::None || args.hasArg(OPT_profile); + unsigned icfLevel = + args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on + unsigned tailMerge = 1; + for (auto *arg : args.filtered(OPT_opt)) { + std::string str = StringRef(arg->getValue()).lower(); + SmallVector vec; + StringRef(str).split(vec, ','); + for (StringRef s : vec) { + if (s == "ref") { + doGC = true; + } else if (s == "noref") { + doGC = false; + } else if (s == "icf" || s.startswith("icf=")) { + icfLevel = 2; + } else if (s == "noicf") { + icfLevel = 0; + } else if (s == "lldtailmerge") { + tailMerge = 2; + } else if (s == "nolldtailmerge") { + tailMerge = 0; + } else if (s.startswith("lldlto=")) { + StringRef optLevel = s.substr(7); + if (optLevel.getAsInteger(10, config->ltoo) || config->ltoo > 3) + error("/opt:lldlto: invalid optimization level: " + optLevel); + } else if (s.startswith("lldltojobs=")) { + StringRef jobs = s.substr(11); + if (jobs.getAsInteger(10, config->thinLTOJobs) || + config->thinLTOJobs == 0) + error("/opt:lldltojobs: invalid job count: " + jobs); + } else if (s.startswith("lldltopartitions=")) { + StringRef n = s.substr(17); + if (n.getAsInteger(10, config->ltoPartitions) || + config->ltoPartitions == 0) + error("/opt:lldltopartitions: invalid partition count: " + n); + } else if (s != "lbr" && s != "nolbr") + error("/opt: unknown option: " + s); } } @@ -1195,37 +1369,37 @@ void LinkerDriver::link(ArrayRef 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 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 WholeArchives; - AutoExporter Exporter; - for (auto *Arg : Args.filtered(OPT_wholearchive_file)) { - if (Optional Path = doFindFile(Arg->getValue())) { - if (Optional ID = getUniqueID(*Path)) - WholeArchives.insert(*ID); - Exporter.addWholeArchive(*Path); - } - } + std::set wholeArchives; + for (auto *arg : args.filtered(OPT_wholearchive_file)) + if (Optional path = doFindFile(arg->getValue())) + if (Optional id = getUniqueID(*path)) + wholeArchives.insert(*id); // A predicate returning true if a given path is an argument for // /wholearchive:, or /wholearchive is enabled globally. // This function is a bit tricky because "foo.obj /wholearchive:././foo.obj" // needs to be handled as "/wholearchive:foo.obj foo.obj". - auto IsWholeArchive = [&](StringRef Path) -> bool { - if (Args.hasArg(OPT_wholearchive_flag)) + auto isWholeArchive = [&](StringRef path) -> bool { + if (args.hasArg(OPT_wholearchive_flag)) return true; - if (Optional ID = getUniqueID(Path)) - return WholeArchives.count(*ID); + if (Optional id = getUniqueID(path)) + return wholeArchives.count(*id); return false; }; // Create a list of input files. Files can be given as arguments // for /defaultlib option. - for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file)) - if (Optional Path = findFile(Arg->getValue())) - enqueuePath(*Path, IsWholeArchive(*Path)); + for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file)) + if (Optional path = findFile(arg->getValue())) + enqueuePath(*path, isWholeArchive(*path)); - for (auto *Arg : Args.filtered(OPT_defaultlib)) - if (Optional Path = findLib(Arg->getValue())) - enqueuePath(*Path, false); + for (auto *arg : args.filtered(OPT_defaultlib)) + if (Optional path = findLib(arg->getValue())) + enqueuePath(*path, false); // Windows specific -- Create a resource file containing a manifest file. - if (Config->Manifest == Configuration::Embed) + if (config->manifest == Configuration::Embed) addBuffer(createManifestRes(), false); // Read all input files given via the command line. @@ -1369,154 +1550,169 @@ void LinkerDriver::link(ArrayRef 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(convertResToCOFF(Resources))); + if (!resources.empty()) + symtab->addFile(make(convertResToCOFF(resources))); - if (Tar) - Tar->append("response.txt", - createResponseFile(Args, FilePaths, - ArrayRef(SearchPaths).slice(1))); + if (tar) + tar->append("response.txt", + createResponseFile(args, filePaths, + ArrayRef(searchPaths).slice(1))); // Handle /largeaddressaware - Config->LargeAddressAware = Args.hasFlag( - OPT_largeaddressaware, OPT_largeaddressaware_no, Config->is64()); + config->largeAddressAware = args.hasFlag( + OPT_largeaddressaware, OPT_largeaddressaware_no, config->is64()); // Handle /highentropyva - Config->HighEntropyVA = - Config->is64() && - Args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true); + config->highEntropyVA = + config->is64() && + args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true); - if (!Config->DynamicBase && - (Config->Machine == ARMNT || Config->Machine == ARM64)) + if (!config->dynamicBase && + (config->machine == ARMNT || config->machine == ARM64)) error("/dynamicbase:no is not compatible with " + - machineToStr(Config->Machine)); + machineToStr(config->machine)); // Handle /export - for (auto *Arg : Args.filtered(OPT_export)) { - Export E = parseExport(Arg->getValue()); - if (Config->Machine == I386) { - if (!isDecorated(E.Name)) - E.Name = Saver.save("_" + E.Name); - if (!E.ExtName.empty() && !isDecorated(E.ExtName)) - E.ExtName = Saver.save("_" + E.ExtName); + for (auto *arg : args.filtered(OPT_export)) { + Export e = parseExport(arg->getValue()); + if (config->machine == I386) { + if (!isDecorated(e.name)) + e.name = saver.save("_" + e.name); + if (!e.extName.empty() && !isDecorated(e.extName)) + e.extName = saver.save("_" + e.extName); } - Config->Exports.push_back(E); + config->exports.push_back(e); } // Handle /def - if (auto *Arg = Args.getLastArg(OPT_deffile)) { + if (auto *arg = args.getLastArg(OPT_deffile)) { // parseModuleDefs mutates Config object. - parseModuleDefs(Arg->getValue()); + parseModuleDefs(arg->getValue()); } // Handle generation of import library from a def file. - if (!Args.hasArg(OPT_INPUT)) { + if (!args.hasArg(OPT_INPUT)) { fixupExports(); - createImportLibrary(/*AsLib=*/true); + createImportLibrary(/*asLib=*/true); return; } // Windows specific -- if no /subsystem is given, we need to infer // that from entry point name. Must happen before /entry handling, // and after the early return when just writing an import library. - if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) { - Config->Subsystem = inferSubsystem(); - if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) + if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) { + config->subsystem = inferSubsystem(); + if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) fatal("subsystem must be defined"); } // Handle /entry and /dll - if (auto *Arg = Args.getLastArg(OPT_entry)) { - Config->Entry = addUndefined(mangle(Arg->getValue())); - } else if (!Config->Entry && !Config->NoEntry) { - if (Args.hasArg(OPT_dll)) { - StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12" + if (auto *arg = args.getLastArg(OPT_entry)) { + config->entry = addUndefined(mangle(arg->getValue())); + } else if (!config->entry && !config->noEntry) { + if (args.hasArg(OPT_dll)) { + StringRef s = (config->machine == I386) ? "__DllMainCRTStartup@12" : "_DllMainCRTStartup"; - Config->Entry = addUndefined(S); + config->entry = addUndefined(s); } else { // Windows specific -- If entry point name is not given, we need to // infer that from user-defined entry name. - StringRef S = findDefaultEntry(); - if (S.empty()) + StringRef s = findDefaultEntry(); + if (s.empty()) fatal("entry point must be defined"); - Config->Entry = addUndefined(S); - log("Entry name inferred: " + S); + config->entry = addUndefined(s); + log("Entry name inferred: " + s); } } // Handle /delayload - for (auto *Arg : Args.filtered(OPT_delayload)) { - Config->DelayLoads.insert(StringRef(Arg->getValue()).lower()); - if (Config->Machine == I386) { - Config->DelayLoadHelper = addUndefined("___delayLoadHelper2@8"); + for (auto *arg : args.filtered(OPT_delayload)) { + config->delayLoads.insert(StringRef(arg->getValue()).lower()); + if (config->machine == I386) { + config->delayLoadHelper = addUndefined("___delayLoadHelper2@8"); } else { - Config->DelayLoadHelper = addUndefined("__delayLoadHelper2"); + config->delayLoadHelper = addUndefined("__delayLoadHelper2"); } } // Set default image name if neither /out or /def set it. - if (Config->OutputFile.empty()) { - Config->OutputFile = - getOutputPath((*Args.filtered(OPT_INPUT).begin())->getValue()); + if (config->outputFile.empty()) { + config->outputFile = + getOutputPath((*args.filtered(OPT_INPUT).begin())->getValue()); } - if (ShouldCreatePDB) { + // Fail early if an output file is not writable. + if (auto e = tryCreateFile(config->outputFile)) { + error("cannot open output file " + config->outputFile + ": " + e.message()); + return; + } + + if (shouldCreatePDB) { // Put the PDB next to the image if no /pdb flag was passed. - if (Config->PDBPath.empty()) { - Config->PDBPath = Config->OutputFile; - sys::path::replace_extension(Config->PDBPath, ".pdb"); + if (config->pdbPath.empty()) { + config->pdbPath = config->outputFile; + sys::path::replace_extension(config->pdbPath, ".pdb"); } // The embedded PDB path should be the absolute path to the PDB if no // /pdbaltpath flag was passed. - if (Config->PDBAltPath.empty()) { - Config->PDBAltPath = Config->PDBPath; + if (config->pdbAltPath.empty()) { + config->pdbAltPath = config->pdbPath; // It's important to make the path absolute and remove dots. This path // will eventually be written into the PE header, and certain Microsoft // tools won't work correctly if these assumptions are not held. - sys::fs::make_absolute(Config->PDBAltPath); - sys::path::remove_dots(Config->PDBAltPath); + sys::fs::make_absolute(config->pdbAltPath); + sys::path::remove_dots(config->pdbAltPath); } else { // Don't do this earlier, so that Config->OutputFile is ready. - parsePDBAltPath(Config->PDBAltPath); + parsePDBAltPath(config->pdbAltPath); } } // Set default image base if /base is not given. - if (Config->ImageBase == uint64_t(-1)) - Config->ImageBase = getDefaultImageBase(); - - 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 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(Sym)) - if (!U->WeakAlias) - U->WeakAlias = Symtab->addUndefined(To); + if (auto *u = dyn_cast(sym)) + if (!u->weakAlias) + u->weakAlias = symtab->addUndefined(to); } // Windows specific -- if __load_config_used can be resolved, resolve it. - if (Symtab->findUnderscore("_load_config_used")) + if (symtab->findUnderscore("_load_config_used")) addUndefined(mangle("_load_config_used")); } while (run()); @@ -1559,11 +1755,29 @@ void LinkerDriver::link(ArrayRef 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(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 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(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(Sym); - if (!DC) + auto *dc = dyn_cast(sym); + if (!dc) continue; - CommonChunk *C = DC->getChunk(); - C->Alignment = std::max(C->Alignment, Alignment); + CommonChunk *c = dc->getChunk(); + c->setAlignment(std::max(c->getAlignment(), alignment)); } // Windows specific -- Create a side-by-side manifest file. - if (Config->Manifest == Configuration::SideBySide) + if (config->manifest == Configuration::SideBySide) createSideBySideManifest(); // Handle /order. We want to do this at this moment because we // need a complete list of comdat sections to warn on nonexistent // functions. - if (auto *Arg = Args.getLastArg(OPT_order)) - parseOrderFile(Arg->getValue()); + if (auto *arg = args.getLastArg(OPT_order)) + parseOrderFile(arg->getValue()); // Identify unreferenced COMDAT sections. - if (Config->DoGC) - markLive(Symtab->getChunks()); + if (config->doGC) + markLive(symtab->getChunks()); + + // Needs to happen after the last call to addFile(). + diagnoseMultipleResourceObjFiles(); // Identify identical COMDAT sections to merge them. - if (Config->DoICF) { + if (config->doICF) { findKeepUniqueSections(); - doICF(Symtab->getChunks()); + doICF(symtab->getChunks()); } // Write the result. @@ -1673,7 +1883,7 @@ void LinkerDriver::link(ArrayRef 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 e779721..6100c3c 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 Args); + llvm::opt::InputArgList parseLINK(std::vector args); // Tokenizes a given string and then parses as command line options. - llvm::opt::InputArgList parse(StringRef S) { return parse(tokenize(S)); } + llvm::opt::InputArgList parse(StringRef s) { return parse(tokenize(s)); } // Tokenizes a given string and then parses as command line options in // .drectve section. /EXPORT options are returned in second element // to be processed in fastpath. std::pair> - parseDirectives(StringRef S); + parseDirectives(StringRef s); private: // Parses command line options. - llvm::opt::InputArgList parse(llvm::ArrayRef Args); + llvm::opt::InputArgList parse(llvm::ArrayRef args); - std::vector tokenize(StringRef S); + std::vector tokenize(StringRef s); - COFFOptTable Table; + COFFOptTable table; }; class LinkerDriver { public: - void link(llvm::ArrayRef Args); + void link(llvm::ArrayRef args); // Used by the resolver to parse .drectve section contents. - void parseDirectives(StringRef S); + void parseDirectives(InputFile *file); // Used by ArchiveFile to enqueue members. - void enqueueArchiveMember(const Archive::Child &C, StringRef SymName, - StringRef ParentName); + void enqueueArchiveMember(const Archive::Child &c, StringRef symName, + StringRef parentName); + + MemoryBufferRef takeBuffer(std::unique_ptr mb); - MemoryBufferRef takeBuffer(std::unique_ptr MB); + void enqueuePath(StringRef path, bool wholeArchive); private: - std::unique_ptr Tar; // for /linkrepro + std::unique_ptr tar; // for /linkrepro // Opens a file. Path has to be resolved already. - MemoryBufferRef openFile(StringRef Path); + MemoryBufferRef openFile(StringRef path); // Searches a file from search paths. - Optional findFile(StringRef Filename); - Optional findLib(StringRef Filename); - StringRef doFindFile(StringRef Filename); - StringRef doFindLib(StringRef Filename); - StringRef doFindLibMinGW(StringRef Filename); + Optional findFile(StringRef filename); + Optional findLib(StringRef filename); + StringRef doFindFile(StringRef filename); + StringRef doFindLib(StringRef filename); + StringRef doFindLibMinGW(StringRef filename); // Parses LIB environment which contains a list of search paths. void addLibSearchPaths(); // Library search path. The first element is always "" (current directory). - std::vector SearchPaths; + std::vector searchPaths; + + void maybeExportMinGWSymbols(const llvm::opt::InputArgList &args); // We don't want to add the same file more than once. // Files are uniquified by their filesystem and file number. - std::set VisitedFiles; + std::set visitedFiles; + + std::set visitedLibs; - std::set VisitedLibs; + Symbol *addUndefined(StringRef sym); - Symbol *addUndefined(StringRef Sym); + StringRef mangleMaybe(Symbol *s); // Windows specific -- "main" is not the only main function in Windows. // You can choose one from these four -- {w,}{WinMain,main}. @@ -115,60 +120,60 @@ private: StringRef findDefaultEntry(); WindowsSubsystem inferSubsystem(); - void addBuffer(std::unique_ptr MB, bool WholeArchive); - void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName, - StringRef ParentName); + void addBuffer(std::unique_ptr mb, bool wholeArchive); + void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName, + StringRef parentName, uint64_t offsetInArchive); - void enqueuePath(StringRef Path, bool WholeArchive); - - void enqueueTask(std::function Task); + void enqueueTask(std::function task); bool run(); - std::list> TaskQueue; - std::vector FilePaths; - std::vector Resources; + std::list> taskQueue; + std::vector filePaths; + std::vector resources; - llvm::StringSet<> DirectivesExports; + llvm::StringSet<> directivesExports; }; // Functions below this line are defined in DriverUtils.cpp. -void printHelp(const char *Argv0); - -// For /machine option. -MachineTypes getMachineType(StringRef Arg); -StringRef machineToStr(MachineTypes MT); +void printHelp(const char *argv0); // Parses a string in the form of "[,]". -void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size = nullptr); +void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr); -void parseGuard(StringRef Arg); +void parseGuard(StringRef arg); // Parses a string in the form of "[.]". // Minor's default value is 0. -void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor); +void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor); // Parses a string in the form of "[,[.]]". -void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major, - uint32_t *Minor); +void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, + uint32_t *minor); void parseAlternateName(StringRef); void parseMerge(StringRef); void parseSection(StringRef); void parseAligncomm(StringRef); +// Parses a string in the form of "[:]" +void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine); + // Parses a string in the form of "EMBED[,=]|NO". -void parseManifest(StringRef Arg); +void parseManifest(StringRef arg); // Parses a string in the form of "level=|uiAccess=" -void parseManifestUAC(StringRef Arg); +void parseManifestUAC(StringRef arg); + +// Parses a string in the form of "cd|net[,(cd|net)]*" +void parseSwaprun(StringRef arg); // Create a resource file containing a manifest XML. std::unique_ptr createManifestRes(); void createSideBySideManifest(); // Used for dllexported symbols. -Export parseExport(StringRef Arg); +Export parseExport(StringRef arg); void fixupExports(); void assignExportOrdinals(); @@ -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 MBs); +MemoryBufferRef convertResToCOFF(ArrayRef mbs); -void runMSVCLinker(std::string Rsp, ArrayRef Objects); +void runMSVCLinker(std::string rsp, ArrayRef objects); // Create enum with OPT_xxx values for each option in Options.td enum { diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp index 3a11895..4360ac2 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 ExeOrErr = sys::findProgramByName(Prog); - if (auto EC = ExeOrErr.getError()) - fatal("unable to find " + Prog + " in PATH: " + EC.message()); - StringRef Exe = Saver.save(*ExeOrErr); - Args.insert(Args.begin(), Exe); + ErrorOr exeOrErr = sys::findProgramByName(prog); + if (auto ec = exeOrErr.getError()) + fatal("unable to find " + prog + " in PATH: " + ec.message()); + StringRef exe = saver.save(*exeOrErr); + args.insert(args.begin(), exe); - if (sys::ExecuteAndWait(Args[0], Args) != 0) + if (sys::ExecuteAndWait(args[0], args) != 0) fatal("ExecuteAndWait failed: " + - llvm::join(Args.begin(), Args.end(), " ")); + llvm::join(args.begin(), args.end(), " ")); } private: - StringRef Prog; - std::vector Args; + StringRef prog; + std::vector args; }; } // anonymous namespace -// Returns /machine's value. -MachineTypes getMachineType(StringRef S) { - MachineTypes MT = StringSwitch(S.lower()) - .Cases("x64", "amd64", AMD64) - .Cases("x86", "i386", I386) - .Case("arm", ARMNT) - .Case("arm64", ARM64) - .Default(IMAGE_FILE_MACHINE_UNKNOWN); - if (MT != IMAGE_FILE_MACHINE_UNKNOWN) - return MT; - fatal("unknown /machine argument: " + S); -} - -StringRef machineToStr(MachineTypes MT) { - switch (MT) { - case ARMNT: - return "arm"; - case ARM64: - return "arm64"; - case AMD64: - return "x64"; - case I386: - return "x86"; - default: - llvm_unreachable("unknown machine type"); - } -} - // Parses a string in the form of "[,]". -void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size) { - StringRef S1, S2; - std::tie(S1, S2) = Arg.split(','); - if (S1.getAsInteger(0, *Addr)) - fatal("invalid number: " + S1); - if (Size && !S2.empty() && S2.getAsInteger(0, *Size)) - fatal("invalid number: " + S2); +void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) { + StringRef s1, s2; + std::tie(s1, s2) = arg.split(','); + if (s1.getAsInteger(0, *addr)) + fatal("invalid number: " + s1); + if (size && !s2.empty() && s2.getAsInteger(0, *size)) + fatal("invalid number: " + s2); } // Parses a string in the form of "[.]". // If second number is not present, Minor is set to 0. -void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor) { - StringRef S1, S2; - std::tie(S1, S2) = Arg.split('.'); - if (S1.getAsInteger(0, *Major)) - fatal("invalid number: " + S1); - *Minor = 0; - if (!S2.empty() && S2.getAsInteger(0, *Minor)) - fatal("invalid number: " + S2); -} - -void parseGuard(StringRef FullArg) { - SmallVector SplitArgs; - FullArg.split(SplitArgs, ","); - for (StringRef Arg : SplitArgs) { - if (Arg.equals_lower("no")) - Config->GuardCF = GuardCFLevel::Off; - else if (Arg.equals_lower("nolongjmp")) - Config->GuardCF = GuardCFLevel::NoLongJmp; - else if (Arg.equals_lower("cf") || Arg.equals_lower("longjmp")) - Config->GuardCF = GuardCFLevel::Full; +void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) { + StringRef s1, s2; + std::tie(s1, s2) = arg.split('.'); + if (s1.getAsInteger(0, *major)) + fatal("invalid number: " + s1); + *minor = 0; + if (!s2.empty() && s2.getAsInteger(0, *minor)) + fatal("invalid number: " + s2); +} + +void parseGuard(StringRef fullArg) { + SmallVector splitArgs; + fullArg.split(splitArgs, ","); + for (StringRef arg : splitArgs) { + if (arg.equals_lower("no")) + config->guardCF = GuardCFLevel::Off; + else if (arg.equals_lower("nolongjmp")) + config->guardCF = GuardCFLevel::NoLongJmp; + else if (arg.equals_lower("cf") || arg.equals_lower("longjmp")) + config->guardCF = GuardCFLevel::Full; else - fatal("invalid argument to /guard: " + Arg); + fatal("invalid argument to /guard: " + arg); } } // Parses a string in the form of "[,[.]]". -void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major, - uint32_t *Minor) { - StringRef SysStr, Ver; - std::tie(SysStr, Ver) = Arg.split(','); - *Sys = StringSwitch(SysStr.lower()) +void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, + uint32_t *minor) { + StringRef sysStr, ver; + std::tie(sysStr, ver) = arg.split(','); + std::string sysStrLower = sysStr.lower(); + *sys = StringSwitch(sysStrLower) .Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION) .Case("console", IMAGE_SUBSYSTEM_WINDOWS_CUI) + .Case("default", IMAGE_SUBSYSTEM_UNKNOWN) .Case("efi_application", IMAGE_SUBSYSTEM_EFI_APPLICATION) .Case("efi_boot_service_driver", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) .Case("efi_rom", IMAGE_SUBSYSTEM_EFI_ROM) @@ -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 "=". // Results are directly written to Config. -void parseAlternateName(StringRef S) { - StringRef From, To; - std::tie(From, To) = S.split('='); - if (From.empty() || To.empty()) - fatal("/alternatename: invalid argument: " + S); - auto It = Config->AlternateNames.find(From); - if (It != Config->AlternateNames.end() && It->second != To) - fatal("/alternatename: conflicts: " + S); - Config->AlternateNames.insert(It, std::make_pair(From, To)); +void parseAlternateName(StringRef s) { + StringRef from, to; + std::tie(from, to) = s.split('='); + if (from.empty() || to.empty()) + fatal("/alternatename: invalid argument: " + s); + auto it = config->alternateNames.find(from); + if (it != config->alternateNames.end() && it->second != to) + fatal("/alternatename: conflicts: " + s); + config->alternateNames.insert(it, std::make_pair(from, to)); } // Parse a string of the form of "=". // Results are directly written to Config. -void parseMerge(StringRef S) { - StringRef From, To; - std::tie(From, To) = S.split('='); - if (From.empty() || To.empty()) - fatal("/merge: invalid argument: " + S); - if (From == ".rsrc" || To == ".rsrc") +void parseMerge(StringRef s) { + StringRef from, to; + std::tie(from, to) = s.split('='); + if (from.empty() || to.empty()) + fatal("/merge: invalid argument: " + s); + if (from == ".rsrc" || to == ".rsrc") fatal("/merge: cannot merge '.rsrc' with any section"); - if (From == ".reloc" || To == ".reloc") + if (from == ".reloc" || to == ".reloc") fatal("/merge: cannot merge '.reloc' with any section"); - auto Pair = Config->Merge.insert(std::make_pair(From, To)); - bool Inserted = Pair.second; - if (!Inserted) { - StringRef Existing = Pair.first->second; - if (Existing != To) - warn(S + ": already merged into " + Existing); + auto pair = config->merge.insert(std::make_pair(from, to)); + bool inserted = pair.second; + if (!inserted) { + StringRef existing = pair.first->second; + if (existing != to) + warn(s + ": already merged into " + existing); } } -static uint32_t parseSectionAttributes(StringRef S) { - uint32_t Ret = 0; - for (char C : S.lower()) { - switch (C) { +static uint32_t parseSectionAttributes(StringRef s) { + uint32_t ret = 0; + for (char c : s.lower()) { + switch (c) { case 'd': - Ret |= IMAGE_SCN_MEM_DISCARDABLE; + ret |= IMAGE_SCN_MEM_DISCARDABLE; break; case 'e': - Ret |= IMAGE_SCN_MEM_EXECUTE; + ret |= IMAGE_SCN_MEM_EXECUTE; break; case 'k': - Ret |= IMAGE_SCN_MEM_NOT_CACHED; + ret |= IMAGE_SCN_MEM_NOT_CACHED; break; case 'p': - Ret |= IMAGE_SCN_MEM_NOT_PAGED; + ret |= IMAGE_SCN_MEM_NOT_PAGED; break; case 'r': - Ret |= IMAGE_SCN_MEM_READ; + ret |= IMAGE_SCN_MEM_READ; break; case 's': - Ret |= IMAGE_SCN_MEM_SHARED; + ret |= IMAGE_SCN_MEM_SHARED; break; case 'w': - Ret |= IMAGE_SCN_MEM_WRITE; + ret |= IMAGE_SCN_MEM_WRITE; break; default: - fatal("/section: invalid argument: " + S); + fatal("/section: invalid argument: " + s); } } - return Ret; + return ret; } // Parses /section option argument. -void parseSection(StringRef S) { - StringRef Name, Attrs; - std::tie(Name, Attrs) = S.split(','); - if (Name.empty() || Attrs.empty()) - fatal("/section: invalid argument: " + S); - Config->Section[Name] = parseSectionAttributes(Attrs); +void parseSection(StringRef s) { + StringRef name, attrs; + std::tie(name, attrs) = s.split(','); + if (name.empty() || attrs.empty()) + fatal("/section: invalid argument: " + s); + config->section[name] = parseSectionAttributes(attrs); } // Parses /aligncomm option argument. -void parseAligncomm(StringRef S) { - StringRef Name, Align; - std::tie(Name, Align) = S.split(','); - if (Name.empty() || Align.empty()) { - error("/aligncomm: invalid argument: " + S); +void parseAligncomm(StringRef s) { + StringRef name, align; + std::tie(name, align) = s.split(','); + if (name.empty() || align.empty()) { + error("/aligncomm: invalid argument: " + s); + return; + } + int v; + if (align.getAsInteger(0, v)) { + error("/aligncomm: invalid argument: " + s); 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[,=]|NO". // Results are directly written to Config. -void parseManifest(StringRef Arg) { - if (Arg.equals_lower("no")) { - Config->Manifest = Configuration::No; +void parseManifest(StringRef arg) { + if (arg.equals_lower("no")) { + config->manifest = Configuration::No; return; } - if (!Arg.startswith_lower("embed")) - fatal("invalid option " + Arg); - Config->Manifest = Configuration::Embed; - Arg = Arg.substr(strlen("embed")); - if (Arg.empty()) + if (!arg.startswith_lower("embed")) + fatal("invalid option " + arg); + config->manifest = Configuration::Embed; + arg = arg.substr(strlen("embed")); + if (arg.empty()) return; - if (!Arg.startswith_lower(",id=")) - fatal("invalid option " + Arg); - Arg = Arg.substr(strlen(",id=")); - if (Arg.getAsInteger(0, Config->ManifestID)) - fatal("invalid option " + Arg); + if (!arg.startswith_lower(",id=")) + fatal("invalid option " + arg); + arg = arg.substr(strlen(",id=")); + if (arg.getAsInteger(0, config->manifestID)) + fatal("invalid option " + arg); } // Parses a string in the form of "level=|uiAccess=|NO". // Results are directly written to Config. -void parseManifestUAC(StringRef Arg) { - if (Arg.equals_lower("no")) { - Config->ManifestUAC = false; +void parseManifestUAC(StringRef arg) { + if (arg.equals_lower("no")) { + config->manifestUAC = false; return; } for (;;) { - Arg = Arg.ltrim(); - if (Arg.empty()) + arg = arg.ltrim(); + if (arg.empty()) return; - if (Arg.startswith_lower("level=")) { - Arg = Arg.substr(strlen("level=")); - std::tie(Config->ManifestLevel, Arg) = Arg.split(" "); + if (arg.startswith_lower("level=")) { + arg = arg.substr(strlen("level=")); + std::tie(config->manifestLevel, arg) = arg.split(" "); continue; } - if (Arg.startswith_lower("uiaccess=")) { - Arg = Arg.substr(strlen("uiaccess=")); - std::tie(Config->ManifestUIAccess, Arg) = Arg.split(" "); + if (arg.startswith_lower("uiaccess=")) { + arg = arg.substr(strlen("uiaccess=")); + std::tie(config->manifestUIAccess, arg) = arg.split(" "); continue; } - fatal("invalid option " + Arg); + fatal("invalid option " + arg); } } +// Parses a string in the form of "cd|net[,(cd|net)]*" +// Results are directly written to Config. +void parseSwaprun(StringRef arg) { + do { + StringRef swaprun, newArg; + std::tie(swaprun, newArg) = arg.split(','); + if (swaprun.equals_lower("cd")) + config->swaprunCD = true; + else if (swaprun.equals_lower("net")) + config->swaprunNet = true; + else if (swaprun.empty()) + error("/swaprun: missing argument"); + else + error("/swaprun: invalid argument: " + swaprun); + // To catch trailing commas, e.g. `/spawrun:cd,` + if (newArg.empty() && arg.endswith(",")) + error("/swaprun: missing argument"); + arg = newArg; + } while (!arg.empty()); +} + // An RAII temporary file class that automatically removes a temporary file. namespace { class TemporaryFile { public: - TemporaryFile(StringRef Prefix, StringRef Extn, StringRef Contents = "") { - SmallString<128> S; - if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S)) - fatal("cannot create a temporary file: " + EC.message()); - Path = S.str(); - - 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 getMemoryBuffer() { - // IsVolatileSize=true forces MemoryBuffer to not use mmap(). - return CHECK(MemoryBuffer::getFile(Path, /*FileSize=*/-1, + // IsVolatile=true forces MemoryBuffer to not use mmap(). + return CHECK(MemoryBuffer::getFile(path, /*FileSize=*/-1, /*RequiresNullTerminator=*/false, - /*IsVolatileSize=*/true), - "could not open " + Path); + /*IsVolatile=*/true), + "could not open " + path); } - std::string Path; + std::string path; }; } static std::string createDefaultXml() { - std::string Ret; - raw_string_ostream OS(Ret); + std::string ret; + raw_string_ostream os(ret); // Emit the XML. Note that we do *not* verify that the XML attributes are // syntactically correct. This is intentional for link.exe compatibility. - OS << "\n" + os << "\n" << "\n"; - if (Config->ManifestUAC) { - OS << " \n" + if (config->manifestUAC) { + os << " \n" << " \n" << " \n" - << " \n" + << " \n" << " \n" << " \n" << " \n"; } - if (!Config->ManifestDependency.empty()) { - OS << " \n" + if (!config->manifestDependency.empty()) { + os << " \n" << " \n" - << " ManifestDependency << " />\n" + << " manifestDependency << " />\n" << " \n" << " \n"; } - OS << "\n"; - return OS.str(); + os << "\n"; + return os.str(); } -static std::string createManifestXmlWithInternalMt(StringRef DefaultXml) { - std::unique_ptr DefaultXmlCopy = - MemoryBuffer::getMemBufferCopy(DefaultXml); +static std::string createManifestXmlWithInternalMt(StringRef defaultXml) { + std::unique_ptr defaultXmlCopy = + MemoryBuffer::getMemBufferCopy(defaultXml); - windows_manifest::WindowsManifestMerger Merger; - if (auto E = Merger.merge(*DefaultXmlCopy.get())) + windows_manifest::WindowsManifestMerger merger; + if (auto e = merger.merge(*defaultXmlCopy.get())) fatal("internal manifest tool failed on default xml: " + - toString(std::move(E))); + toString(std::move(e))); - for (StringRef Filename : Config->ManifestInput) { - std::unique_ptr Manifest = - check(MemoryBuffer::getFile(Filename)); - if (auto E = Merger.merge(*Manifest.get())) - fatal("internal manifest tool failed on file " + Filename + ": " + - toString(std::move(E))); + for (StringRef filename : config->manifestInput) { + std::unique_ptr manifest = + check(MemoryBuffer::getFile(filename)); + if (auto e = merger.merge(*manifest.get())) + fatal("internal manifest tool failed on file " + filename + ": " + + toString(std::move(e))); } - return Merger.getMergedManifest().get()->getBuffer(); + return merger.getMergedManifest().get()->getBuffer(); } -static std::string createManifestXmlWithExternalMt(StringRef DefaultXml) { +static std::string createManifestXmlWithExternalMt(StringRef defaultXml) { // Create the default manifest file as a temporary file. TemporaryFile Default("defaultxml", "manifest"); - std::error_code EC; - raw_fd_ostream OS(Default.Path, EC, sys::fs::F_Text); - if (EC) - fatal("failed to open " + Default.Path + ": " + EC.message()); - OS << DefaultXml; - OS.close(); + std::error_code ec; + raw_fd_ostream os(Default.path, ec, sys::fs::F_Text); + if (ec) + fatal("failed to open " + Default.path + ": " + ec.message()); + os << defaultXml; + os.close(); // Merge user-supplied manifests if they are given. Since libxml2 is not // enabled, we must shell out to Microsoft's mt.exe tool. - TemporaryFile User("user", "manifest"); + TemporaryFile user("user", "manifest"); - Executor E("mt.exe"); - E.add("/manifest"); - E.add(Default.Path); - for (StringRef Filename : Config->ManifestInput) { - E.add("/manifest"); - E.add(Filename); + Executor e("mt.exe"); + e.add("/manifest"); + e.add(Default.path); + for (StringRef filename : config->manifestInput) { + e.add("/manifest"); + e.add(filename); } - E.add("/nologo"); - E.add("/out:" + StringRef(User.Path)); - E.run(); + e.add("/nologo"); + e.add("/out:" + StringRef(user.path)); + e.run(); - return CHECK(MemoryBuffer::getFile(User.Path), "could not open " + User.Path) + return CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path) .get() ->getBuffer(); } static std::string createManifestXml() { - std::string DefaultXml = createDefaultXml(); - if (Config->ManifestInput.empty()) - return DefaultXml; + std::string defaultXml = createDefaultXml(); + if (config->manifestInput.empty()) + return defaultXml; if (windows_manifest::isAvailable()) - return createManifestXmlWithInternalMt(DefaultXml); + return createManifestXmlWithInternalMt(defaultXml); - return createManifestXmlWithExternalMt(DefaultXml); + return createManifestXmlWithExternalMt(defaultXml); } static std::unique_ptr -createMemoryBufferForManifestRes(size_t ManifestSize) { - size_t ResSize = alignTo( +createMemoryBufferForManifestRes(size_t manifestSize) { + size_t resSize = alignTo( object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE + sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) + - sizeof(object::WinResHeaderSuffix) + ManifestSize, + sizeof(object::WinResHeaderSuffix) + manifestSize, object::WIN_RES_DATA_ALIGNMENT); - return WritableMemoryBuffer::getNewMemBuffer(ResSize, Config->OutputFile + + return WritableMemoryBuffer::getNewMemBuffer(resSize, config->outputFile + ".manifest.res"); } -static void writeResFileHeader(char *&Buf) { - memcpy(Buf, COFF::WinResMagic, sizeof(COFF::WinResMagic)); - Buf += sizeof(COFF::WinResMagic); - memset(Buf, 0, object::WIN_RES_NULL_ENTRY_SIZE); - Buf += object::WIN_RES_NULL_ENTRY_SIZE; +static void writeResFileHeader(char *&buf) { + memcpy(buf, COFF::WinResMagic, sizeof(COFF::WinResMagic)); + buf += sizeof(COFF::WinResMagic); + memset(buf, 0, object::WIN_RES_NULL_ENTRY_SIZE); + buf += object::WIN_RES_NULL_ENTRY_SIZE; } -static void writeResEntryHeader(char *&Buf, size_t ManifestSize) { +static void writeResEntryHeader(char *&buf, size_t manifestSize) { // Write the prefix. - auto *Prefix = reinterpret_cast(Buf); - Prefix->DataSize = ManifestSize; - Prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) + + auto *prefix = reinterpret_cast(buf); + prefix->DataSize = manifestSize; + prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) + sizeof(object::WinResHeaderSuffix); - Buf += sizeof(object::WinResHeaderPrefix); + buf += sizeof(object::WinResHeaderPrefix); // Write the Type/Name IDs. - auto *IDs = reinterpret_cast(Buf); - IDs->setType(RT_MANIFEST); - IDs->setName(Config->ManifestID); - Buf += sizeof(object::WinResIDs); + auto *iDs = reinterpret_cast(buf); + iDs->setType(RT_MANIFEST); + iDs->setName(config->manifestID); + buf += sizeof(object::WinResIDs); // Write the suffix. - auto *Suffix = reinterpret_cast(Buf); - Suffix->DataVersion = 0; - Suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE; - Suffix->Language = SUBLANG_ENGLISH_US; - Suffix->Version = 0; - Suffix->Characteristics = 0; - Buf += sizeof(object::WinResHeaderSuffix); + auto *suffix = reinterpret_cast(buf); + suffix->DataVersion = 0; + suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE; + suffix->Language = SUBLANG_ENGLISH_US; + suffix->Version = 0; + suffix->Characteristics = 0; + buf += sizeof(object::WinResHeaderSuffix); } // Create a resource file containing a manifest XML. std::unique_ptr createManifestRes() { - std::string Manifest = createManifestXml(); + std::string manifest = createManifestXml(); - std::unique_ptr Res = - createMemoryBufferForManifestRes(Manifest.size()); + std::unique_ptr res = + createMemoryBufferForManifestRes(manifest.size()); - char *Buf = Res->getBufferStart(); - writeResFileHeader(Buf); - writeResEntryHeader(Buf, Manifest.size()); + char *buf = res->getBufferStart(); + writeResFileHeader(buf); + writeResEntryHeader(buf, manifest.size()); // Copy the manifest data into the .res file. - std::copy(Manifest.begin(), Manifest.end(), Buf); - return std::move(Res); + std::copy(manifest.begin(), manifest.end(), buf); + return std::move(res); } void createSideBySideManifest() { - std::string Path = Config->ManifestFile; - if (Path == "") - Path = Config->OutputFile + ".manifest"; - std::error_code EC; - raw_fd_ostream Out(Path, EC, sys::fs::F_Text); - if (EC) - fatal("failed to create manifest: " + EC.message()); - Out << createManifestXml(); + std::string path = config->manifestFile; + if (path == "") + path = config->outputFile + ".manifest"; + std::error_code ec; + raw_fd_ostream out(path, ec, sys::fs::F_Text); + if (ec) + fatal("failed to create manifest: " + ec.message()); + out << createManifestXml(); } // Parse a string in the form of // "[=][,@ordinal[,NONAME]][,DATA][,PRIVATE]" // or "=.". // Used for parsing /export arguments. -Export parseExport(StringRef Arg) { - Export E; - StringRef Rest; - std::tie(E.Name, Rest) = Arg.split(","); - if (E.Name.empty()) +Export parseExport(StringRef arg) { + Export e; + StringRef rest; + std::tie(e.name, rest) = arg.split(","); + if (e.name.empty()) goto err; - if (E.Name.contains('=')) { - StringRef X, Y; - std::tie(X, Y) = E.Name.split("="); + if (e.name.contains('=')) { + StringRef x, y; + std::tie(x, y) = e.name.split("="); // If "=.". - if (Y.contains(".")) { - E.Name = X; - E.ForwardTo = Y; - return E; + if (y.contains(".")) { + e.name = x; + e.forwardTo = y; + return e; } - E.ExtName = X; - E.Name = Y; - if (E.Name.empty()) + e.extName = x; + e.name = y; + if (e.name.empty()) goto err; } // If "=[,@ordinal[,NONAME]][,DATA][,PRIVATE]" - while (!Rest.empty()) { - StringRef Tok; - std::tie(Tok, Rest) = Rest.split(","); - if (Tok.equals_lower("noname")) { - if (E.Ordinal == 0) + while (!rest.empty()) { + StringRef tok; + std::tie(tok, rest) = rest.split(","); + if (tok.equals_lower("noname")) { + if (e.ordinal == 0) goto err; - E.Noname = true; + e.noname = true; continue; } - if (Tok.equals_lower("data")) { - E.Data = true; + if (tok.equals_lower("data")) { + e.data = true; continue; } - if (Tok.equals_lower("constant")) { - E.Constant = true; + if (tok.equals_lower("constant")) { + e.constant = true; continue; } - if (Tok.equals_lower("private")) { - E.Private = true; + if (tok.equals_lower("private")) { + e.isPrivate = true; continue; } - if (Tok.startswith("@")) { - int32_t Ord; - if (Tok.substr(1).getAsInteger(0, Ord)) + if (tok.startswith("@")) { + int32_t ord; + if (tok.substr(1).getAsInteger(0, ord)) goto err; - if (Ord <= 0 || 65535 < Ord) + if (ord <= 0 || 65535 < ord) goto err; - E.Ordinal = Ord; + e.ordinal = ord; continue; } goto err; } - return E; + return e; err: - fatal("invalid /export: " + Arg); + fatal("invalid /export: " + arg); } -static StringRef undecorate(StringRef Sym) { - if (Config->Machine != I386) - return Sym; +static StringRef undecorate(StringRef sym) { + if (config->machine != I386) + return sym; // In MSVC mode, a fully decorated stdcall function is exported // as-is with the leading underscore (with type IMPORT_NAME). // In MinGW mode, a decorated stdcall function gets the underscore // removed, just like normal cdecl functions. - if (Sym.startswith("_") && Sym.contains('@') && !Config->MinGW) - return Sym; - return Sym.startswith("_") ? Sym.substr(1) : Sym; + if (sym.startswith("_") && sym.contains('@') && !config->mingw) + return sym; + return sym.startswith("_") ? sym.substr(1) : sym; } // Convert stdcall/fastcall style symbols into unsuffixed symbols, // with or without a leading underscore. (MinGW specific.) -static StringRef killAt(StringRef Sym, bool Prefix) { - if (Sym.empty()) - return Sym; +static StringRef killAt(StringRef sym, bool prefix) { + if (sym.empty()) + return sym; // Strip any trailing stdcall suffix - Sym = Sym.substr(0, Sym.find('@', 1)); - if (!Sym.startswith("@")) { - if (Prefix && !Sym.startswith("_")) - return Saver.save("_" + Sym); - return Sym; + sym = sym.substr(0, sym.find('@', 1)); + if (!sym.startswith("@")) { + if (prefix && !sym.startswith("_")) + return saver.save("_" + sym); + return sym; } // For fastcall, remove the leading @ and replace it with an // underscore, if prefixes are used. - Sym = Sym.substr(1); - if (Prefix) - Sym = Saver.save("_" + Sym); - return Sym; + sym = sym.substr(1); + if (prefix) + sym = saver.save("_" + sym); + return sym; } // Performs error checking on all /export arguments. // It also sets ordinals. void fixupExports() { // Symbol ordinals must be unique. - std::set Ords; - for (Export &E : Config->Exports) { - if (E.Ordinal == 0) + std::set ords; + for (Export &e : config->exports) { + if (e.ordinal == 0) continue; - if (!Ords.insert(E.Ordinal).second) - fatal("duplicate export ordinal: " + E.Name); - } - - for (Export &E : Config->Exports) { - Symbol *Sym = E.Sym; - if (!E.ForwardTo.empty() || !Sym) { - E.SymbolName = E.Name; - } else { - if (auto *U = dyn_cast(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 Map(Config->Exports.size()); - std::vector V; - for (Export &E : Config->Exports) { - auto Pair = Map.insert(std::make_pair(E.ExportName, &E)); - bool Inserted = Pair.second; - if (Inserted) { - V.push_back(E); + DenseMap map(config->exports.size()); + std::vector v; + for (Export &e : config->exports) { + auto pair = map.insert(std::make_pair(e.exportName, &e)); + bool inserted = pair.second; + if (inserted) { + v.push_back(e); continue; } - Export *Existing = Pair.first->second; - if (E == *Existing || E.Name != Existing->Name) + Export *existing = pair.first->second; + if (e == *existing || e.name != existing->name) continue; - warn("duplicate /export option: " + E.Name); + warn("duplicate /export option: " + e.name); } - Config->Exports = std::move(V); + config->exports = std::move(v); // Sort by name. - std::sort(Config->Exports.begin(), Config->Exports.end(), - [](const Export &A, const Export &B) { - return A.ExportName < B.ExportName; + std::sort(config->exports.begin(), config->exports.end(), + [](const Export &a, const Export &b) { + return a.exportName < b.exportName; }); } void assignExportOrdinals() { // Assign unique ordinals if default (= 0). - uint16_t Max = 0; - for (Export &E : Config->Exports) - Max = std::max(Max, E.Ordinal); - for (Export &E : Config->Exports) - if (E.Ordinal == 0) - E.Ordinal = ++Max; + uint16_t max = 0; + for (Export &e : config->exports) + max = std::max(max, e.ordinal); + for (Export &e : config->exports) + if (e.ordinal == 0) + e.ordinal = ++max; } // Parses a string in the form of "key=value" and check // if value matches previous values for the same key. -void checkFailIfMismatch(StringRef Arg) { - StringRef K, V; - std::tie(K, V) = Arg.split('='); - if (K.empty() || V.empty()) - fatal("/failifmismatch: invalid argument: " + Arg); - StringRef Existing = Config->MustMatch[K]; - if (!Existing.empty() && V != Existing) - fatal("/failifmismatch: mismatch detected: " + Existing + " and " + V + - " for key " + K); - Config->MustMatch[K] = V; +void checkFailIfMismatch(StringRef arg, InputFile *source) { + StringRef k, v; + std::tie(k, v) = arg.split('='); + if (k.empty() || v.empty()) + fatal("/failifmismatch: invalid argument: " + arg); + std::pair existing = config->mustMatch[k]; + if (!existing.first.empty() && v != existing.first) { + std::string sourceStr = source ? toString(source) : "cmd-line"; + std::string existingStr = + existing.second ? toString(existing.second) : "cmd-line"; + fatal("/failifmismatch: mismatch detected for '" + k + "':\n>>> " + + existingStr + " has value " + existing.first + "\n>>> " + sourceStr + + " has value " + v); + } + config->mustMatch[k] = {v, source}; } // Convert Windows resource files (.res files) to a .obj file. -MemoryBufferRef convertResToCOFF(ArrayRef MBs) { - object::WindowsResourceParser Parser; - - for (MemoryBufferRef MB : MBs) { - std::unique_ptr Bin = check(object::createBinary(MB)); - object::WindowsResource *RF = dyn_cast(Bin.get()); - if (!RF) +// Does what cvtres.exe does, but in-process and cross-platform. +MemoryBufferRef convertResToCOFF(ArrayRef mbs) { + object::WindowsResourceParser parser; + + for (MemoryBufferRef mb : mbs) { + std::unique_ptr bin = check(object::createBinary(mb)); + object::WindowsResource *rf = dyn_cast(bin.get()); + if (!rf) fatal("cannot compile non-resource file as resource"); - if (auto EC = Parser.parse(RF)) - fatal("failed to parse .res file: " + toString(std::move(EC))); + + std::vector duplicates; + if (auto ec = parser.parse(rf, duplicates)) + fatal(toString(std::move(ec))); + + for (const auto &dupeDiag : duplicates) + if (config->forceMultipleRes) + warn(dupeDiag); + else + error(dupeDiag); } - Expected> E = - llvm::object::writeWindowsResourceCOFF(Config->Machine, Parser); - if (!E) - fatal("failed to write .res to COFF: " + toString(E.takeError())); + Expected> e = + llvm::object::writeWindowsResourceCOFF(config->machine, parser, + config->timestamp); + if (!e) + fatal("failed to write .res to COFF: " + toString(e.takeError())); - MemoryBufferRef MBRef = **E; - make>(std::move(*E)); // take ownership - return MBRef; + MemoryBufferRef mbref = **e; + make>(std::move(*e)); // take ownership + return mbref; } // Create OptTable @@ -721,7 +739,7 @@ MemoryBufferRef convertResToCOFF(ArrayRef 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 Argv) { +opt::InputArgList ArgParser::parse(ArrayRef argv) { // Make InputArgList from string vectors. - unsigned MissingIndex; - unsigned MissingCount; + unsigned missingIndex; + unsigned missingCount; // We need to get the quoting style for response files before parsing all // options so we parse here before and ignore all the options but // --rsp-quoting. - opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount); + opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount); // Expand response files (arguments in the form of @) // and then parse the argument again. - SmallVector ExpandedArgv(Argv.data(), Argv.data() + Argv.size()); - cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), ExpandedArgv); - Args = Table.ParseArgs(makeArrayRef(ExpandedArgv).drop_front(), MissingIndex, - MissingCount); + SmallVector expandedArgv(argv.data(), + argv.data() + argv.size()); + cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv); + args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex, + missingCount); // Print the real command line if response files are expanded. - if (Args.hasArg(OPT_verbose) && Argv.size() != ExpandedArgv.size()) { - std::string Msg = "Command line:"; - for (const char *S : ExpandedArgv) - Msg += " " + std::string(S); - message(Msg); + if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) { + std::string msg = "Command line:"; + for (const char *s : expandedArgv) + msg += " " + std::string(s); + message(msg); } // Save the command line after response file expansion so we can write it to // the PDB if necessary. - Config->Argv = {ExpandedArgv.begin(), ExpandedArgv.end()}; + config->argv = {expandedArgv.begin(), expandedArgv.end()}; // Handle /WX early since it converts missing argument warnings to errors. - errorHandler().FatalWarnings = Args.hasFlag(OPT_WX, OPT_WX_no, false); + errorHandler().fatalWarnings = args.hasFlag(OPT_WX, OPT_WX_no, false); - if (MissingCount) - fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); + if (missingCount) + fatal(Twine(args.getArgString(missingIndex)) + ": missing argument"); - handleColorDiagnostics(Args); + handleColorDiagnostics(args); - for (auto *Arg : Args.filtered(OPT_UNKNOWN)) - warn("ignoring unknown argument: " + Arg->getSpelling()); + for (auto *arg : args.filtered(OPT_UNKNOWN)) { + std::string nearest; + if (table.findNearest(arg->getAsString(args), nearest) > 1) + warn("ignoring unknown argument '" + arg->getAsString(args) + "'"); + else + warn("ignoring unknown argument '" + arg->getAsString(args) + + "', did you mean '" + nearest + "'"); + } - if (Args.hasArg(OPT_lib)) + if (args.hasArg(OPT_lib)) warn("ignoring /lib since it's not the first argument"); - return Args; + return args; } // Tokenizes and parses a given string as command line in .drective section. // /EXPORT options are processed in fastpath. std::pair> -ArgParser::parseDirectives(StringRef S) { - std::vector Exports; - SmallVector Rest; +ArgParser::parseDirectives(StringRef s) { + std::vector exports; + SmallVector rest; - for (StringRef Tok : tokenize(S)) { - if (Tok.startswith_lower("/export:") || Tok.startswith_lower("-export:")) - Exports.push_back(Tok.substr(strlen("/export:"))); + for (StringRef tok : tokenize(s)) { + if (tok.startswith_lower("/export:") || tok.startswith_lower("-export:")) + exports.push_back(tok.substr(strlen("/export:"))); else - Rest.push_back(Tok.data()); + rest.push_back(tok.data()); } // Make InputArgList from unparsed string vectors. - unsigned MissingIndex; - unsigned MissingCount; + unsigned missingIndex; + unsigned missingCount; - opt::InputArgList Args = Table.ParseArgs(Rest, MissingIndex, MissingCount); + opt::InputArgList args = table.ParseArgs(rest, missingIndex, missingCount); - if (MissingCount) - fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); - for (auto *Arg : Args.filtered(OPT_UNKNOWN)) - warn("ignoring unknown argument: " + Arg->getSpelling()); - return {std::move(Args), std::move(Exports)}; + if (missingCount) + fatal(Twine(args.getArgString(missingIndex)) + ": missing argument"); + for (auto *arg : args.filtered(OPT_UNKNOWN)) + warn("ignoring unknown argument: " + arg->getAsString(args)); + return {std::move(args), std::move(exports)}; } // link.exe has an interesting feature. If LINK or _LINK_ environment // variables exist, their contents are handled as command line strings. // So you can pass extra arguments using them. -opt::InputArgList ArgParser::parseLINK(std::vector Argv) { +opt::InputArgList ArgParser::parseLINK(std::vector argv) { // Concatenate LINK env and command line arguments, and then parse them. - if (Optional S = Process::GetEnv("LINK")) { - std::vector V = tokenize(*S); - Argv.insert(std::next(Argv.begin()), V.begin(), V.end()); + if (Optional s = Process::GetEnv("LINK")) { + std::vector v = tokenize(*s); + argv.insert(std::next(argv.begin()), v.begin(), v.end()); } - if (Optional S = Process::GetEnv("_LINK_")) { - std::vector V = tokenize(*S); - Argv.insert(std::next(Argv.begin()), V.begin(), V.end()); + if (Optional s = Process::GetEnv("_LINK_")) { + std::vector v = tokenize(*s); + argv.insert(std::next(argv.begin()), v.begin(), v.end()); } - return parse(Argv); + return parse(argv); } -std::vector ArgParser::tokenize(StringRef S) { - SmallVector Tokens; - cl::TokenizeWindowsCommandLine(S, Saver, Tokens); - return std::vector(Tokens.begin(), Tokens.end()); +std::vector ArgParser::tokenize(StringRef s) { + SmallVector tokens; + cl::TokenizeWindowsCommandLine(s, saver, tokens); + return std::vector(tokens.begin(), tokens.end()); } -void printHelp(const char *Argv0) { +void printHelp(const char *argv0) { COFFOptTable().PrintHelp(outs(), - (std::string(Argv0) + " [options] file...").c_str(), + (std::string(argv0) + " [options] file...").c_str(), "LLVM Linker", false); } diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp index 34ea360..2b2818d 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 V); + void run(ArrayRef v); private: - void segregate(size_t Begin, size_t End, bool Constant); + void segregate(size_t begin, size_t end, bool constant); - bool assocEquals(const SectionChunk *A, const SectionChunk *B); + bool assocEquals(const SectionChunk *a, const SectionChunk *b); - bool equalsConstant(const SectionChunk *A, const SectionChunk *B); - bool equalsVariable(const SectionChunk *A, const SectionChunk *B); + bool equalsConstant(const SectionChunk *a, const SectionChunk *b); + bool equalsVariable(const SectionChunk *a, const SectionChunk *b); - uint32_t getHash(SectionChunk *C); - bool isEligible(SectionChunk *C); + bool isEligible(SectionChunk *c); - size_t findBoundary(size_t Begin, size_t End); + size_t findBoundary(size_t begin, size_t end); - void forEachClassRange(size_t Begin, size_t End, - std::function Fn); + void forEachClassRange(size_t begin, size_t end, + std::function fn); - void forEachClass(std::function Fn); + void forEachClass(std::function fn); - std::vector Chunks; - int Cnt = 0; - std::atomic Repeat = {false}; + std::vector chunks; + int cnt = 0; + std::atomic repeat = {false}; }; // Returns true if section S is subject of ICF. @@ -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 Classes; - for (const SectionChunk *C : SC->children()) - if (!C->SectionName.startswith(".debug") && - C->SectionName != ".gfids$y" && C->SectionName != ".gljmp$y") - Classes.push_back(C->Class[Cnt % 2]); - return Classes; +bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) { + auto childClasses = [&](const SectionChunk *sc) { + std::vector classes; + for (const SectionChunk &c : sc->children()) + if (!c.getSectionName().startswith(".debug") && + c.getSectionName() != ".gfids$y" && c.getSectionName() != ".gljmp$y") + classes.push_back(c.eqClass[cnt % 2]); + return classes; }; - return ChildClasses(A) == ChildClasses(B); + return childClasses(a) == childClasses(b); } // Compare "non-moving" part of two sections, namely everything // except relocation targets. -bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) { - if (A->Relocs.size() != B->Relocs.size()) +bool ICF::equalsConstant(const SectionChunk *a, const SectionChunk *b) { + if (a->relocsSize != b->relocsSize) return false; // Compare relocations. - auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) { - if (R1.Type != R2.Type || - R1.VirtualAddress != R2.VirtualAddress) { + auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) { + if (r1.Type != r2.Type || + r1.VirtualAddress != r2.VirtualAddress) { return false; } - Symbol *B1 = A->File->getSymbol(R1.SymbolTableIndex); - Symbol *B2 = B->File->getSymbol(R2.SymbolTableIndex); - if (B1 == B2) + Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex); + Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex); + if (b1 == b2) return true; - if (auto *D1 = dyn_cast(B1)) - if (auto *D2 = dyn_cast(B2)) - return D1->getValue() == D2->getValue() && - D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2]; + if (auto *d1 = dyn_cast(b1)) + if (auto *d2 = dyn_cast(b2)) + return d1->getValue() == d2->getValue() && + d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2]; return false; }; - if (!std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq)) + if (!std::equal(a->getRelocs().begin(), a->getRelocs().end(), + b->getRelocs().begin(), eq)) return false; // Compare section attributes and contents. - return A->getOutputCharacteristics() == B->getOutputCharacteristics() && - A->SectionName == B->SectionName && - A->Header->SizeOfRawData == B->Header->SizeOfRawData && - A->Checksum == B->Checksum && A->getContents() == B->getContents() && - assocEquals(A, B); + return a->getOutputCharacteristics() == b->getOutputCharacteristics() && + a->getSectionName() == b->getSectionName() && + a->header->SizeOfRawData == b->header->SizeOfRawData && + a->checksum == b->checksum && a->getContents() == b->getContents() && + assocEquals(a, b); } // Compare "moving" part of two sections, namely relocation targets. -bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) { +bool ICF::equalsVariable(const SectionChunk *a, const SectionChunk *b) { // Compare relocations. - auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) { - Symbol *B1 = A->File->getSymbol(R1.SymbolTableIndex); - Symbol *B2 = B->File->getSymbol(R2.SymbolTableIndex); - if (B1 == B2) + auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) { + Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex); + Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex); + if (b1 == b2) return true; - if (auto *D1 = dyn_cast(B1)) - if (auto *D2 = dyn_cast(B2)) - return D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2]; + if (auto *d1 = dyn_cast(b1)) + if (auto *d2 = dyn_cast(b2)) + return d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2]; return false; }; - return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), - Eq) && - assocEquals(A, B); + return std::equal(a->getRelocs().begin(), a->getRelocs().end(), + b->getRelocs().begin(), eq) && + assocEquals(a, b); } // Find the first Chunk after Begin that has a different class from Begin. -size_t ICF::findBoundary(size_t Begin, size_t End) { - for (size_t I = Begin + 1; I < End; ++I) - if (Chunks[Begin]->Class[Cnt % 2] != Chunks[I]->Class[Cnt % 2]) - return I; - return End; +size_t ICF::findBoundary(size_t begin, size_t end) { + for (size_t i = begin + 1; i < end; ++i) + if (chunks[begin]->eqClass[cnt % 2] != chunks[i]->eqClass[cnt % 2]) + return i; + return end; } -void ICF::forEachClassRange(size_t Begin, size_t End, - std::function Fn) { - while (Begin < End) { - size_t Mid = findBoundary(Begin, End); - Fn(Begin, Mid); - Begin = Mid; +void ICF::forEachClassRange(size_t begin, size_t end, + std::function fn) { + while (begin < end) { + size_t mid = findBoundary(begin, end); + fn(begin, mid); + begin = mid; } } // Call Fn on each class group. -void ICF::forEachClass(std::function Fn) { +void ICF::forEachClass(std::function fn) { // If the number of sections are too small to use threading, // call Fn sequentially. - if (Chunks.size() < 1024) { - forEachClassRange(0, Chunks.size(), Fn); - ++Cnt; + if (chunks.size() < 1024) { + forEachClassRange(0, chunks.size(), fn); + ++cnt; return; } @@ -222,95 +221,97 @@ void ICF::forEachClass(std::function 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 Vec) { - ScopedTimer T(ICFTimer); +void ICF::run(ArrayRef vec) { + ScopedTimer t(icfTimer); // Collect only mergeable sections and group by hash value. - uint32_t NextId = 1; - for (Chunk *C : Vec) { - if (auto *SC = dyn_cast(C)) { - if (isEligible(SC)) - Chunks.push_back(SC); + uint32_t nextId = 1; + for (Chunk *c : vec) { + if (auto *sc = dyn_cast(c)) { + if (isEligible(sc)) + chunks.push_back(sc); else - SC->Class[0] = NextId++; + sc->eqClass[0] = nextId++; } } // Make sure that ICF doesn't merge sections that are being handled by string // tail merging. - for (auto &P : MergeChunk::Instances) - for (SectionChunk *SC : P.second->Sections) - SC->Class[0] = NextId++; + for (MergeChunk *mc : MergeChunk::instances) + if (mc) + for (SectionChunk *sc : mc->sections) + sc->eqClass[0] = nextId++; // Initially, we use hash values to partition sections. - parallelForEach(Chunks, [&](SectionChunk *SC) { - SC->Class[1] = xxHash64(SC->getContents()); + parallelForEach(chunks, [&](SectionChunk *sc) { + sc->eqClass[0] = xxHash64(sc->getContents()); }); // Combine the hashes of the sections referenced by each section into its // hash. - parallelForEach(Chunks, [&](SectionChunk *SC) { - uint32_t Hash = SC->Class[1]; - for (Symbol *B : SC->symbols()) - if (auto *Sym = dyn_cast_or_null(B)) - Hash ^= Sym->getChunk()->Class[1]; - // Set MSB to 1 to avoid collisions with non-hash classs. - SC->Class[0] = Hash | (1U << 31); - }); + for (unsigned cnt = 0; cnt != 2; ++cnt) { + parallelForEach(chunks, [&](SectionChunk *sc) { + uint32_t hash = sc->eqClass[cnt % 2]; + for (Symbol *b : sc->symbols()) + if (auto *sym = dyn_cast_or_null(b)) + hash += sym->getChunk()->eqClass[cnt % 2]; + // Set MSB to 1 to avoid collisions with non-hash classs. + sc->eqClass[(cnt + 1) % 2] = hash | (1U << 31); + }); + } // From now on, sections in Chunks are ordered so that sections in // the same group are consecutive in the vector. - std::stable_sort(Chunks.begin(), Chunks.end(), - [](SectionChunk *A, SectionChunk *B) { - return A->Class[0] < B->Class[0]; - }); + llvm::stable_sort(chunks, [](const SectionChunk *a, const SectionChunk *b) { + return a->eqClass[0] < b->eqClass[0]; + }); // Compare static contents and assign unique IDs for each static content. - forEachClass([&](size_t Begin, size_t End) { segregate(Begin, End, true); }); + forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); }); // Split groups by comparing relocations until convergence is obtained. do { - Repeat = false; + repeat = false; forEachClass( - [&](size_t Begin, size_t End) { segregate(Begin, End, false); }); - } while (Repeat); + [&](size_t begin, size_t end) { segregate(begin, end, false); }); + } while (repeat); - log("ICF needed " + Twine(Cnt) + " iterations"); + log("ICF needed " + Twine(cnt) + " iterations"); // Merge sections in the same classs. - forEachClass([&](size_t Begin, size_t End) { - if (End - Begin == 1) + forEachClass([&](size_t begin, size_t end) { + if (end - begin == 1) return; - log("Selected " + Chunks[Begin]->getDebugName()); - for (size_t I = Begin + 1; I < End; ++I) { - log(" Removed " + Chunks[I]->getDebugName()); - Chunks[Begin]->replace(Chunks[I]); + log("Selected " + chunks[begin]->getDebugName()); + for (size_t i = begin + 1; i < end; ++i) { + log(" Removed " + chunks[i]->getDebugName()); + chunks[begin]->replace(chunks[i]); } }); } // Entry point to ICF. -void doICF(ArrayRef Chunks) { ICF().run(Chunks); } +void doICF(ArrayRef chunks) { ICF().run(chunks); } } // namespace coff } // namespace lld diff --git a/COFF/ICF.h b/COFF/ICF.h index 9c54e0c..0b3c8fa 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 Chunks); +void doICF(ArrayRef chunks); } // namespace coff } // namespace lld diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp index 236c90e..c00d5c5 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::Instances; -std::vector ImportFile::Instances; -std::vector BitcodeFile::Instances; +std::vector ObjFile::instances; +std::vector ImportFile::instances; +std::vector BitcodeFile::instances; /// Checks that Source is compatible with being a weak alias to Target. /// If Source is Undefined and has no weak alias set, makes it a weak /// alias to Target. -static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F, - Symbol *Source, Symbol *Target) { - if (auto *U = dyn_cast(Source)) { - if (U->WeakAlias && U->WeakAlias != Target) { +static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f, + Symbol *source, Symbol *target) { + if (auto *u = dyn_cast(source)) { + if (u->weakAlias && u->weakAlias != target) { // Weak aliases as produced by GCC are named in the form // .weak.., where is the name // of another symbol emitted near the weak symbol. // Just use the definition from the first object file that defined // this weak symbol. - if (Config->MinGW) + if (config->mingw) return; - Symtab->reportDuplicate(Source, F); + symtab->reportDuplicate(source, f); } - U->WeakAlias = Target; + u->weakAlias = target; } } -ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {} +ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {} void ArchiveFile::parse() { // Parse a MemoryBufferRef as an archive file. - File = CHECK(Archive::create(MB), this); + file = CHECK(Archive::create(mb), this); // Read the symbol table to construct Lazy objects. - for (const Archive::Symbol &Sym : File->symbols()) - Symtab->addLazy(this, Sym); + for (const Archive::Symbol &sym : file->symbols()) + symtab->addLazy(this, sym); } // Returns a buffer pointing to a member file containing a given symbol. -void ArchiveFile::addMember(const Archive::Symbol *Sym) { - const Archive::Child &C = - CHECK(Sym->getMember(), - "could not get the member for symbol " + Sym->getName()); +void ArchiveFile::addMember(const Archive::Symbol *sym) { + const Archive::Child &c = + CHECK(sym->getMember(), + "could not get the member for symbol " + sym->getName()); // Return an empty buffer if we have already returned the same buffer. - if (!Seen.insert(C.getChildOffset()).second) + if (!seen.insert(c.getChildOffset()).second) return; - Driver->enqueueArchiveMember(C, Sym->getName(), getName()); + driver->enqueueArchiveMember(c, sym->getName(), getName()); } -std::vector getArchiveMembers(Archive *File) { - std::vector V; - Error Err = Error::success(); - for (const ErrorOr &COrErr : File->children(Err)) { - Archive::Child C = - CHECK(COrErr, - File->getFileName() + ": could not get the child of the archive"); - MemoryBufferRef MBRef = - CHECK(C.getMemoryBufferRef(), - File->getFileName() + +std::vector getArchiveMembers(Archive *file) { + std::vector v; + Error err = Error::success(); + for (const ErrorOr &cOrErr : file->children(err)) { + Archive::Child c = + CHECK(cOrErr, + file->getFileName() + ": could not get the child of the archive"); + MemoryBufferRef mbref = + CHECK(c.getMemoryBufferRef(), + file->getFileName() + ": could not get the buffer for a child of the archive"); - V.push_back(MBRef); + v.push_back(mbref); } - if (Err) - fatal(File->getFileName() + - ": Archive::children failed: " + toString(std::move(Err))); - return V; + if (err) + fatal(file->getFileName() + + ": Archive::children failed: " + toString(std::move(err))); + return v; } void ObjFile::parse() { // Parse a memory buffer as a COFF file. - std::unique_ptr Bin = CHECK(createBinary(MB), this); + std::unique_ptr bin = CHECK(createBinary(mb), this); - if (auto *Obj = dyn_cast(Bin.get())) { - Bin.release(); - COFFObj.reset(Obj); + if (auto *obj = dyn_cast(bin.get())) { + bin.release(); + coffObj.reset(obj); } else { fatal(toString(this) + " is not a COFF file"); } @@ -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(1); +static SectionChunk *const pendingComdat = reinterpret_cast(1); void ObjFile::initializeChunks() { - uint32_t NumSections = COFFObj->getNumberOfSections(); - Chunks.reserve(NumSections); - SparseChunks.resize(NumSections + 1); - for (uint32_t I = 1; I < NumSections + 1; ++I) { - const coff_section *Sec; - if (auto EC = COFFObj->getSection(I, Sec)) - fatal("getSection failed: #" + Twine(I) + ": " + EC.message()); - - if (Sec->Characteristics & IMAGE_SCN_LNK_COMDAT) - SparseChunks[I] = PendingComdat; + uint32_t numSections = coffObj->getNumberOfSections(); + chunks.reserve(numSections); + sparseChunks.resize(numSections + 1); + for (uint32_t i = 1; i < numSections + 1; ++i) { + const coff_section *sec = getSection(i); + if (sec->Characteristics & IMAGE_SCN_LNK_COMDAT) + sparseChunks[i] = pendingComdat; else - SparseChunks[I] = readSection(I, nullptr, ""); + sparseChunks[i] = readSection(i, nullptr, ""); } } -SectionChunk *ObjFile::readSection(uint32_t SectionNumber, - const coff_aux_section_definition *Def, - StringRef LeaderName) { - const coff_section *Sec; - if (auto EC = COFFObj->getSection(SectionNumber, Sec)) - fatal("getSection failed: #" + Twine(SectionNumber) + ": " + EC.message()); - - StringRef Name; - if (auto EC = COFFObj->getSectionName(Sec, Name)) - fatal("getSectionName failed: #" + Twine(SectionNumber) + ": " + - EC.message()); - - if (Name == ".drectve") { - ArrayRef 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 e = coffObj->getSectionName(sec)) + name = *e; + else + fatal("getSectionName failed: #" + Twine(sectionNumber) + ": " + + toString(e.takeError())); + + if (name == ".drectve") { + ArrayRef data; + cantFail(coffObj->getSectionContents(sec, data)); + directives = StringRef((const char *)data.data(), data.size()); return nullptr; } - if (Name == ".llvm_addrsig") { - AddrsigSec = Sec; + if (name == ".llvm_addrsig") { + addrsigSec = sec; return nullptr; } @@ -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(this, Sec); - if (Def) - C->Checksum = Def->CheckSum; + auto *c = make(this, sec); + if (def) + c->checksum = def->CheckSum; + + // link.exe uses the presence of .rsrc$01 for LNK4078, so match that. + if (name == ".rsrc$01") + isResourceObjFile = true; // CodeView sections are stored to a different vector because they are not // linked in the regular manner. - if (C->isCodeView()) - DebugChunks.push_back(C); - else if (Config->GuardCF != GuardCFLevel::Off && Name == ".gfids$y") - GuardFidChunks.push_back(C); - else if (Config->GuardCF != GuardCFLevel::Off && Name == ".gljmp$y") - GuardLJmpChunks.push_back(C); - else if (Name == ".sxdata") - SXDataChunks.push_back(C); - else if (Config->TailMerge && Sec->NumberOfRelocations == 0 && - Name == ".rdata" && LeaderName.startswith("??_C@")) + if (c->isCodeView()) + debugChunks.push_back(c); + else if (name == ".gfids$y") + guardFidChunks.push_back(c); + else if (name == ".gljmp$y") + guardLJmpChunks.push_back(c); + else if (name == ".sxdata") + sXDataChunks.push_back(c); + else if (config->tailMerge && sec->NumberOfRelocations == 0 && + name == ".rdata" && leaderName.startswith("??_C@")) // COFF sections that look like string literal sections (i.e. no // relocations, in .rdata, leader symbol name matches the MSVC name mangling // for string literals) are subject to string tail merging. - MergeChunk::addSection(C); + MergeChunk::addSection(c); else - Chunks.push_back(C); + chunks.push_back(c); - return C; + return c; } void ObjFile::readAssociativeDefinition( - COFFSymbolRef Sym, const coff_aux_section_definition *Def) { - readAssociativeDefinition(Sym, Def, Def->getNumber(Sym.isBigObj())); + COFFSymbolRef sym, const coff_aux_section_definition *def) { + readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj())); } -void ObjFile::readAssociativeDefinition(COFFSymbolRef Sym, - const coff_aux_section_definition *Def, - uint32_t ParentSection) { - SectionChunk *Parent = SparseChunks[ParentSection]; +void ObjFile::readAssociativeDefinition(COFFSymbolRef sym, + const coff_aux_section_definition *def, + uint32_t parentIndex) { + SectionChunk *parent = sparseChunks[parentIndex]; + int32_t sectionNumber = sym.getSectionNumber(); + + auto diag = [&]() { + StringRef name, parentName; + coffObj->getSymbolName(sym, name); + + const coff_section *parentSec = getSection(parentIndex); + if (Expected e = coffObj->getSectionName(parentSec)) + parentName = *e; + error(toString(this) + ": associative comdat " + name + " (sec " + + Twine(sectionNumber) + ") has invalid reference to section " + + parentName + " (sec " + Twine(parentIndex) + ")"); + }; - // If 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 &PrevailingSectionMap) { + COFFSymbolRef sym, DenseMap &prevailingSectionMap) { // For comdat symbols in executable sections, where this is the copy // of the section chunk we actually include instead of discarding it, // add the symbol to a map to allow using it for implicitly // associating .[px]data$ sections to it. - int32_t SectionNumber = Sym.getSectionNumber(); - SectionChunk *SC = SparseChunks[SectionNumber]; - if (SC && SC->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) { - StringRef Name; - COFFObj->getSymbolName(Sym, Name); - PrevailingSectionMap[Name] = SectionNumber; + int32_t sectionNumber = sym.getSectionNumber(); + SectionChunk *sc = sparseChunks[sectionNumber]; + if (sc && sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) { + StringRef name; + coffObj->getSymbolName(sym, name); + if (getMachineType() == I386) + name.consume_front("_"); + prevailingSectionMap[name] = sectionNumber; } } void ObjFile::maybeAssociateSEHForMingw( - COFFSymbolRef Sym, const coff_aux_section_definition *Def, - const DenseMap &PrevailingSectionMap) { - StringRef Name; - COFFObj->getSymbolName(Sym, Name); - if (Name.consume_front(".pdata$") || Name.consume_front(".xdata$")) { - // For MinGW, treat .[px]data$ as implicitly associative to - // the symbol . - auto ParentSym = PrevailingSectionMap.find(Name); - if (ParentSym != PrevailingSectionMap.end()) - readAssociativeDefinition(Sym, Def, ParentSym->second); + COFFSymbolRef sym, const coff_aux_section_definition *def, + const DenseMap &prevailingSectionMap) { + StringRef name; + coffObj->getSymbolName(sym, name); + if (name.consume_front(".pdata$") || name.consume_front(".xdata$") || + name.consume_front(".eh_frame$")) { + // For MinGW, treat .[px]data$ and .eh_frame$ as implicitly + // associative to the symbol . + auto parentSym = prevailingSectionMap.find(name); + if (parentSym != prevailingSectionMap.end()) + readAssociativeDefinition(sym, def, parentSym->second); } } -Symbol *ObjFile::createRegular(COFFSymbolRef Sym) { - SectionChunk *SC = SparseChunks[Sym.getSectionNumber()]; - if (Sym.isExternal()) { - StringRef Name; - COFFObj->getSymbolName(Sym, Name); - if (SC) - return Symtab->addRegular(this, Name, Sym.getGeneric(), SC); +Symbol *ObjFile::createRegular(COFFSymbolRef sym) { + SectionChunk *sc = sparseChunks[sym.getSectionNumber()]; + if (sym.isExternal()) { + StringRef name; + coffObj->getSymbolName(sym, name); + if (sc) + return symtab->addRegular(this, name, sym.getGeneric(), sc); // For MinGW symbols named .weak.* that point to a discarded section, // don't create an Undefined symbol. If nothing ever refers to the symbol, // everything should be fine. If something actually refers to the symbol // (e.g. the undefined weak alias), linking will fail due to undefined // references at the end. - if (Config->MinGW && Name.startswith(".weak.")) + if (config->mingw && name.startswith(".weak.")) return nullptr; - return Symtab->addUndefined(Name, this, false); + return symtab->addUndefined(name, this, false); } - if (SC) + if (sc) return make(this, /*Name*/ "", /*IsCOMDAT*/ false, - /*IsExternal*/ false, Sym.getGeneric(), SC); + /*IsExternal*/ false, sym.getGeneric(), sc); return nullptr; } void ObjFile::initializeSymbols() { - uint32_t NumSymbols = COFFObj->getNumberOfSymbols(); - Symbols.resize(NumSymbols); - - SmallVector, 8> WeakAliases; - std::vector PendingIndexes; - PendingIndexes.reserve(NumSymbols); - - DenseMap PrevailingSectionMap; - std::vector ComdatDefs( - COFFObj->getNumberOfSections() + 1); - - for (uint32_t I = 0; I < NumSymbols; ++I) { - COFFSymbolRef COFFSym = check(COFFObj->getSymbol(I)); - bool PrevailingComdat; - if (COFFSym.isUndefined()) { - Symbols[I] = createUndefined(COFFSym); - } else if (COFFSym.isWeakExternal()) { - Symbols[I] = createUndefined(COFFSym); - uint32_t TagIndex = COFFSym.getAux()->TagIndex; - WeakAliases.emplace_back(Symbols[I], TagIndex); - } else if (Optional OptSym = - createDefined(COFFSym, ComdatDefs, PrevailingComdat)) { - Symbols[I] = *OptSym; - if (Config->MinGW && PrevailingComdat) - recordPrevailingSymbolForMingw(COFFSym, PrevailingSectionMap); + uint32_t numSymbols = coffObj->getNumberOfSymbols(); + symbols.resize(numSymbols); + + SmallVector, 8> weakAliases; + std::vector pendingIndexes; + pendingIndexes.reserve(numSymbols); + + DenseMap prevailingSectionMap; + std::vector comdatDefs( + coffObj->getNumberOfSections() + 1); + + for (uint32_t i = 0; i < numSymbols; ++i) { + COFFSymbolRef coffSym = check(coffObj->getSymbol(i)); + bool prevailingComdat; + if (coffSym.isUndefined()) { + symbols[i] = createUndefined(coffSym); + } else if (coffSym.isWeakExternal()) { + symbols[i] = createUndefined(coffSym); + uint32_t tagIndex = coffSym.getAux()->TagIndex; + weakAliases.emplace_back(symbols[i], tagIndex); + } else if (Optional optSym = + createDefined(coffSym, comdatDefs, prevailingComdat)) { + symbols[i] = *optSym; + if (config->mingw && prevailingComdat) + recordPrevailingSymbolForMingw(coffSym, prevailingSectionMap); } else { // createDefined() returns None if a symbol belongs to a section that // was pending at the point when the symbol was read. This can happen in // two cases: // 1) section definition symbol for a comdat leader; - // 2) symbol belongs to a comdat section associated with a section whose - // section definition symbol appears later in the symbol table. + // 2) symbol belongs to a comdat section associated with another section. // In both of these cases, we can expect the section to be resolved by // the time we finish visiting the remaining symbols in the symbol // table. So we postpone the handling of this symbol until that time. - PendingIndexes.push_back(I); + pendingIndexes.push_back(i); } - I += COFFSym.getNumberOfAuxSymbols(); + i += coffSym.getNumberOfAuxSymbols(); } - for (uint32_t I : PendingIndexes) { - COFFSymbolRef Sym = check(COFFObj->getSymbol(I)); - if (const coff_aux_section_definition *Def = Sym.getSectionDefinition()) { - if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) - readAssociativeDefinition(Sym, Def); - else if (Config->MinGW) - maybeAssociateSEHForMingw(Sym, Def, PrevailingSectionMap); + for (uint32_t i : pendingIndexes) { + COFFSymbolRef sym = check(coffObj->getSymbol(i)); + if (const coff_aux_section_definition *def = sym.getSectionDefinition()) { + if (def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) + readAssociativeDefinition(sym, def); + else if (config->mingw) + maybeAssociateSEHForMingw(sym, def, prevailingSectionMap); } - if (SparseChunks[Sym.getSectionNumber()] == PendingComdat) { - StringRef Name; - COFFObj->getSymbolName(Sym, Name); - log("comdat section " + Name + + if (sparseChunks[sym.getSectionNumber()] == pendingComdat) { + StringRef name; + coffObj->getSymbolName(sym, name); + log("comdat section " + name + " without leader and unassociated, discarding"); continue; } - Symbols[I] = createRegular(Sym); + symbols[i] = createRegular(sym); } - for (auto &KV : WeakAliases) { - Symbol *Sym = KV.first; - uint32_t Idx = KV.second; - checkAndSetWeakAlias(Symtab, this, Sym, Symbols[Idx]); + for (auto &kv : weakAliases) { + Symbol *sym = kv.first; + uint32_t idx = kv.second; + checkAndSetWeakAlias(symtab, this, sym, symbols[idx]); } } -Symbol *ObjFile::createUndefined(COFFSymbolRef Sym) { - StringRef Name; - COFFObj->getSymbolName(Sym, Name); - return Symtab->addUndefined(Name, this, Sym.isWeakExternal()); +Symbol *ObjFile::createUndefined(COFFSymbolRef sym) { + StringRef name; + coffObj->getSymbolName(sym, name); + return symtab->addUndefined(name, this, sym.isWeakExternal()); +} + +void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection, + bool &prevailing, DefinedRegular *leader) { + if (prevailing) + return; + // There's already an existing comdat for this symbol: `Leader`. + // Use the comdats's selection field to determine if the new + // symbol in `Sym` should be discarded, produce a duplicate symbol + // error, etc. + + SectionChunk *leaderChunk = nullptr; + COMDATType leaderSelection = IMAGE_COMDAT_SELECT_ANY; + + if (leader->data) { + leaderChunk = leader->getChunk(); + leaderSelection = leaderChunk->selection; + } else { + // FIXME: comdats from LTO files don't know their selection; treat them + // as "any". + selection = leaderSelection; + } + + if ((selection == IMAGE_COMDAT_SELECT_ANY && + leaderSelection == IMAGE_COMDAT_SELECT_LARGEST) || + (selection == IMAGE_COMDAT_SELECT_LARGEST && + leaderSelection == IMAGE_COMDAT_SELECT_ANY)) { + // cl.exe picks "any" for vftables when building with /GR- and + // "largest" when building with /GR. To be able to link object files + // compiled with each flag, "any" and "largest" are merged as "largest". + leaderSelection = selection = IMAGE_COMDAT_SELECT_LARGEST; + } + + // Other than that, comdat selections must match. This is a bit more + // strict than link.exe which allows merging "any" and "largest" if "any" + // is the first symbol the linker sees, and it allows merging "largest" + // with everything (!) if "largest" is the first symbol the linker sees. + // Making this symmetric independent of which selection is seen first + // seems better though. + // (This behavior matches ModuleLinker::getComdatResult().) + if (selection != leaderSelection) { + log(("conflicting comdat type for " + toString(*leader) + ": " + + Twine((int)leaderSelection) + " in " + toString(leader->getFile()) + + " and " + Twine((int)selection) + " in " + toString(this)) + .str()); + symtab->reportDuplicate(leader, this); + return; + } + + switch (selection) { + case IMAGE_COMDAT_SELECT_NODUPLICATES: + symtab->reportDuplicate(leader, this); + break; + + case IMAGE_COMDAT_SELECT_ANY: + // Nothing to do. + break; + + case IMAGE_COMDAT_SELECT_SAME_SIZE: + if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) + symtab->reportDuplicate(leader, this); + break; + + case IMAGE_COMDAT_SELECT_EXACT_MATCH: { + SectionChunk newChunk(this, getSection(sym)); + // link.exe only compares section contents here and doesn't complain + // if the two comdat sections have e.g. different alignment. + // Match that. + if (leaderChunk->getContents() != newChunk.getContents()) + symtab->reportDuplicate(leader, this); + break; + } + + case IMAGE_COMDAT_SELECT_ASSOCIATIVE: + // createDefined() is never called for IMAGE_COMDAT_SELECT_ASSOCIATIVE. + // (This means lld-link doesn't produce duplicate symbol errors for + // associative comdats while link.exe does, but associate comdats + // are never extern in practice.) + llvm_unreachable("createDefined not called for associative comdats"); + + case IMAGE_COMDAT_SELECT_LARGEST: + if (leaderChunk->getSize() < getSection(sym)->SizeOfRawData) { + // Replace the existing comdat symbol with the new one. + StringRef name; + coffObj->getSymbolName(sym, name); + // FIXME: This is incorrect: With /opt:noref, the previous sections + // make it into the final executable as well. Correct handling would + // be to undo reading of the whole old section that's being replaced, + // or doing one pass that determines what the final largest comdat + // is for all IMAGE_COMDAT_SELECT_LARGEST comdats and then reading + // only the largest one. + replaceSymbol(leader, this, name, /*IsCOMDAT*/ true, + /*IsExternal*/ true, sym.getGeneric(), + nullptr); + prevailing = true; + } + break; + + case IMAGE_COMDAT_SELECT_NEWEST: + llvm_unreachable("should have been rejected earlier"); + } } Optional ObjFile::createDefined( - COFFSymbolRef Sym, - std::vector &ComdatDefs, - bool &Prevailing) { - Prevailing = false; - auto GetName = [&]() { - StringRef S; - COFFObj->getSymbolName(Sym, S); - return S; + COFFSymbolRef sym, + std::vector &comdatDefs, + bool &prevailing) { + prevailing = false; + auto getName = [&]() { + StringRef s; + coffObj->getSymbolName(sym, s); + return s; }; - if (Sym.isCommon()) { - auto *C = make(Sym); - Chunks.push_back(C); - return Symtab->addCommon(this, GetName(), Sym.getValue(), Sym.getGeneric(), - C); + if (sym.isCommon()) { + auto *c = make(sym); + chunks.push_back(c); + return symtab->addCommon(this, getName(), sym.getValue(), sym.getGeneric(), + c); } - if (Sym.isAbsolute()) { - StringRef Name = GetName(); + if (sym.isAbsolute()) { + StringRef name = getName(); // Skip special symbols. - if (Name == "@comp.id") + if (name == "@comp.id") return nullptr; - if (Name == "@feat.00") { - Feat00Flags = Sym.getValue(); + if (name == "@feat.00") { + feat00Flags = sym.getValue(); return nullptr; } - if (Sym.isExternal()) - return Symtab->addAbsolute(Name, Sym); - return make(Name, Sym); + if (sym.isExternal()) + return symtab->addAbsolute(name, sym); + return make(name, sym); } - int32_t SectionNumber = Sym.getSectionNumber(); - if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG) + int32_t sectionNumber = sym.getSectionNumber(); + if (sectionNumber == llvm::COFF::IMAGE_SYM_DEBUG) return nullptr; - if (llvm::COFF::isReservedSectionNumber(SectionNumber)) - fatal(toString(this) + ": " + GetName() + - " should not refer to special section " + Twine(SectionNumber)); - - if ((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(this, /*Name*/ "", /*IsCOMDAT*/ false, - /*IsExternal*/ false, Sym.getGeneric()); - Prevailing = true; + leader = make(this, /*Name*/ "", /*IsCOMDAT*/ false, + /*IsExternal*/ false, sym.getGeneric()); + prevailing = true; } - if (Prevailing) { - SectionChunk *C = readSection(SectionNumber, Def, GetName()); - SparseChunks[SectionNumber] = C; - C->Sym = cast(Leader); - cast(Leader)->Data = &C->Repl; - } 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(leader); + c->selection = selection; + cast(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(COFFObj->getMachine()); + if (coffObj) + return static_cast(coffObj->getMachine()); return IMAGE_FILE_MACHINE_UNKNOWN; } -StringRef ltrim1(StringRef S, const char *Chars) { - if (!S.empty() && strchr(Chars, S[0])) - return S.substr(1); - return S; +ArrayRef ObjFile::getDebugSection(StringRef secName) { + if (SectionChunk *sec = SectionChunk::findByName(debugChunks, secName)) + return sec->consumeDebugMagic(); + return {}; +} + +// OBJ files systematically store critical informations in a .debug$S stream, +// even if the TU was compiled with no debug info. At least two records are +// always there. S_OBJNAME stores a 32-bit signature, which is loaded into the +// PCHSignature member. S_COMPILE3 stores compile-time cmd-line flags. This is +// currently used to initialize the hotPatchable member. +void ObjFile::initializeFlags() { + ArrayRef data = getDebugSection(".debug$S"); + if (data.empty()) + return; + + DebugSubsectionArray subsections; + + BinaryStreamReader reader(data, support::little); + ExitOnError exitOnErr; + exitOnErr(reader.readArray(subsections, data.size())); + + for (const DebugSubsectionRecord &ss : subsections) { + if (ss.kind() != DebugSubsectionKind::Symbols) + continue; + + unsigned offset = 0; + + // Only parse the first two records. We are only looking for S_OBJNAME + // and S_COMPILE3, and they usually appear at the beginning of the + // stream. + for (unsigned i = 0; i < 2; ++i) { + Expected sym = readSymbolFromStream(ss.getRecordData(), offset); + if (!sym) { + consumeError(sym.takeError()); + return; + } + if (sym->kind() == SymbolKind::S_COMPILE3) { + auto cs = + cantFail(SymbolDeserializer::deserializeAs(sym.get())); + hotPatchable = + (cs.Flags & CompileSym3Flags::HotPatch) != CompileSym3Flags::None; + } + if (sym->kind() == SymbolKind::S_OBJNAME) { + auto objName = cantFail(SymbolDeserializer::deserializeAs( + sym.get())); + pchSignature = objName.Signature; + } + offset += sym->length(); + } + } +} + +// Depending on the compilation flags, OBJs can refer to external files, +// necessary to merge this OBJ into the final PDB. We currently support two +// types of external files: Precomp/PCH OBJs, when compiling with /Yc and /Yu. +// And PDB type servers, when compiling with /Zi. This function extracts these +// dependencies and makes them available as a TpiSource interface (see +// DebugTypes.h). Both cases only happen with cl.exe: clang-cl produces regular +// output even with /Yc and /Yu and with /Zi. +void ObjFile::initializeDependencies() { + if (!config->debug) + return; + + bool isPCH = false; + + ArrayRef data = getDebugSection(".debug$P"); + if (!data.empty()) + isPCH = true; + else + data = getDebugSection(".debug$T"); + + if (data.empty()) + return; + + CVTypeArray types; + BinaryStreamReader reader(data, support::little); + cantFail(reader.readArray(types, reader.getLength())); + + CVTypeArray::Iterator firstType = types.begin(); + if (firstType == types.end()) + return; + + debugTypes.emplace(types); + + if (isPCH) { + debugTypesObj = makePrecompSource(this); + return; + } + + if (firstType->kind() == LF_TYPESERVER2) { + TypeServer2Record ts = cantFail( + TypeDeserializer::deserializeAs(firstType->data())); + debugTypesObj = makeUseTypeServerSource(this, &ts); + return; + } + + if (firstType->kind() == LF_PRECOMP) { + PrecompRecord precomp = cantFail( + TypeDeserializer::deserializeAs(firstType->data())); + debugTypesObj = makeUsePrecompSource(this, &precomp); + return; + } + + debugTypesObj = makeTpiSource(this); +} + +StringRef ltrim1(StringRef s, const char *chars) { + if (!s.empty() && strchr(chars, s[0])) + return s.substr(1); + return s; } void ImportFile::parse() { - const char *Buf = MB.getBufferStart(); - const char *End = MB.getBufferEnd(); - const auto *Hdr = reinterpret_cast(Buf); + const char *buf = mb.getBufferStart(); + const auto *hdr = reinterpret_cast(buf); // Check if the total size is valid. - if ((size_t)(End - Buf) != (sizeof(*Hdr) + Hdr->SizeOfData)) + if (mb.getBufferSize() != sizeof(*hdr) + hdr->SizeOfData) fatal("broken import library"); // Read names and create an __imp_ symbol. - StringRef Name = Saver.save(StringRef(Buf + sizeof(*Hdr))); - StringRef ImpName = Saver.save("__imp_" + Name); - const char *NameStart = Buf + sizeof(coff_import_header) + Name.size() + 1; - DLLName = StringRef(NameStart); - StringRef ExtName; - switch (Hdr->getNameType()) { + StringRef name = saver.save(StringRef(buf + sizeof(*hdr))); + StringRef impName = saver.save("__imp_" + name); + const char *nameStart = buf + sizeof(coff_import_header) + name.size() + 1; + dllName = StringRef(nameStart); + StringRef extName; + switch (hdr->getNameType()) { case IMPORT_ORDINAL: - ExtName = ""; + extName = ""; break; case IMPORT_NAME: - ExtName = Name; + extName = name; break; case IMPORT_NAME_NOPREFIX: - ExtName = ltrim1(Name, "?@_"); + extName = ltrim1(name, "?@_"); break; case IMPORT_NAME_UNDECORATE: - ExtName = ltrim1(Name, "?@_"); - ExtName = ExtName.substr(0, ExtName.find('@')); + extName = ltrim1(name, "?@_"); + extName = extName.substr(0, extName.find('@')); break; } - this->Hdr = Hdr; - ExternalName = ExtName; + this->hdr = hdr; + externalName = extName; - ImpSym = Symtab->addImportData(ImpName, this); + impSym = symtab->addImportData(impName, this); // If this was a duplicate, we logged an error but may continue; - // in this case, ImpSym is nullptr. - if (!ImpSym) + // in this case, impSym is nullptr. + if (!impSym) return; - if (Hdr->getType() == llvm::COFF::IMPORT_CONST) - static_cast(Symtab->addImportData(Name, this)); + if (hdr->getType() == llvm::COFF::IMPORT_CONST) + static_cast(symtab->addImportData(name, this)); // If type is function, we need to create a thunk which jump to an // address pointed by the __imp_ symbol. (This allows you to call // DLL functions just like regular non-DLL functions.) - if (Hdr->getType() == llvm::COFF::IMPORT_CODE) - ThunkSym = Symtab->addImportThunk( - Name, cast_or_null(ImpSym), Hdr->Machine); + if (hdr->getType() == llvm::COFF::IMPORT_CODE) + thunkSym = symtab->addImportThunk( + name, cast_or_null(impSym), hdr->Machine); +} + +BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive) + : InputFile(BitcodeKind, mb) { + std::string path = mb.getBufferIdentifier().str(); + if (config->thinLTOIndexOnly) + path = replaceThinLTOSuffix(mb.getBufferIdentifier()); + + // ThinLTO assumes that all MemoryBufferRefs given to it have a unique + // name. If two archives define two members with the same name, this + // causes a collision which result in only one of the objects being taken + // into consideration at LTO time (which very likely causes undefined + // symbols later in the link stage). So we append file offset to make + // filename unique. + MemoryBufferRef mbref( + mb.getBuffer(), + saver.save(archiveName + path + + (archiveName.empty() ? "" : utostr(offsetInArchive)))); + + obj = check(lto::InputFile::create(mbref)); } void BitcodeFile::parse() { - Obj = check(lto::InputFile::create(MemoryBufferRef( - MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier())))); - std::vector> Comdat(Obj->getComdatTable().size()); - for (size_t I = 0; I != Obj->getComdatTable().size(); ++I) - Comdat[I] = Symtab->addComdat(this, Saver.save(Obj->getComdatTable()[I])); - for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) { - StringRef SymName = Saver.save(ObjSym.getName()); - int ComdatIndex = ObjSym.getComdatIndex(); - Symbol *Sym; - if (ObjSym.isUndefined()) { - Sym = Symtab->addUndefined(SymName, this, false); - } else if (ObjSym.isCommon()) { - Sym = Symtab->addCommon(this, SymName, ObjSym.getCommonSize()); - } else if (ObjSym.isWeak() && ObjSym.isIndirect()) { + std::vector> comdat(obj->getComdatTable().size()); + for (size_t i = 0; i != obj->getComdatTable().size(); ++i) + // FIXME: lto::InputFile doesn't keep enough data to do correct comdat + // selection handling. + comdat[i] = symtab->addComdat(this, saver.save(obj->getComdatTable()[i])); + for (const lto::InputFile::Symbol &objSym : obj->symbols()) { + StringRef symName = saver.save(objSym.getName()); + int comdatIndex = objSym.getComdatIndex(); + Symbol *sym; + if (objSym.isUndefined()) { + sym = symtab->addUndefined(symName, this, false); + } else if (objSym.isCommon()) { + sym = symtab->addCommon(this, symName, objSym.getCommonSize()); + } else if (objSym.isWeak() && objSym.isIndirect()) { // Weak external. - Sym = Symtab->addUndefined(SymName, this, true); - std::string Fallback = ObjSym.getCOFFWeakExternalFallback(); - Symbol *Alias = Symtab->addUndefined(Saver.save(Fallback)); - checkAndSetWeakAlias(Symtab, this, Sym, Alias); - } else if (ComdatIndex != -1) { - if (SymName == Obj->getComdatTable()[ComdatIndex]) - Sym = Comdat[ComdatIndex].first; - else if (Comdat[ComdatIndex].second) - Sym = Symtab->addRegular(this, SymName); + sym = symtab->addUndefined(symName, this, true); + std::string fallback = objSym.getCOFFWeakExternalFallback(); + Symbol *alias = symtab->addUndefined(saver.save(fallback)); + checkAndSetWeakAlias(symtab, this, sym, alias); + } else if (comdatIndex != -1) { + if (symName == obj->getComdatTable()[comdatIndex]) + sym = comdat[comdatIndex].first; + else if (comdat[comdatIndex].second) + sym = symtab->addRegular(this, symName); else - Sym = Symtab->addUndefined(SymName, this, false); + sym = symtab->addUndefined(symName, this, false); } else { - Sym = Symtab->addRegular(this, SymName); + sym = symtab->addRegular(this, symName); } - Symbols.push_back(Sym); + symbols.push_back(sym); + if (objSym.isUsed()) + config->gcroot.push_back(sym); } - Directives = Obj->getCOFFLinkerOpts(); + directives = obj->getCOFFLinkerOpts(); } MachineTypes BitcodeFile::getMachineType() { - switch (Triple(Obj->getTargetTriple()).getArch()) { + switch (Triple(obj->getTargetTriple()).getArch()) { case Triple::x86_64: return AMD64; case Triple::x86: @@ -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 ""; - 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 ec802f2..dfad981 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 getArchiveMembers(llvm::object::Archive *File); +std::vector 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 File; - std::string Filename; - llvm::DenseSet Seen; + std::unique_ptr file; + llvm::DenseSet seen; }; // .obj or .o file. This may be a member of an archive file. class ObjFile : public InputFile { public: - explicit ObjFile(MemoryBufferRef M) : InputFile(ObjectKind, M) {} - static bool classof(const InputFile *F) { return F->kind() == ObjectKind; } + explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {} + static bool classof(const InputFile *f) { return f->kind() == ObjectKind; } void parse() override; MachineTypes getMachineType() override; - ArrayRef getChunks() { return Chunks; } - ArrayRef getDebugChunks() { return DebugChunks; } - ArrayRef getSXDataChunks() { return SXDataChunks; } - ArrayRef getGuardFidChunks() { return GuardFidChunks; } - ArrayRef getGuardLJmpChunks() { return GuardLJmpChunks; } - ArrayRef getSymbols() { return Symbols; } - - // Returns a Symbol object for the SymbolIndex'th symbol in the + ArrayRef getChunks() { return chunks; } + ArrayRef getDebugChunks() { return debugChunks; } + ArrayRef getSXDataChunks() { return sXDataChunks; } + ArrayRef getGuardFidChunks() { return guardFidChunks; } + ArrayRef getGuardLJmpChunks() { return guardLJmpChunks; } + ArrayRef getSymbols() { return symbols; } + + ArrayRef getDebugSection(StringRef secName); + + // Returns a Symbol object for the symbolIndex'th symbol in the // underlying object file. - Symbol *getSymbol(uint32_t SymbolIndex) { - return Symbols[SymbolIndex]; + Symbol *getSymbol(uint32_t symbolIndex) { + return symbols[symbolIndex]; } // Returns the underlying COFF file. - COFFObjectFile *getCOFFObj() { return COFFObj.get(); } + COFFObjectFile *getCOFFObj() { return coffObj.get(); } - // Whether the object was already merged into the final PDB or not - bool wasProcessedForPDB() const { return !!ModuleDBI; } + // Add a symbol for a range extension thunk. Return the new symbol table + // index. This index can be used to modify a relocation. + uint32_t addRangeThunkSymbol(Symbol *thunk) { + symbols.push_back(thunk); + return symbols.size() - 1; + } - static std::vector Instances; + static std::vector instances; // Flags in the absolute @feat.00 symbol if it is present. These usually // indicate if an object was compiled with certain security features enabled // like stack guard, safeseh, /guard:cf, or other things. - uint32_t Feat00Flags = 0; + uint32_t feat00Flags = 0; // True if this object file is compatible with SEH. COFF-specific and // x86-only. COFF spec 5.10.1. The .sxdata section. - bool hasSafeSEH() { return Feat00Flags & 0x1; } + bool hasSafeSEH() { return feat00Flags & 0x1; } // True if this file was compiled with /guard:cf. - bool hasGuardCF() { return Feat00Flags & 0x800; } + bool hasGuardCF() { return feat00Flags & 0x800; } // Pointer to the PDB module descriptor builder. Various debug info records // will reference object files by "module index", which is here. Things like // source files and section contributions are also recorded here. Will be null // if we are not producing a PDB. - llvm::pdb::DbiModuleDescriptorBuilder *ModuleDBI = nullptr; + llvm::pdb::DbiModuleDescriptorBuilder *moduleDBI = nullptr; - const coff_section *AddrsigSec = nullptr; + const coff_section *addrsigSec = nullptr; // When using Microsoft precompiled headers, this is the PCH's key. // The same key is used by both the precompiled object, and objects using the // precompiled object. Any difference indicates out-of-date objects. - llvm::Optional PCHSignature; + llvm::Optional pchSignature; + + // Whether this is an object file created from .res files. + bool isResourceObjFile = false; + + // Whether this file was compiled with /hotpatch. + bool hotPatchable = false; + + // Whether the object was already merged into the final PDB. + bool mergedIntoPDB = false; + + // If the OBJ has a .debug$T stream, this tells how it will be handled. + TpiSource *debugTypesObj = nullptr; + + // The .debug$T stream if there's one. + llvm::Optional debugTypes; private: + const coff_section* getSection(uint32_t i); + const coff_section *getSection(COFFSymbolRef sym) { + return getSection(sym.getSectionNumber()); + } + void initializeChunks(); void initializeSymbols(); + void initializeFlags(); + void initializeDependencies(); SectionChunk * - readSection(uint32_t SectionNumber, - const llvm::object::coff_aux_section_definition *Def, - StringRef LeaderName); + readSection(uint32_t sectionNumber, + const llvm::object::coff_aux_section_definition *def, + StringRef leaderName); void readAssociativeDefinition( - COFFSymbolRef COFFSym, - const llvm::object::coff_aux_section_definition *Def); + COFFSymbolRef coffSym, + const llvm::object::coff_aux_section_definition *def); void readAssociativeDefinition( - COFFSymbolRef COFFSym, - const llvm::object::coff_aux_section_definition *Def, - uint32_t ParentSection); + COFFSymbolRef coffSym, + const llvm::object::coff_aux_section_definition *def, + uint32_t parentSection); void recordPrevailingSymbolForMingw( - COFFSymbolRef COFFSym, - llvm::DenseMap &PrevailingSectionMap); + COFFSymbolRef coffSym, + llvm::DenseMap &prevailingSectionMap); void maybeAssociateSEHForMingw( - COFFSymbolRef Sym, const llvm::object::coff_aux_section_definition *Def, - const llvm::DenseMap &PrevailingSectionMap); + COFFSymbolRef sym, const llvm::object::coff_aux_section_definition *def, + const llvm::DenseMap &prevailingSectionMap); + + // Given a new symbol Sym with comdat selection Selection, if the new + // symbol is not (yet) Prevailing and the existing comdat leader set to + // Leader, emits a diagnostic if the new symbol and its selection doesn't + // match the existing symbol and its selection. If either old or new + // symbol have selection IMAGE_COMDAT_SELECT_LARGEST, Sym might replace + // the existing leader. In that case, Prevailing is set to true. + void handleComdatSelection(COFFSymbolRef sym, + llvm::COFF::COMDATType &selection, + bool &prevailing, DefinedRegular *leader); llvm::Optional - createDefined(COFFSymbolRef Sym, + createDefined(COFFSymbolRef sym, std::vector - &ComdatDefs, - bool &PrevailingComdat); - Symbol *createRegular(COFFSymbolRef Sym); - Symbol *createUndefined(COFFSymbolRef Sym); + &comdatDefs, + bool &prevailingComdat); + Symbol *createRegular(COFFSymbolRef sym); + Symbol *createUndefined(COFFSymbolRef sym); - std::unique_ptr COFFObj; + std::unique_ptr coffObj; // List of all chunks defined by this file. This includes both section // chunks and non-section chunks for common symbols. - std::vector Chunks; + std::vector chunks; // CodeView debug info sections. - std::vector DebugChunks; + std::vector debugChunks; // Chunks containing symbol table indices of exception handlers. Only used for // 32-bit x86. - std::vector SXDataChunks; + std::vector sXDataChunks; // Chunks containing symbol table indices of address taken symbols and longjmp // targets. These are not linked into the final binary when /guard:cf is set. - std::vector GuardFidChunks; - std::vector GuardLJmpChunks; + std::vector guardFidChunks; + std::vector guardLJmpChunks; // This vector contains the same chunks as Chunks, but they are // indexed such that you can get a SectionChunk by section index. // Nonexistent section indices are filled with null pointers. // (Because section number is 1-based, the first slot is always a // null pointer.) - std::vector SparseChunks; + std::vector sparseChunks; // This vector contains a list of all symbols defined or referenced by this // file. They are indexed such that you can get a Symbol by symbol // index. Nonexistent indices (which are occupied by auxiliary // symbols in the real symbol table) are filled with null pointers. - std::vector Symbols; + std::vector symbols; }; // This type represents import library members that contain DLL names @@ -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 Instances; + static std::vector instances; - Symbol *ImpSym = nullptr; - Symbol *ThunkSym = nullptr; - std::string DLLName; + Symbol *impSym = nullptr; + Symbol *thunkSym = nullptr; + std::string dllName; private: void parse() override; public: - StringRef ExternalName; - const coff_import_header *Hdr; - Chunk *Location = nullptr; + StringRef externalName; + const coff_import_header *hdr; + Chunk *location = nullptr; // We want to eliminate dllimported symbols if no one actually refers them. // These "Live" bits are used to keep track of which import library members @@ -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 getSymbols() { return Symbols; } + BitcodeFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive); + static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } + ArrayRef getSymbols() { return symbols; } MachineTypes getMachineType() override; - static std::vector Instances; - std::unique_ptr Obj; + static std::vector instances; + std::unique_ptr obj; private: void parse() override; - std::vector Symbols; + std::vector symbols; }; + +std::string replaceThinLTOSuffix(StringRef path); } // namespace coff -std::string toString(const coff::InputFile *File); +std::string toString(const coff::InputFile *file); } // namespace lld #endif diff --git a/COFF/LTO.cpp b/COFF/LTO.cpp index 92d9ff0..eb3c60d 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 createLTO() { - lto::Config C; - C.Options = InitTargetOptionsFromCodeGenFlags(); +// Creates an empty file to and returns a raw_fd_ostream to write to it. +static std::unique_ptr openFile(StringRef file) { + std::error_code ec; + auto ret = + llvm::make_unique(file, ec, sys::fs::OpenFlags::F_None); + if (ec) { + error("cannot open " + file + ": " + ec.message()); + return nullptr; + } + return ret; +} + +static std::string getThinLTOOutputFile(StringRef path) { + return lto::getThinLTOOutputFile(path, + config->thinLTOPrefixReplace.first, + config->thinLTOPrefixReplace.second); +} + +static lto::Config createConfig() { + lto::Config c; + c.Options = initTargetOptionsFromCodeGenFlags(); // Always emit a section per function/datum with LTO. LLVM LTO should get most // of the benefit of linker GC, but there are still opportunities for ICF. - C.Options.FunctionSections = true; - C.Options.DataSections = true; + c.Options.FunctionSections = true; + c.Options.DataSections = true; // Use static reloc model on 32-bit x86 because it usually results in more // compact code, and because there are also known code generation bugs when // using the PIC model (see PR34306). - if (Config->Machine == COFF::IMAGE_FILE_MACHINE_I386) - C.RelocModel = Reloc::Static; + if (config->machine == COFF::IMAGE_FILE_MACHINE_I386) + c.RelocModel = Reloc::Static; else - C.RelocModel = Reloc::PIC_; - C.DisableVerify = true; - C.DiagHandler = diagnosticHandler; - C.OptLevel = Config->LTOO; - C.CPU = GetCPUStr(); - C.MAttrs = GetMAttrs(); - - 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(std::move(C), Backend, - Config->LTOPartitions); + return c; } -BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {} +BitcodeCompiler::BitcodeCompiler() { + // Initialize indexFile. + if (!config->thinLTOIndexOnlyArg.empty()) + indexFile = openFile(config->thinLTOIndexOnlyArg); + + // Initialize ltoObj. + lto::ThinBackend backend; + if (config->thinLTOIndexOnly) { + auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); }; + backend = lto::createWriteIndexesThinBackend( + config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second, + config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite); + } else if (config->thinLTOJobs != 0) { + backend = lto::createInProcessThinBackend(config->thinLTOJobs); + } + + ltoObj = llvm::make_unique(createConfig(), backend, + config->ltoPartitions); +} BitcodeCompiler::~BitcodeCompiler() = default; -static void undefine(Symbol *S) { replaceSymbol(S, S->getName()); } +static void undefine(Symbol *s) { replaceSymbol(s, s->getName()); } + +void BitcodeCompiler::add(BitcodeFile &f) { + lto::InputFile &obj = *f.obj; + unsigned symNum = 0; + std::vector symBodies = f.getSymbols(); + std::vector resols(symBodies.size()); -void BitcodeCompiler::add(BitcodeFile &F) { - lto::InputFile &Obj = *F.Obj; - unsigned SymNum = 0; - std::vector SymBodies = F.getSymbols(); - std::vector Resols(SymBodies.size()); + if (config->thinLTOIndexOnly) + thinIndices.insert(obj.getName()); // Provide a resolution to the LTO API for each symbol. - for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) { - Symbol *Sym = SymBodies[SymNum]; - lto::SymbolResolution &R = Resols[SymNum]; - ++SymNum; + for (const lto::InputFile::Symbol &objSym : obj.symbols()) { + Symbol *sym = symBodies[symNum]; + lto::SymbolResolution &r = resols[symNum]; + ++symNum; // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile // reports two symbols for module ASM defined. Without this check, lld // flags an undefined in IR with a definition in ASM as prevailing. // Once IRObjectFile is fixed to report only one symbol this hack can // be removed. - R.Prevailing = !ObjSym.isUndefined() && Sym->getFile() == &F; - R.VisibleToRegularObj = Sym->IsUsedInRegularObj; - if (R.Prevailing) - undefine(Sym); + r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f; + r.VisibleToRegularObj = sym->isUsedInRegularObj; + if (r.Prevailing) + undefine(sym); } - checkError(LTOObj->add(std::move(F.Obj), Resols)); + checkError(ltoObj->add(std::move(f.obj), resols)); } // Merge all the bitcode files we have seen, codegen the result // and return the resulting objects. std::vector BitcodeCompiler::compile() { - unsigned MaxTasks = LTOObj->getMaxTasks(); - Buf.resize(MaxTasks); - Files.resize(MaxTasks); + unsigned maxTasks = ltoObj->getMaxTasks(); + buf.resize(maxTasks); + files.resize(maxTasks); // The /lldltocache option specifies the path to a directory in which to cache // native object files for ThinLTO incremental builds. If a path was // specified, configure LTO to use it as the cache directory. - lto::NativeObjectCache Cache; - if (!Config->LTOCache.empty()) - Cache = check(lto::localCache( - Config->LTOCache, [&](size_t Task, std::unique_ptr MB) { - Files[Task] = std::move(MB); + lto::NativeObjectCache cache; + if (!config->ltoCache.empty()) + cache = check(lto::localCache( + config->ltoCache, [&](size_t task, std::unique_ptr mb) { + files[task] = std::move(mb); })); - checkError(LTOObj->run( - [&](size_t Task) { + checkError(ltoObj->run( + [&](size_t task) { return llvm::make_unique( - llvm::make_unique(Buf[Task])); + llvm::make_unique(buf[task])); }, - Cache)); + cache)); + + // 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 Ret; - for (unsigned I = 0; I != MaxTasks; ++I) { - if (Buf[I].empty()) + std::vector ret; + for (unsigned i = 0; i != maxTasks; ++i) { + if (buf[i].empty()) continue; - if (Config->SaveTemps) { - if (I == 0) - saveBuffer(Buf[I], Config->OutputFile + ".lto.obj"); + if (config->saveTemps) { + if (i == 0) + saveBuffer(buf[i], config->outputFile + ".lto.obj"); else - saveBuffer(Buf[I], Config->OutputFile + Twine(I) + ".lto.obj"); + saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.obj"); } - Ret.emplace_back(Buf[I].data(), Buf[I].size()); + ret.emplace_back(buf[i].data(), buf[i].size()); } - for (std::unique_ptr &File : Files) - if (File) - Ret.push_back(File->getBuffer()); + for (std::unique_ptr &file : files) + if (file) + ret.push_back(file->getBuffer()); - return Ret; + return ret; } diff --git a/COFF/LTO.h b/COFF/LTO.h index f009246..2a0cfa0 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 #include @@ -43,13 +44,15 @@ public: BitcodeCompiler(); ~BitcodeCompiler(); - void add(BitcodeFile &F); + void add(BitcodeFile &f); std::vector compile(); private: - std::unique_ptr LTOObj; - std::vector> Buf; - std::vector> Files; + std::unique_ptr ltoObj; + std::vector> buf; + std::vector> files; + std::unique_ptr indexFile; + llvm::DenseSet thinIndices; }; } } diff --git a/COFF/MapFile.cpp b/COFF/MapFile.cpp index fd48942..f98cf8f 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> - SymbolMapTy; +using SymbolMapTy = + DenseMap>; -static const std::string Indent8 = " "; // 8 spaces -static const std::string Indent16 = " "; // 16 spaces +static const std::string indent8 = " "; // 8 spaces +static const std::string indent16 = " "; // 16 spaces // Print out the first three columns of a line. -static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size, - uint64_t Align) { - OS << format("%08llx %08llx %5lld ", Addr, Size, Align); +static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size, + uint64_t align) { + os << format("%08llx %08llx %5lld ", addr, size, align); } // Returns a list of all symbols that we want to print out. static std::vector getSymbols() { - std::vector V; - for (ObjFile *File : ObjFile::Instances) - for (Symbol *B : File->getSymbols()) - if (auto *Sym = dyn_cast_or_null(B)) - if (Sym && !Sym->getCOFFSymbol().isSectionDefinition()) - V.push_back(Sym); - return V; + std::vector v; + for (ObjFile *file : ObjFile::instances) + for (Symbol *b : file->getSymbols()) + if (auto *sym = dyn_cast_or_null(b)) + if (sym && !sym->getCOFFSymbol().isSectionDefinition()) + v.push_back(sym); + return v; } // Returns a map from sections to their symbols. -static SymbolMapTy getSectionSyms(ArrayRef Syms) { - SymbolMapTy Ret; - for (DefinedRegular *S : Syms) - Ret[S->getChunk()].push_back(S); +static SymbolMapTy getSectionSyms(ArrayRef syms) { + SymbolMapTy ret; + for (DefinedRegular *s : syms) + ret[s->getChunk()].push_back(s); // Sort symbols by address. - for (auto &It : Ret) { - SmallVectorImpl &V = It.second; - std::sort(V.begin(), V.end(), [](DefinedRegular *A, DefinedRegular *B) { - return A->getRVA() < B->getRVA(); + for (auto &it : ret) { + SmallVectorImpl &v = it.second; + std::sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) { + return a->getRVA() < b->getRVA(); }); } - return Ret; + return ret; } // Construct a map from symbols to their stringified representations. static DenseMap -getSymbolStrings(ArrayRef Syms) { - std::vector Str(Syms.size()); - for_each_n(parallel::par, (size_t)0, Syms.size(), [&](size_t I) { - raw_string_ostream OS(Str[I]); - writeHeader(OS, Syms[I]->getRVA(), 0, 0); - OS << Indent16 << toString(*Syms[I]); +getSymbolStrings(ArrayRef syms) { + std::vector str(syms.size()); + parallelForEachN((size_t)0, syms.size(), [&](size_t i) { + raw_string_ostream os(str[i]); + writeHeader(os, syms[i]->getRVA(), 0, 0); + os << indent16 << toString(*syms[i]); }); - DenseMap Ret; - for (size_t I = 0, E = Syms.size(); I < E; ++I) - Ret[Syms[I]] = std::move(Str[I]); - return Ret; + DenseMap ret; + for (size_t i = 0, e = syms.size(); i < e; ++i) + ret[syms[i]] = std::move(str[i]); + return ret; } -void coff::writeMapFile(ArrayRef OutputSections) { - if (Config->MapFile.empty()) +void coff::writeMapFile(ArrayRef outputSections) { + if (config->mapFile.empty()) return; - std::error_code EC; - raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None); - if (EC) - fatal("cannot open " + Config->MapFile + ": " + EC.message()); + std::error_code ec; + raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None); + if (ec) + fatal("cannot open " + config->mapFile + ": " + ec.message()); // Collect symbol info that we want to print out. - std::vector Syms = getSymbols(); - SymbolMapTy SectionSyms = getSectionSyms(Syms); - DenseMap SymStr = getSymbolStrings(Syms); + std::vector syms = getSymbols(); + SymbolMapTy sectionSyms = getSectionSyms(syms); + DenseMap symStr = getSymbolStrings(syms); // Print out the header line. - OS << "Address Size Align Out In Symbol\n"; + os << "Address Size Align Out In Symbol\n"; // Print out file contents. - for (OutputSection *Sec : OutputSections) { - writeHeader(OS, Sec->getRVA(), Sec->getVirtualSize(), /*Align=*/PageSize); - OS << Sec->Name << '\n'; + for (OutputSection *sec : outputSections) { + writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize); + os << sec->name << '\n'; - for (Chunk *C : Sec->Chunks) { - auto *SC = dyn_cast(C); - if (!SC) + for (Chunk *c : sec->chunks) { + auto *sc = dyn_cast(c); + if (!sc) continue; - writeHeader(OS, SC->getRVA(), SC->getSize(), SC->Alignment); - OS << Indent8 << SC->File->getName() << ":(" << SC->getSectionName() + writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment()); + os << indent8 << sc->file->getName() << ":(" << sc->getSectionName() << ")\n"; - for (DefinedRegular *Sym : SectionSyms[SC]) - OS << SymStr[Sym] << '\n'; + for (DefinedRegular *sym : sectionSyms[sc]) + os << symStr[sym] << '\n'; } } } diff --git a/COFF/MapFile.h b/COFF/MapFile.h index 0d0d68c..2bf01bd 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 OutputSections); +void writeMapFile(llvm::ArrayRef outputSections); } } diff --git a/COFF/MarkLive.cpp b/COFF/MarkLive.cpp index 18b1c9c..6d34cb8 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 Chunks) { - ScopedTimer T(GCTimer); +void markLive(ArrayRef chunks) { + ScopedTimer t(gctimer); // We build up a worklist of sections which have been marked as live. We only // push into the worklist when we discover an unmarked section, and we mark // as we push, so sections never appear twice in the list. - SmallVector Worklist; + SmallVector worklist; // COMDAT section chunks are dead by default. Add non-COMDAT chunks. - for (Chunk *C : Chunks) - if (auto *SC = dyn_cast(C)) - if (SC->Live) - Worklist.push_back(SC); + for (Chunk *c : chunks) + if (auto *sc = dyn_cast(c)) + if (sc->live) + worklist.push_back(sc); - auto Enqueue = [&](SectionChunk *C) { - if (C->Live) + auto enqueue = [&](SectionChunk *c) { + if (c->live) return; - C->Live = true; - Worklist.push_back(C); + c->live = true; + worklist.push_back(c); }; - auto AddSym = [&](Symbol *B) { - if (auto *Sym = dyn_cast(B)) - Enqueue(Sym->getChunk()); - else if (auto *Sym = dyn_cast(B)) - Sym->File->Live = true; - else if (auto *Sym = dyn_cast(B)) - Sym->WrappedSym->File->Live = Sym->WrappedSym->File->ThunkLive = true; + auto addSym = [&](Symbol *b) { + if (auto *sym = dyn_cast(b)) + enqueue(sym->getChunk()); + else if (auto *sym = dyn_cast(b)) + sym->file->live = true; + else if (auto *sym = dyn_cast(b)) + sym->wrappedSym->file->live = sym->wrappedSym->file->thunkLive = true; }; // Add GC root chunks. - for (Symbol *B : Config->GCRoot) - AddSym(B); + for (Symbol *b : config->gcroot) + addSym(b); - while (!Worklist.empty()) { - SectionChunk *SC = Worklist.pop_back_val(); - assert(SC->Live && "We mark as live when pushing onto the worklist!"); + while (!worklist.empty()) { + SectionChunk *sc = worklist.pop_back_val(); + assert(sc->live && "We mark as live when pushing onto the worklist!"); // Mark all symbols listed in the relocation table for this section. - for (Symbol *B : SC->symbols()) - if (B) - AddSym(B); + for (Symbol *b : sc->symbols()) + if (b) + addSym(b); // Mark associative sections if any. - for (SectionChunk *C : SC->children()) - Enqueue(C); + for (SectionChunk &c : sc->children()) + enqueue(&c); } } diff --git a/COFF/MarkLive.h b/COFF/MarkLive.h index 5b652dd..e4e4c31 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 Chunks); +class Chunk; + +void markLive(ArrayRef chunks); } // namespace coff } // namespace lld diff --git a/COFF/MinGW.cpp b/COFF/MinGW.cpp index b2c8c4e..2ca8ca0 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(Sym) && !isa(Sym)) + if (!isa(sym) && !isa(sym)) return false; - if (ExcludeSymbols.count(Sym->getName())) + if (excludeSymbols.count(sym->getName())) return false; - for (StringRef Prefix : ExcludeSymbolPrefixes.keys()) - if (Sym->getName().startswith(Prefix)) + for (StringRef prefix : excludeSymbolPrefixes.keys()) + if (sym->getName().startswith(prefix)) return false; - for (StringRef Suffix : ExcludeSymbolSuffixes.keys()) - if (Sym->getName().endswith(Suffix)) + for (StringRef suffix : excludeSymbolSuffixes.keys()) + if (sym->getName().endswith(suffix)) return false; // If a corresponding __imp_ symbol exists and is defined, don't export it. - if (Symtab->find(("__imp_" + Sym->getName()).str())) + if (symtab->find(("__imp_" + sym->getName()).str())) return false; // Check that file is non-null before dereferencing it, symbols not // originating in regular object files probably shouldn't be exported. - if (!Sym->getFile()) + if (!sym->getFile()) return false; - StringRef LibName = sys::path::filename(Sym->getFile()->ParentName); + StringRef libName = sys::path::filename(sym->getFile()->parentName); // Drop the file extension. - LibName = LibName.substr(0, LibName.rfind('.')); - if (!LibName.empty()) - return !ExcludeLibs.count(LibName); + libName = libName.substr(0, libName.rfind('.')); + if (!libName.empty()) + return !excludeLibs.count(libName); - StringRef FileName = sys::path::filename(Sym->getFile()->getName()); - return !ExcludeObjects.count(FileName); + StringRef fileName = sys::path::filename(sym->getFile()->getName()); + return !excludeObjects.count(fileName); } -void coff::writeDefFile(StringRef Name) { - std::error_code EC; - raw_fd_ostream OS(Name, EC, sys::fs::F_None); - if (EC) - fatal("cannot open " + Name + ": " + EC.message()); - - OS << "EXPORTS\n"; - for (Export &E : Config->Exports) { - OS << " " << E.ExportName << " " - << "@" << E.Ordinal; - if (auto *Def = dyn_cast_or_null(E.Sym)) { - if (Def && Def->getChunk() && - !(Def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) - OS << " DATA"; +void coff::writeDefFile(StringRef name) { + std::error_code ec; + raw_fd_ostream os(name, ec, sys::fs::F_None); + if (ec) + fatal("cannot open " + name + ": " + ec.message()); + + os << "EXPORTS\n"; + for (Export &e : config->exports) { + os << " " << e.exportName << " " + << "@" << e.ordinal; + if (auto *def = dyn_cast_or_null(e.sym)) { + if (def && def->getChunk() && + !(def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) + os << " DATA"; } - OS << "\n"; + os << "\n"; } } diff --git a/COFF/MinGW.h b/COFF/MinGW.h index f9c5e3e..578a277 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 acf1bc5..024b7be 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 : Flag<["/", "-", "-?"], name>; +class F : Flag<["/", "-", "/?", "-?"], name>; // Flag that takes one argument after ":". class P : - Joined<["/", "-", "-?"], name#":">, HelpText; + Joined<["/", "-", "/?", "-?"], name#":">, HelpText; // Boolean flag which can be suffixed by ":no". Using it unsuffixed turns the // flag on and using it suffixed by ":no" turns it off. @@ -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; +def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">, + Alias; 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, AliasArgs<["cd"]>, + HelpText<"Make loader run output binary from swap instead of from CD">; +def swaprun_net : F<"swaprun:net">, Alias, AliasArgs<["net"]>, + HelpText<"Make loader run output binary from swap instead of from network">; def verbose : F<"verbose">; def wholearchive_flag : F<"wholearchive">; @@ -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; + +// /?? and -?? must be before /? and -? to not confuse lib/Options. +def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias; // LLD extensions +def exclude_all_symbols : F<"exclude-all-symbols">; def export_all_symbols : F<"export-all-symbols">; +defm demangle : B<"demangle", + "Demangle symbols in output (default)", + "Do not demangle symbols in output">; +def include_optional : Joined<["/", "-", "/?", "-?"], "includeoptional:">, + HelpText<"Add symbol as undefined, but allow it to remain undefined">; def kill_at : F<"kill-at">; def lldmingw : F<"lldmingw">; -def output_def : Joined<["/", "-"], "output-def:">; +def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">; def pdb_source_path : P<"pdbsourcepath", "Base path used to make relative source file path absolute in PDB">; def rsp_quoting : Joined<["--"], "rsp-quoting=">, HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">; +def thinlto_emit_imports_files : + F<"thinlto-emit-imports-files">, + HelpText<"Emit .imports files with -thinlto-index-only">; +def thinlto_index_only : + F<"thinlto-index-only">, + HelpText<"Instead of linking, emit ThinLTO index files">; +def thinlto_index_only_arg : P< + "thinlto-index-only", + "-thinlto-index-only and also write native module names to file">; +def thinlto_object_suffix_replace : P< + "thinlto-object-suffix-replace", + "'old;new' replace old suffix with new suffix in ThinLTO index">; +def thinlto_prefix_replace: P< + "thinlto-prefix-replace", + "'old;new' replace old prefix with new prefix in ThinLTO outputs">; def dash_dash_version : Flag<["--"], "version">, HelpText<"Print version information">; +defm threads: B<"threads", + "Run the linker multi-threaded (default)", + "Do not run the linker multi-threaded">; // Flags for debugging def lldmap : F<"lldmap">; -def lldmap_file : Joined<["/", "-"], "lldmap:">; +def lldmap_file : Joined<["/", "-", "/?", "-?"], "lldmap:">; def show_timing : F<"time">; +def summary : F<"summary">; //============================================================================== // The flags below do nothing. They are defined only for link.exe compatibility. //============================================================================== -class QF : Joined<["/", "-", "-?"], name#":">; +class QF : Joined<["/", "-", "/?", "-?"], name#":">; -multiclass QB { - def "" : F; - def _no : F; -} - -def functionpadmin : F<"functionpadmin">; def ignoreidl : F<"ignoreidl">; def nologo : F<"nologo">; def throwingnew : F<"throwingnew">; diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp index 7862b6c..a55e513 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 @@ -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 TPIMap; - SmallVector IPIMap; - bool IsTypeServerMap = false; - bool IsPrecompiledTypeMap = false; -}; - class DebugSHandler; class PDBLinker { friend DebugSHandler; public: - PDBLinker(SymbolTable *Symtab) - : Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc), - IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc) { + PDBLinker(SymbolTable *symtab) + : alloc(), symtab(symtab), builder(alloc), tMerger(alloc) { // This isn't strictly necessary, but link.exe usually puts an empty string // as the first "valid" string in the string table, so we do the same in // order to maintain as much byte-for-byte compatibility as possible. - PDBStrTab.insert(""); + pdbStrTab.insert(""); } /// Emit the basic PDB structure: initial streams, headers, etc. - void initialize(llvm::codeview::DebugInfo *BuildId); + void initialize(llvm::codeview::DebugInfo *buildId); /// Add natvis files specified on the command line. void addNatvisFiles(); @@ -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 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 mergeDebugT(ObjFile *File, - CVIndexMap *ObjectIndexMap); + /// caller-provided objectIndexMap. + Expected mergeDebugT(ObjFile *file, + CVIndexMap *objectIndexMap); /// Reads and makes available a PDB. - Expected maybeMergeTypeServerPDB(ObjFile *File, - const CVType &FirstType); + Expected maybeMergeTypeServerPDB(ObjFile *file); /// Merges a precompiled headers TPI map into the current TPI map. The /// precompiled headers object will also be loaded and remapped in the /// process. - Expected - mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType, - CVIndexMap *ObjectIndexMap); + Error mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *objectIndexMap); /// Reads and makes available a precompiled headers object. /// @@ -149,109 +140,87 @@ public: /// /// If the precompiled headers object was already loaded, this function will /// simply return its (remapped) TPI map. - Expected aquirePrecompObj(ObjFile *File, - PrecompRecord Precomp); + Expected aquirePrecompObj(ObjFile *file); /// Adds a precompiled headers object signature -> TPI mapping. std::pair - registerPrecompiledHeaders(uint32_t Signature); + registerPrecompiledHeaders(uint32_t signature); - void mergeSymbolRecords(ObjFile *File, const CVIndexMap &IndexMap, - std::vector &StringTableRefs, - BinaryStreamRef SymData); + void mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, + std::vector &stringTableRefs, + BinaryStreamRef symData); /// Add the section map and section contributions to the PDB. - void addSections(ArrayRef OutputSections, - ArrayRef SectionTable); - - /// 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 outputSections, + ArrayRef 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, 2> LoadedPDBs; + llvm::SmallString<128> nativePath; - std::vector SectionMap; + std::vector sectionMap; /// Type index mappings of type server PDBs that we've loaded so far. - std::map TypeServerIndexMappings; + std::map typeServerIndexMappings; /// Type index mappings of precompiled objects type map that we've loaded so /// far. - std::map PrecompTypeIndexMappings; + std::map precompTypeIndexMappings; - /// List of TypeServer PDBs which cannot be loaded. - /// Cached to prevent repeated load attempts. - std::map MissingTypeServerPDBs; + // For statistics + uint64_t globalSymbols = 0; + uint64_t moduleSymbols = 0; + uint64_t publicSymbols = 0; }; class DebugSHandler { - PDBLinker &Linker; + PDBLinker &linker; /// The object file whose .debug$S sections we're processing. - ObjFile &File; + ObjFile &file; /// The result of merging type indices. - const CVIndexMap &IndexMap; + const CVIndexMap &indexMap; /// The DEBUG_S_STRINGTABLE subsection. These strings are referred to by /// index from other records in the .debug$S section. All of these strings /// need to be added to the global PDB string table, and all references to /// these strings need to have their indices re-written to refer to the /// global PDB string table. - DebugStringTableSubsectionRef CVStrTab; + DebugStringTableSubsectionRef cVStrTab; /// The DEBUG_S_FILECHKSMS subsection. As above, these are referred to /// by other records in the .debug$S section and need to be merged into the /// PDB. - DebugChecksumsSubsectionRef Checksums; + DebugChecksumsSubsectionRef checksums; + + /// The DEBUG_S_INLINEELINES subsection. There can be only one of these per + /// object file. + DebugInlineeLinesSubsectionRef inlineeLines; /// The DEBUG_S_FRAMEDATA subsection(s). There can be more than one of /// these and they need not appear in any specific order. However, they /// contain string table references which need to be re-written, so we /// collect them all here and re-write them after all subsections have been /// discovered and processed. - std::vector NewFpoFrames; + std::vector newFpoFrames; /// Pointers to raw memory that we determine have string table references /// that need to be re-written. We first process all .debug$S subsections @@ -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 StringTableReferences; + std::vector stringTableReferences; public: - DebugSHandler(PDBLinker &Linker, ObjFile &File, const CVIndexMap &IndexMap) - : Linker(Linker), File(File), IndexMap(IndexMap) {} + DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap &indexMap) + : linker(linker), file(file), indexMap(indexMap) {} + + void handleDebugS(lld::coff::SectionChunk &debugS); + + std::shared_ptr + 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 &FileName) { +static void pdbMakeAbsolute(SmallVectorImpl &fileName) { // The default behavior is to produce paths that are valid within the context // of the machine that you perform the link on. If the linker is running on // a POSIX system, we will output absolute POSIX paths. If the linker is @@ -281,408 +254,256 @@ static void pdbMakeAbsolute(SmallVectorImpl &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 Sections, - StringRef Name) { - for (SectionChunk *C : Sections) - if (C->getSectionName() == Name) - return C; - return nullptr; -} -static ArrayRef consumeDebugMagic(ArrayRef Data, - StringRef SecName) { - // First 4 bytes are section magic. - if (Data.size() < 4) - fatal(SecName + " too short"); - if (support::endian::read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC) - fatal(SecName + " has an invalid magic"); - return Data.slice(4); -} - -static ArrayRef getDebugSection(ObjFile *File, StringRef SecName) { - if (SectionChunk *Sec = findByName(File->getDebugChunks(), SecName)) - return consumeDebugMagic(Sec->getContents(), SecName); - return {}; + // 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 DebugH) { - if (DebugH.size() < sizeof(object::debug_h_header)) +static bool canUseDebugH(ArrayRef debugH) { + if (debugH.size() < sizeof(object::debug_h_header)) return false; - auto *Header = - reinterpret_cast(DebugH.data()); - DebugH = DebugH.drop_front(sizeof(object::debug_h_header)); - return Header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC && - Header->Version == 0 && - Header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) && - (DebugH.size() % 8 == 0); + auto *header = + reinterpret_cast(debugH.data()); + debugH = debugH.drop_front(sizeof(object::debug_h_header)); + return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC && + header->Version == 0 && + header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) && + (debugH.size() % 8 == 0); } -static Optional> getDebugH(ObjFile *File) { - SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$H"); - if (!Sec) +static Optional> getDebugH(ObjFile *file) { + SectionChunk *sec = + SectionChunk::findByName(file->getDebugChunks(), ".debug$H"); + if (!sec) return llvm::None; - ArrayRef Contents = Sec->getContents(); - if (!canUseDebugH(Contents)) + ArrayRef contents = sec->getContents(); + if (!canUseDebugH(contents)) return None; - return Contents; + return contents; } static ArrayRef -getHashesFromDebugH(ArrayRef DebugH) { - assert(canUseDebugH(DebugH)); +getHashesFromDebugH(ArrayRef debugH) { + assert(canUseDebugH(debugH)); - DebugH = DebugH.drop_front(sizeof(object::debug_h_header)); - uint32_t Count = DebugH.size() / sizeof(GloballyHashedType); - return {reinterpret_cast(DebugH.data()), Count}; + debugH = debugH.drop_front(sizeof(object::debug_h_header)); + uint32_t count = debugH.size() / sizeof(GloballyHashedType); + return {reinterpret_cast(debugH.data()), count}; } -static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder, - TypeCollection &TypeTable) { +static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder, + TypeCollection &typeTable) { // Start the TPI or IPI stream header. - TpiBuilder.setVersionHeader(pdb::PdbTpiV80); + tpiBuilder.setVersionHeader(pdb::PdbTpiV80); // Flatten the in memory type table and hash each type. - TypeTable.ForEachRecord([&](TypeIndex TI, const CVType &Type) { - auto Hash = pdb::hashTypeRecord(Type); - if (auto E = Hash.takeError()) + typeTable.ForEachRecord([&](TypeIndex ti, const CVType &type) { + auto hash = pdb::hashTypeRecord(type); + if (auto e = hash.takeError()) fatal("type hashing error"); - TpiBuilder.addTypeRecord(Type.RecordData, *Hash); - }); -} - -// 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 Contents = - consumeDebugMagic((*DbgIt)->getContents(), ".debug$S"); - DebugSubsectionArray Subsections; - BinaryStreamReader Reader(Contents, support::little); - ExitOnErr(Reader.readArray(Subsections, Contents.size())); - - for (const DebugSubsectionRecord &SS : Subsections) { - if (SS.kind() != DebugSubsectionKind::Symbols) - continue; - - // If it's there, the S_OBJNAME record shall come first in the stream. - Expected Sym = readSymbolFromStream(SS.getRecordData(), 0); - if (!Sym) { - consumeError(Sym.takeError()); - continue; - } - if (auto ObjName = SymbolDeserializer::deserializeAs(Sym.get())) - return ObjName->Signature; - } - return 0; } Expected -PDBLinker::mergeDebugT(ObjFile *File, CVIndexMap *ObjectIndexMap) { - ScopedTimer T(TypeMergingTimer); +PDBLinker::mergeDebugT(ObjFile *file, CVIndexMap *objectIndexMap) { + ScopedTimer t(typeMergingTimer); - bool IsPrecompiledHeader = false; - - ArrayRef Data = getDebugSection(File, ".debug$T"); - if (Data.empty()) { - // Try again, Microsoft precompiled headers use .debug$P instead of - // .debug$T - Data = getDebugSection(File, ".debug$P"); - IsPrecompiledHeader = true; - } - if (Data.empty()) - return *ObjectIndexMap; // no debug info + 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 Hashes; - std::vector OwnedHashes; - if (Optional> DebugH = getDebugH(File)) - Hashes = getHashesFromDebugH(*DebugH); + if (config->debugGHashes) { + ArrayRef hashes; + std::vector ownedHashes; + if (Optional> debugH = getDebugH(file)) + hashes = getHashesFromDebugH(*debugH); else { - OwnedHashes = GloballyHashedType::hashTypes(Types); - Hashes = OwnedHashes; + ownedHashes = GloballyHashedType::hashTypes(types); + hashes = ownedHashes; } - if (auto Err = mergeTypeAndIdRecords(GlobalIDTable, GlobalTypeTable, - ObjectIndexMap->TPIMap, Types, Hashes, - File->PCHSignature)) + if (auto err = mergeTypeAndIdRecords( + tMerger.globalIDTable, tMerger.globalTypeTable, + objectIndexMap->tpiMap, types, hashes, file->pchSignature)) fatal("codeview::mergeTypeAndIdRecords failed: " + - toString(std::move(Err))); + toString(std::move(err))); } else { - if (auto Err = - mergeTypeAndIdRecords(IDTable, TypeTable, ObjectIndexMap->TPIMap, - Types, File->PCHSignature)) + if (auto err = mergeTypeAndIdRecords(tMerger.iDTable, tMerger.typeTable, + objectIndexMap->tpiMap, types, + file->pchSignature)) fatal("codeview::mergeTypeAndIdRecords failed: " + - toString(std::move(Err))); + toString(std::move(err))); } - return *ObjectIndexMap; + return *objectIndexMap; } -static Expected> -tryToLoadPDB(const codeview::GUID &GuidFromObj, StringRef TSPath) { - // Ensure the file exists before anything else. We want to return ENOENT, - // "file not found", even if the path points to a removable device (in which - // case the return message would be EAGAIN, "resource unavailable try again") - if (!llvm::sys::fs::exists(TSPath)) - return errorCodeToError(std::error_code(ENOENT, std::generic_category())); - - ErrorOr> MBOrErr = MemoryBuffer::getFile( - TSPath, /*FileSize=*/-1, /*RequiresNullTerminator=*/false); - if (!MBOrErr) - return errorCodeToError(MBOrErr.getError()); - - std::unique_ptr ThisSession; - if (auto EC = pdb::NativeSession::createFromPdb( - MemoryBuffer::getMemBuffer(Driver->takeBuffer(std::move(*MBOrErr)), - /*RequiresNullTerminator=*/false), - ThisSession)) - return std::move(EC); - - std::unique_ptr NS( - static_cast(ThisSession.release())); - pdb::PDBFile &File = NS->getPDBFile(); - auto ExpectedInfo = File.getPDBInfoStream(); - // All PDB Files should have an Info stream. - if (!ExpectedInfo) - return ExpectedInfo.takeError(); - - // Just because a file with a matching name was found and it was an actual - // PDB file doesn't mean it matches. For it to match the InfoStream's GUID - // must match the GUID specified in the TypeServer2 record. - if (ExpectedInfo->getGuid() != GuidFromObj) - return make_error(pdb::pdb_error_code::signature_out_of_date); - - return std::move(NS); -} +Expected PDBLinker::maybeMergeTypeServerPDB(ObjFile *file) { + Expected pdbSession = findTypeServerSource(file); + if (!pdbSession) + return pdbSession.takeError(); -Expected -PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, const CVType &FirstType) { - TypeServer2Record TS; - if (auto EC = - TypeDeserializer::deserializeAs(const_cast(FirstType), TS)) - fatal("error reading record: " + toString(std::move(EC))); - - const codeview::GUID &TSId = TS.getGuid(); - StringRef TSPath = TS.getName(); - - // First, check if the PDB has previously failed to load. - auto PrevErr = MissingTypeServerPDBs.find(TSId); - if (PrevErr != MissingTypeServerPDBs.end()) - return createFileError( - TSPath, - make_error(PrevErr->second, inconvertibleErrorCode())); + 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 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 expectedTpi = pdbFile.getPDBTpiStream(); + if (auto e = expectedTpi.takeError()) + fatal("Type server does not have TPI stream: " + toString(std::move(e))); + pdb::TpiStream *maybeIpi = nullptr; + if (pdbFile.hasPDBIpiStream()) { + Expected expectedIpi = pdbFile.getPDBIpiStream(); + if (auto e = expectedIpi.takeError()) + fatal("Error getting type server IPI stream: " + toString(std::move(e))); + maybeIpi = &*expectedIpi; } - pdb::NativeSession *Session = ExpectedSession->get(); - - // Keep a strong reference to this PDB, so that it's safe to hold pointers - // into the file. - LoadedPDBs.push_back(std::move(*ExpectedSession)); - - auto ExpectedTpi = Session->getPDBFile().getPDBTpiStream(); - if (auto E = ExpectedTpi.takeError()) - fatal("Type server does not have TPI stream: " + toString(std::move(E))); - auto ExpectedIpi = Session->getPDBFile().getPDBIpiStream(); - if (auto E = ExpectedIpi.takeError()) - fatal("Type server does not have TPI stream: " + toString(std::move(E))); - - if (Config->DebugGHashes) { + if (config->debugGHashes) { // PDBs do not actually store global hashes, so when merging a type server // PDB we have to synthesize global hashes. To do this, we first synthesize // global hashes for the TPI stream, since it is independent, then we // synthesize hashes for the IPI stream, using the hashes for the TPI stream // as inputs. - auto TpiHashes = GloballyHashedType::hashTypes(ExpectedTpi->typeArray()); - auto IpiHashes = - GloballyHashedType::hashIds(ExpectedIpi->typeArray(), TpiHashes); - - Optional EndPrecomp; + auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray()); + Optional endPrecomp; // Merge TPI first, because the IPI stream will reference type indices. - if (auto Err = mergeTypeRecords(GlobalTypeTable, IndexMap.TPIMap, - ExpectedTpi->typeArray(), TpiHashes, EndPrecomp)) - fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err))); + if (auto err = + mergeTypeRecords(tMerger.globalTypeTable, indexMap.tpiMap, + expectedTpi->typeArray(), tpiHashes, endPrecomp)) + fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); // Merge IPI. - if (auto Err = - mergeIdRecords(GlobalIDTable, IndexMap.TPIMap, IndexMap.IPIMap, - ExpectedIpi->typeArray(), IpiHashes)) - fatal("codeview::mergeIdRecords failed: " + toString(std::move(Err))); + if (maybeIpi) { + auto ipiHashes = + GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes); + if (auto err = + mergeIdRecords(tMerger.globalIDTable, indexMap.tpiMap, + indexMap.ipiMap, maybeIpi->typeArray(), ipiHashes)) + fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); + } } else { // Merge TPI first, because the IPI stream will reference type indices. - if (auto Err = mergeTypeRecords(TypeTable, IndexMap.TPIMap, - ExpectedTpi->typeArray())) - fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err))); + if (auto err = mergeTypeRecords(tMerger.typeTable, indexMap.tpiMap, + expectedTpi->typeArray())) + fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); // Merge IPI. - if (auto Err = mergeIdRecords(IDTable, IndexMap.TPIMap, IndexMap.IPIMap, - ExpectedIpi->typeArray())) - fatal("codeview::mergeIdRecords failed: " + toString(std::move(Err))); + if (maybeIpi) { + if (auto err = mergeIdRecords(tMerger.iDTable, indexMap.tpiMap, + indexMap.ipiMap, maybeIpi->typeArray())) + fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); + } } - return IndexMap; + return indexMap; } -Expected -PDBLinker::mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType, - CVIndexMap *ObjectIndexMap) { - PrecompRecord Precomp; - if (auto EC = TypeDeserializer::deserializeAs(const_cast(FirstType), - Precomp)) - fatal("error reading record: " + toString(std::move(EC))); +Error PDBLinker::mergeInPrecompHeaderObj(ObjFile *file, + CVIndexMap *objectIndexMap) { + const PrecompRecord &precomp = + retrieveDependencyInfo(file->debugTypesObj); - auto E = aquirePrecompObj(File, Precomp); - if (!E) - return E.takeError(); + Expected e = aquirePrecompObj(file); + if (!e) + return e.takeError(); - const CVIndexMap &PrecompIndexMap = *E; - assert(PrecompIndexMap.IsPrecompiledTypeMap); + const CVIndexMap &precompIndexMap = *e; + assert(precompIndexMap.isPrecompiledTypeMap); - if (PrecompIndexMap.TPIMap.empty()) - return PrecompIndexMap; + if (precompIndexMap.tpiMap.empty()) + return Error::success(); - assert(Precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex); - assert(Precomp.getTypesCount() <= PrecompIndexMap.TPIMap.size()); + assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex); + assert(precomp.getTypesCount() <= precompIndexMap.tpiMap.size()); // Use the previously remapped index map from the precompiled headers. - ObjectIndexMap->TPIMap.append(PrecompIndexMap.TPIMap.begin(), - PrecompIndexMap.TPIMap.begin() + - Precomp.getTypesCount()); - return *ObjectIndexMap; + objectIndexMap->tpiMap.append(precompIndexMap.tpiMap.begin(), + precompIndexMap.tpiMap.begin() + + precomp.getTypesCount()); + return Error::success(); } static bool equals_path(StringRef path1, StringRef path2) { @@ -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 -PDBLinker::registerPrecompiledHeaders(uint32_t Signature) { - auto Insertion = PrecompTypeIndexMappings.insert({Signature, CVIndexMap()}); - CVIndexMap &IndexMap = Insertion.first->second; - if (!Insertion.second) - return {IndexMap, true}; +PDBLinker::registerPrecompiledHeaders(uint32_t signature) { + auto insertion = precompTypeIndexMappings.insert({signature, CVIndexMap()}); + CVIndexMap &indexMap = insertion.first->second; + if (!insertion.second) + return {indexMap, true}; // Mark this map as a precompiled types map. - IndexMap.IsPrecompiledTypeMap = true; - return {IndexMap, false}; + indexMap.isPrecompiledTypeMap = true; + return {indexMap, false}; } -Expected -PDBLinker::aquirePrecompObj(ObjFile *File, PrecompRecord Precomp) { +Expected PDBLinker::aquirePrecompObj(ObjFile *file) { + const PrecompRecord &precomp = + retrieveDependencyInfo(file->debugTypesObj); + // First, check if we already loaded the precompiled headers object with this // signature. Return the type index mapping if we've already seen it. - auto R = registerPrecompiledHeaders(Precomp.getSignature()); - if (R.second) - return R.first; + auto r = registerPrecompiledHeaders(precomp.getSignature()); + if (r.second) + return r.first; - CVIndexMap &IndexMap = R.first; + CVIndexMap &indexMap = r.first; // Cross-compile warning: given that Clang doesn't generate LF_PRECOMP // records, we assume the OBJ comes from a Windows build of cl.exe. Thusly, // the paths embedded in the OBJs are in the Windows format. - SmallString<128> PrecompFileName = sys::path::filename( - Precomp.getPrecompFilePath(), sys::path::Style::windows); + SmallString<128> precompFileName = sys::path::filename( + precomp.getPrecompFilePath(), sys::path::Style::windows); // link.exe requires that a precompiled headers object must always be provided // on the command-line, even if that's not necessary. - auto PrecompFile = findObjByName(PrecompFileName); - if (!PrecompFile) + auto precompFile = findObjByName(precompFileName); + if (!precompFile) return createFileError( - PrecompFileName.str(), + precompFileName.str(), make_error(pdb::pdb_error_code::external_cmdline_ref)); - addObjFile(PrecompFile, &IndexMap); + addObjFile(precompFile, &indexMap); - if (!PrecompFile->PCHSignature) - fatal(PrecompFile->getName() + " is not a precompiled headers object"); + if (!precompFile->pchSignature) + fatal(precompFile->getName() + " is not a precompiled headers object"); - if (Precomp.getSignature() != PrecompFile->PCHSignature.getValueOr(0)) + if (precomp.getSignature() != precompFile->pchSignature.getValueOr(0)) return createFileError( - Precomp.getPrecompFilePath().str(), + precomp.getPrecompFilePath().str(), make_error(pdb::pdb_error_code::signature_out_of_date)); - return IndexMap; + return indexMap; } -static bool remapTypeIndex(TypeIndex &TI, ArrayRef TypeIndexMap) { - if (TI.isSimple()) +static bool remapTypeIndex(TypeIndex &ti, ArrayRef typeIndexMap) { + if (ti.isSimple()) return true; - if (TI.toArrayIndex() >= TypeIndexMap.size()) + if (ti.toArrayIndex() >= typeIndexMap.size()) return false; - TI = TypeIndexMap[TI.toArrayIndex()]; + ti = typeIndexMap[ti.toArrayIndex()]; return true; } -static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind, - MutableArrayRef RecordBytes, - const CVIndexMap &IndexMap, - ArrayRef TypeRefs) { - MutableArrayRef Contents = - RecordBytes.drop_front(sizeof(RecordPrefix)); - for (const TiReference &Ref : TypeRefs) { - unsigned ByteSize = Ref.Count * sizeof(TypeIndex); - if (Contents.size() < Ref.Offset + ByteSize) +static void remapTypesInSymbolRecord(ObjFile *file, SymbolKind symKind, + MutableArrayRef recordBytes, + const CVIndexMap &indexMap, + ArrayRef typeRefs) { + MutableArrayRef contents = + recordBytes.drop_front(sizeof(RecordPrefix)); + for (const TiReference &ref : typeRefs) { + unsigned byteSize = ref.Count * sizeof(TypeIndex); + if (contents.size() < ref.Offset + byteSize) fatal("symbol record too short"); // This can be an item index or a type index. Choose the appropriate map. - ArrayRef TypeOrItemMap = IndexMap.TPIMap; - bool IsItemIndex = Ref.Kind == TiRefKind::IndexRef; - if (IsItemIndex && IndexMap.IsTypeServerMap) - TypeOrItemMap = IndexMap.IPIMap; - - MutableArrayRef TIs( - reinterpret_cast(Contents.data() + Ref.Offset), Ref.Count); - for (TypeIndex &TI : TIs) { - if (!remapTypeIndex(TI, TypeOrItemMap)) { - log("ignoring symbol record of kind 0x" + utohexstr(SymKind) + " in " + - File->getName() + " with bad " + (IsItemIndex ? "item" : "type") + - " index 0x" + utohexstr(TI.getIndex())); - TI = TypeIndex(SimpleTypeKind::NotTranslated); + ArrayRef typeOrItemMap = indexMap.tpiMap; + bool isItemIndex = ref.Kind == TiRefKind::IndexRef; + if (isItemIndex && indexMap.isTypeServerMap) + typeOrItemMap = indexMap.ipiMap; + + MutableArrayRef tIs( + reinterpret_cast(contents.data() + ref.Offset), ref.Count); + for (TypeIndex &ti : tIs) { + if (!remapTypeIndex(ti, typeOrItemMap)) { + log("ignoring symbol record of kind 0x" + utohexstr(symKind) + " in " + + file->getName() + " with bad " + (isItemIndex ? "item" : "type") + + " index 0x" + utohexstr(ti.getIndex())); + ti = TypeIndex(SimpleTypeKind::NotTranslated); continue; } } @@ -796,26 +619,26 @@ static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind, } static void -recordStringTableReferenceAtOffset(MutableArrayRef Contents, - uint32_t Offset, - std::vector &StrTableRefs) { - Contents = - Contents.drop_front(Offset).take_front(sizeof(support::ulittle32_t)); - ulittle32_t *Index = reinterpret_cast(Contents.data()); - StrTableRefs.push_back(Index); +recordStringTableReferenceAtOffset(MutableArrayRef contents, + uint32_t offset, + std::vector &strTableRefs) { + contents = + contents.drop_front(offset).take_front(sizeof(support::ulittle32_t)); + ulittle32_t *index = reinterpret_cast(contents.data()); + strTableRefs.push_back(index); } static void -recordStringTableReferences(SymbolKind Kind, MutableArrayRef Contents, - std::vector &StrTableRefs) { +recordStringTableReferences(SymbolKind kind, MutableArrayRef contents, + std::vector &strTableRefs) { // For now we only handle S_FILESTATIC, but we may need the same logic for // S_DEFRANGE and S_DEFRANGE_SUBFIELD. However, I cannot seem to generate any // PDBs that contain these types of records, so because of the uncertainty // they are omitted here until we can prove that it's necessary. - switch (Kind) { + switch (kind) { case SymbolKind::S_FILESTATIC: // FileStaticSym::ModFileOffset - recordStringTableReferenceAtOffset(Contents, 8, StrTableRefs); + recordStringTableReferenceAtOffset(contents, 8, strTableRefs); break; case SymbolKind::S_DEFRANGE: case SymbolKind::S_DEFRANGE_SUBFIELD: @@ -827,21 +650,21 @@ recordStringTableReferences(SymbolKind Kind, MutableArrayRef Contents, } } -static SymbolKind symbolKind(ArrayRef RecordData) { - const RecordPrefix *Prefix = - reinterpret_cast(RecordData.data()); - return static_cast(uint16_t(Prefix->RecordKind)); +static SymbolKind symbolKind(ArrayRef recordData) { + const RecordPrefix *prefix = + reinterpret_cast(recordData.data()); + return static_cast(uint16_t(prefix->RecordKind)); } /// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32 -static void translateIdSymbols(MutableArrayRef &RecordData, - TypeCollection &IDTable) { - RecordPrefix *Prefix = reinterpret_cast(RecordData.data()); +static void translateIdSymbols(MutableArrayRef &recordData, + TypeCollection &iDTable) { + RecordPrefix *prefix = reinterpret_cast(recordData.data()); - SymbolKind Kind = symbolKind(RecordData); + SymbolKind kind = symbolKind(recordData); - if (Kind == SymbolKind::S_PROC_ID_END) { - Prefix->RecordKind = SymbolKind::S_END; + if (kind == SymbolKind::S_PROC_ID_END) { + prefix->RecordKind = SymbolKind::S_END; return; } @@ -850,89 +673,89 @@ static void translateIdSymbols(MutableArrayRef &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 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(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 refs; + auto content = recordData.drop_front(sizeof(RecordPrefix)); + CVSymbol sym(recordData); + discoverTypeIndicesInSymbol(sym, refs); + assert(refs.size() == 1); + assert(refs.front().Count == 1); + + TypeIndex *ti = + reinterpret_cast(content.data() + refs[0].Offset); + // `ti` is the index of a FuncIdRecord or MemberFuncIdRecord which lives in // the IPI stream, whose `FunctionType` member refers to the TPI stream. // Note that LF_FUNC_ID and LF_MEMFUNC_ID have the same record layout, and // in both cases we just need the second type index. - if (!TI->isSimple() && !TI->isNoneType()) { - CVType FuncIdData = IDTable.getType(*TI); - SmallVector Indices; - discoverTypeIndices(FuncIdData, Indices); - assert(Indices.size() == 2); - *TI = Indices[1]; + if (!ti->isSimple() && !ti->isNoneType()) { + CVType funcIdData = iDTable.getType(*ti); + SmallVector indices; + discoverTypeIndices(funcIdData, indices); + assert(indices.size() == 2); + *ti = indices[1]; } - Kind = (Kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32 + kind = (kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32 : SymbolKind::S_LPROC32; - Prefix->RecordKind = uint16_t(Kind); + prefix->RecordKind = uint16_t(kind); } } /// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned. /// The object file may not be aligned. static MutableArrayRef -copyAndAlignSymbol(const CVSymbol &Sym, MutableArrayRef &AlignedMem) { - size_t Size = alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb)); - assert(Size >= 4 && "record too short"); - assert(Size <= MaxRecordLength && "record too long"); - assert(AlignedMem.size() >= Size && "didn't preallocate enough"); +copyAndAlignSymbol(const CVSymbol &sym, MutableArrayRef &alignedMem) { + size_t size = alignTo(sym.length(), alignOf(CodeViewContainer::Pdb)); + assert(size >= 4 && "record too short"); + assert(size <= MaxRecordLength && "record too long"); + assert(alignedMem.size() >= size && "didn't preallocate enough"); // Copy the symbol record and zero out any padding bytes. - MutableArrayRef NewData = AlignedMem.take_front(Size); - AlignedMem = AlignedMem.drop_front(Size); - memcpy(NewData.data(), Sym.data().data(), Sym.length()); - memset(NewData.data() + Sym.length(), 0, Size - Sym.length()); + MutableArrayRef newData = alignedMem.take_front(size); + alignedMem = alignedMem.drop_front(size); + memcpy(newData.data(), sym.data().data(), sym.length()); + memset(newData.data() + sym.length(), 0, size - sym.length()); // Update the record prefix length. It should point to the beginning of the // next record. - auto *Prefix = reinterpret_cast(NewData.data()); - Prefix->RecordLen = Size - 2; - return NewData; + auto *prefix = reinterpret_cast(newData.data()); + prefix->RecordLen = size - 2; + return newData; } struct ScopeRecord { - ulittle32_t PtrParent; - ulittle32_t PtrEnd; + ulittle32_t ptrParent; + ulittle32_t ptrEnd; }; struct SymbolScope { - ScopeRecord *OpeningRecord; - uint32_t ScopeOffset; + ScopeRecord *openingRecord; + uint32_t scopeOffset; }; -static void scopeStackOpen(SmallVectorImpl &Stack, - uint32_t CurOffset, CVSymbol &Sym) { - assert(symbolOpensScope(Sym.kind())); - SymbolScope S; - S.ScopeOffset = CurOffset; - S.OpeningRecord = const_cast( - reinterpret_cast(Sym.content().data())); - S.OpeningRecord->PtrParent = Stack.empty() ? 0 : Stack.back().ScopeOffset; - Stack.push_back(S); +static void scopeStackOpen(SmallVectorImpl &stack, + uint32_t curOffset, CVSymbol &sym) { + assert(symbolOpensScope(sym.kind())); + SymbolScope s; + s.scopeOffset = curOffset; + s.openingRecord = const_cast( + reinterpret_cast(sym.content().data())); + s.openingRecord->ptrParent = stack.empty() ? 0 : stack.back().scopeOffset; + stack.push_back(s); } -static void scopeStackClose(SmallVectorImpl &Stack, - uint32_t CurOffset, ObjFile *File) { - if (Stack.empty()) { - warn("symbol scopes are not balanced in " + File->getName()); +static void scopeStackClose(SmallVectorImpl &stack, + uint32_t curOffset, InputFile *file) { + if (stack.empty()) { + warn("symbol scopes are not balanced in " + file->getName()); return; } - SymbolScope S = Stack.pop_back_val(); - S.OpeningRecord->PtrEnd = CurOffset; + SymbolScope s = stack.pop_back_val(); + s.openingRecord->ptrEnd = curOffset; } -static bool symbolGoesInModuleStream(const CVSymbol &Sym, bool IsGlobalScope) { - switch (Sym.kind()) { +static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) { + switch (sym.kind()) { case SymbolKind::S_GDATA32: case SymbolKind::S_CONSTANT: // We really should not be seeing S_PROCREF and S_LPROCREF in the first place @@ -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 &StringTableRefs, - BinaryStreamRef SymData) { - ArrayRef SymsBuffer; - cantFail(SymData.readBytes(0, SymData.getLength(), SymsBuffer)); - SmallVector Scopes; +void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap, + std::vector &stringTableRefs, + BinaryStreamRef symData) { + ArrayRef symsBuffer; + cantFail(symData.readBytes(0, symData.getLength(), symsBuffer)); + SmallVector scopes; // Iterate every symbol to check if any need to be realigned, and if so, how // much space we need to allocate for them. - bool NeedsRealignment = false; - unsigned TotalRealignedSize = 0; - auto EC = forEachCodeViewRecord( - SymsBuffer, [&](CVSymbol Sym) -> llvm::Error { - unsigned RealignedSize = - alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb)); - NeedsRealignment |= RealignedSize != Sym.length(); - TotalRealignedSize += RealignedSize; + bool needsRealignment = false; + unsigned totalRealignedSize = 0; + auto ec = forEachCodeViewRecord( + symsBuffer, [&](CVSymbol sym) -> llvm::Error { + unsigned realignedSize = + alignTo(sym.length(), alignOf(CodeViewContainer::Pdb)); + needsRealignment |= realignedSize != sym.length(); + totalRealignedSize += realignedSize; return Error::success(); }); // If any of the symbol record lengths was corrupt, ignore them all, warn // about it, and move on. - if (EC) { - warn("corrupt symbol records in " + File->getName()); - consumeError(std::move(EC)); + if (ec) { + warn("corrupt symbol records in " + file->getName()); + consumeError(std::move(ec)); return; } // If any symbol needed realignment, allocate enough contiguous memory for // them all. Typically symbol subsections are small enough that this will not // cause fragmentation. - MutableArrayRef AlignedSymbolMem; - if (NeedsRealignment) { - void *AlignedData = - Alloc.Allocate(TotalRealignedSize, alignOf(CodeViewContainer::Pdb)); - AlignedSymbolMem = makeMutableArrayRef( - reinterpret_cast(AlignedData), TotalRealignedSize); + MutableArrayRef alignedSymbolMem; + if (needsRealignment) { + void *alignedData = + alloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb)); + alignedSymbolMem = makeMutableArrayRef( + reinterpret_cast(alignedData), totalRealignedSize); } // Iterate again, this time doing the real work. - unsigned CurSymOffset = File->ModuleDBI->getNextSymbolOffset(); - ArrayRef BulkSymbols; + unsigned curSymOffset = file->moduleDBI->getNextSymbolOffset(); + ArrayRef bulkSymbols; cantFail(forEachCodeViewRecord( - SymsBuffer, [&](CVSymbol Sym) -> llvm::Error { + symsBuffer, [&](CVSymbol sym) -> llvm::Error { // Align the record if required. - MutableArrayRef RecordBytes; - if (NeedsRealignment) { - RecordBytes = copyAndAlignSymbol(Sym, AlignedSymbolMem); - Sym = CVSymbol(Sym.kind(), RecordBytes); + MutableArrayRef recordBytes; + if (needsRealignment) { + recordBytes = copyAndAlignSymbol(sym, alignedSymbolMem); + sym = CVSymbol(recordBytes); } else { // Otherwise, we can actually mutate the symbol directly, since we // copied it to apply relocations. - RecordBytes = makeMutableArrayRef( - const_cast(Sym.data().data()), Sym.length()); + recordBytes = makeMutableArrayRef( + const_cast(sym.data().data()), sym.length()); } // Discover type index references in the record. Skip it if we don't // know where they are. - SmallVector TypeRefs; - if (!discoverTypeIndicesInSymbol(Sym, TypeRefs)) { + SmallVector typeRefs; + if (!discoverTypeIndicesInSymbol(sym, typeRefs)) { log("ignoring unknown symbol record with kind 0x" + - utohexstr(Sym.kind())); + utohexstr(sym.kind())); return Error::success(); } // Re-map all the type index references. - remapTypesInSymbolRecord(File, Sym.kind(), RecordBytes, IndexMap, - TypeRefs); + remapTypesInSymbolRecord(file, sym.kind(), recordBytes, indexMap, + typeRefs); // An object file may have S_xxx_ID symbols, but these get converted to // "real" symbols in a PDB. - translateIdSymbols(RecordBytes, getIDTable()); - Sym = CVSymbol(symbolKind(RecordBytes), RecordBytes); + translateIdSymbols(recordBytes, tMerger.getIDTable()); + sym = CVSymbol(recordBytes); // If this record refers to an offset in the object file's string table, // add that item to the global PDB string table and re-write the index. - recordStringTableReferences(Sym.kind(), RecordBytes, StringTableRefs); + recordStringTableReferences(sym.kind(), recordBytes, stringTableRefs); // Fill in "Parent" and "End" fields by maintaining a stack of scopes. - if (symbolOpensScope(Sym.kind())) - scopeStackOpen(Scopes, CurSymOffset, Sym); - else if (symbolEndsScope(Sym.kind())) - scopeStackClose(Scopes, CurSymOffset, File); + if (symbolOpensScope(sym.kind())) + scopeStackOpen(scopes, curSymOffset, sym); + else if (symbolEndsScope(sym.kind())) + scopeStackClose(scopes, curSymOffset, file); // Add the symbol to the globals stream if necessary. Do this before // adding the symbol to the module since we may need to get the next // symbol offset, and writing to the module's symbol stream will update // that offset. - if (symbolGoesInGlobalsStream(Sym, Scopes.empty())) - addGlobalSymbol(Builder.getGsiBuilder(), - File->ModuleDBI->getModuleIndex(), CurSymOffset, Sym); + if (symbolGoesInGlobalsStream(sym, scopes.empty())) { + addGlobalSymbol(builder.getGsiBuilder(), + file->moduleDBI->getModuleIndex(), curSymOffset, sym); + ++globalSymbols; + } - if (symbolGoesInModuleStream(Sym, Scopes.empty())) { + if (symbolGoesInModuleStream(sym, scopes.empty())) { // Add symbols to the module in bulk. If this symbol is contiguous // with the previous run of symbols to add, combine the ranges. If // not, close the previous range of symbols and start a new one. - if (Sym.data().data() == BulkSymbols.end()) { - BulkSymbols = makeArrayRef(BulkSymbols.data(), - BulkSymbols.size() + Sym.length()); + if (sym.data().data() == bulkSymbols.end()) { + bulkSymbols = makeArrayRef(bulkSymbols.data(), + bulkSymbols.size() + sym.length()); } else { - File->ModuleDBI->addSymbolsInBulk(BulkSymbols); - BulkSymbols = RecordBytes; + file->moduleDBI->addSymbolsInBulk(bulkSymbols); + bulkSymbols = recordBytes; } - CurSymOffset += Sym.length(); + curSymOffset += sym.length(); + ++moduleSymbols; } return Error::success(); })); // Add any remaining symbols we've accumulated. - File->ModuleDBI->addSymbolsInBulk(BulkSymbols); + file->moduleDBI->addSymbolsInBulk(bulkSymbols); } // Allocate memory for a .debug$S / .debug$F section and relocate it. -static ArrayRef relocateDebugChunk(BumpPtrAllocator &Alloc, - SectionChunk &DebugChunk) { - uint8_t *Buffer = Alloc.Allocate(DebugChunk.getSize()); - assert(DebugChunk.OutputSectionOff == 0 && +static ArrayRef relocateDebugChunk(BumpPtrAllocator &alloc, + SectionChunk &debugChunk) { + uint8_t *buffer = alloc.Allocate(debugChunk.getSize()); + assert(debugChunk.getOutputSectionIdx() == 0 && "debug sections should not be in output sections"); - DebugChunk.readRelocTargets(); - DebugChunk.writeTo(Buffer); - return makeArrayRef(Buffer, DebugChunk.getSize()); + debugChunk.writeTo(buffer); + return makeArrayRef(buffer, debugChunk.getSize()); } -static pdb::SectionContrib createSectionContrib(const Chunk *C, uint32_t Modi) { - OutputSection *OS = C->getOutputSection(); - pdb::SectionContrib SC; - memset(&SC, 0, sizeof(SC)); - SC.ISect = OS->SectionIndex; - SC.Off = C->getRVA() - OS->getRVA(); - SC.Size = C->getSize(); - if (auto *SecChunk = dyn_cast(C)) { - SC.Characteristics = SecChunk->Header->Characteristics; - SC.Imod = SecChunk->File->ModuleDBI->getModuleIndex(); - ArrayRef Contents = SecChunk->getContents(); - JamCRC CRC(0); - ArrayRef CharContents = makeArrayRef( - reinterpret_cast(Contents.data()), Contents.size()); - CRC.update(CharContents); - SC.DataCrc = CRC.getCRC(); +static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) { + OutputSection *os = c ? c->getOutputSection() : nullptr; + pdb::SectionContrib sc; + memset(&sc, 0, sizeof(sc)); + sc.ISect = os ? os->sectionIndex : llvm::pdb::kInvalidStreamIndex; + sc.Off = c && os ? c->getRVA() - os->getRVA() : 0; + sc.Size = c ? c->getSize() : -1; + if (auto *secChunk = dyn_cast_or_null(c)) { + sc.Characteristics = secChunk->header->Characteristics; + sc.Imod = secChunk->file->moduleDBI->getModuleIndex(); + ArrayRef contents = secChunk->getContents(); + JamCRC crc(0); + ArrayRef charContents = makeArrayRef( + reinterpret_cast(contents.data()), contents.size()); + crc.update(charContents); + sc.DataCrc = crc.getCRC(); } else { - SC.Characteristics = OS->Header.Characteristics; - // FIXME: When we start creating DBI for import libraries, use those here. - SC.Imod = Modi; + sc.Characteristics = os ? os->header.Characteristics : 0; + sc.Imod = modi; } - SC.RelocCrc = 0; // FIXME + sc.RelocCrc = 0; // FIXME - return SC; + return sc; } static uint32_t -translateStringTableIndex(uint32_t ObjIndex, - const DebugStringTableSubsectionRef &ObjStrTable, - DebugStringTableSubsection &PdbStrTable) { - auto ExpectedString = ObjStrTable.getString(ObjIndex); - if (!ExpectedString) { +translateStringTableIndex(uint32_t objIndex, + const DebugStringTableSubsectionRef &objStrTable, + DebugStringTableSubsection &pdbStrTable) { + auto expectedString = objStrTable.getString(objIndex); + if (!expectedString) { warn("Invalid string table reference"); - consumeError(ExpectedString.takeError()); + consumeError(expectedString.takeError()); return 0; } - return PdbStrTable.insert(*ExpectedString); + return pdbStrTable.insert(*expectedString); } -void DebugSHandler::handleDebugS(lld::coff::SectionChunk &DebugS) { - DebugSubsectionArray Subsections; +void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) { + DebugSubsectionArray subsections; + + ArrayRef relocatedDebugContents = SectionChunk::consumeDebugMagic( + relocateDebugChunk(linker.alloc, debugS), debugS.getSectionName()); - ArrayRef 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 +getFileName(const DebugStringTableSubsectionRef &strings, + const DebugChecksumsSubsectionRef &checksums, uint32_t fileID) { + auto iter = checksums.getArray().at(fileID); + if (iter == checksums.getArray().end()) + return make_error(cv_error_code::no_records); + uint32_t offset = iter->FileNameOffset; + return strings.getString(offset); +} + +std::shared_ptr +DebugSHandler::mergeInlineeLines(DebugChecksumsSubsection *newChecksums) { + auto newInlineeLines = std::make_shared( + *newChecksums, inlineeLines.hasExtraFiles()); + + for (const InlineeSourceLine &line : inlineeLines) { + TypeIndex inlinee = line.Header->Inlinee; + uint32_t fileID = line.Header->FileID; + uint32_t sourceLine = line.Header->SourceLineNum; + + ArrayRef typeOrItemMap = + indexMap.isTypeServerMap ? indexMap.ipiMap : indexMap.tpiMap; + if (!remapTypeIndex(inlinee, typeOrItemMap)) { + log("ignoring inlinee line record in " + file.getName() + + " with bad inlinee index 0x" + utohexstr(inlinee.getIndex())); + continue; + } + + SmallString<128> filename = + exitOnErr(getFileName(cVStrTab, checksums, fileID)); + pdbMakeAbsolute(filename); + newInlineeLines->addInlineSite(inlinee, filename, sourceLine); + + if (inlineeLines.hasExtraFiles()) { + for (uint32_t extraFileId : line.ExtraFiles) { + filename = exitOnErr(getFileName(cVStrTab, checksums, extraFileId)); + pdbMakeAbsolute(filename); + newInlineeLines->addExtraFile(filename); + } + } + } + + return newInlineeLines; +} + void DebugSHandler::finish() { - pdb::DbiStreamBuilder &DbiBuilder = Linker.Builder.getDbiBuilder(); + pdb::DbiStreamBuilder &dbiBuilder = linker.builder.getDbiBuilder(); // We should have seen all debug subsections across the entire object file now // which means that if a StringTable subsection and Checksums subsection were // present, now is the time to handle them. - if (!CVStrTab.valid()) { - if (Checksums.valid()) + if (!cVStrTab.valid()) { + if (checksums.valid()) fatal(".debug$S sections with a checksums subsection must also contain a " "string table subsection"); - if (!StringTableReferences.empty()) + if (!stringTableReferences.empty()) warn("No StringTable subsection was encountered, but there are string " "table references"); return; @@ -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(Linker.PDBStrTab); - for (FileChecksumEntry &FC : Checksums) { - SmallString<128> FileName = - ExitOnErr(CVStrTab.getString(FC.FileNameOffset)); - pdbMakeAbsolute(FileName); - ExitOnErr(Linker.Builder.getDbiBuilder().addModuleSourceFile( - *File.ModuleDBI, FileName)); - NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum); + auto newChecksums = make_unique(linker.pdbStrTab); + for (FileChecksumEntry &fc : checksums) { + SmallString<128> filename = + exitOnErr(cVStrTab.getString(fc.FileNameOffset)); + pdbMakeAbsolute(filename); + exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename)); + newChecksums->addChecksum(filename, fc.Kind, fc.Checksum); } - File.ModuleDBI->addDebugSubsection(std::move(NewChecksums)); + + // Rewrite inlinee item indices if present. + if (inlineeLines.valid()) + file.moduleDBI->addDebugSubsection(mergeInlineeLines(newChecksums.get())); + + file.moduleDBI->addDebugSubsection(std::move(newChecksums)); } -void PDBLinker::addObjFile(ObjFile *File, CVIndexMap *ExternIndexMap) { - if (File->wasProcessedForPDB()) +void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) { + if (file->mergedIntoPDB) return; - // Add a module descriptor for every object file. We need to put an absolute - // path to the object into the PDB. If this is a plain object, we make its - // path absolute. If it's an object in an archive, we make the archive path - // absolute. - bool InArchive = !File->ParentName.empty(); - SmallString<128> Path = InArchive ? File->ParentName : File->getName(); - pdbMakeAbsolute(Path); - StringRef Name = InArchive ? File->getName() : StringRef(Path); - - pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder(); - File->ModuleDBI = &ExitOnErr(DbiBuilder.addModuleInfo(Name)); - File->ModuleDBI->setObjFileName(Path); - - auto Chunks = File->getChunks(); - uint32_t Modi = File->ModuleDBI->getModuleIndex(); - for (Chunk *C : Chunks) { - auto *SecChunk = dyn_cast(C); - if (!SecChunk || !SecChunk->Live) - continue; - pdb::SectionContrib SC = createSectionContrib(SecChunk, Modi); - File->ModuleDBI->setFirstSectionContrib(SC); - break; - } + 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 RelocatedDebugContents = - relocateDebugChunk(Alloc, *DebugChunk); + if (debugChunk->getSectionName() == ".debug$F") { + ArrayRef relocatedDebugContents = + relocateDebugChunk(alloc, *debugChunk); - FixedStreamArray FpoRecords; - BinaryStreamReader Reader(RelocatedDebugContents, support::little); - uint32_t Count = RelocatedDebugContents.size() / sizeof(object::FpoData); - ExitOnErr(Reader.readArray(FpoRecords, Count)); + FixedStreamArray fpoRecords; + BinaryStreamReader reader(relocatedDebugContents, support::little); + uint32_t count = relocatedDebugContents.size() / sizeof(object::FpoData); + exitOnErr(reader.readArray(fpoRecords, count)); // These are already relocated and don't refer to the string table, so we // can just copy it. - for (const object::FpoData &FD : FpoRecords) - DbiBuilder.addOldFpoData(FD); + for (const object::FpoData &fd : fpoRecords) + dbiBuilder.addOldFpoData(fd); continue; } } // Do any post-processing now that all .debug$S sections have been processed. - DSH.finish(); + dsh.finish(); } -static PublicSym32 createPublic(Defined *Def) { - PublicSym32 Pub(SymbolKind::S_PUB32); - Pub.Name = Def->getName(); - if (auto *D = dyn_cast(Def)) { - if (D->getCOFFSymbol().isFunctionDefinition()) - Pub.Flags = PublicSymFlags::Function; - } else if (isa(Def)) { - Pub.Flags = PublicSymFlags::Function; +// Add a module descriptor for every object file. We need to put an absolute +// path to the object into the PDB. If this is a plain object, we make its +// path absolute. If it's an object in an archive, we make the archive path +// absolute. +static void createModuleDBI(pdb::PDBFileBuilder &builder) { + pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); + SmallString<128> objName; + + for (ObjFile *file : ObjFile::instances) { + + bool inArchive = !file->parentName.empty(); + objName = inArchive ? file->parentName : file->getName(); + pdbMakeAbsolute(objName); + StringRef modName = inArchive ? file->getName() : StringRef(objName); + + file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName)); + file->moduleDBI->setObjFileName(objName); + + ArrayRef chunks = file->getChunks(); + uint32_t modi = file->moduleDBI->getModuleIndex(); + + for (Chunk *c : chunks) { + auto *secChunk = dyn_cast(c); + if (!secChunk || !secChunk->live) + continue; + pdb::SectionContrib sc = createSectionContrib(secChunk, modi); + file->moduleDBI->setFirstSectionContrib(sc); + break; + } } +} - OutputSection *OS = Def->getChunk()->getOutputSection(); - assert(OS && "all publics should be in final image"); - Pub.Offset = Def->getRVA() - OS->getRVA(); - Pub.Segment = OS->SectionIndex; - return Pub; +static PublicSym32 createPublic(Defined *def) { + PublicSym32 pub(SymbolKind::S_PUB32); + pub.Name = def->getName(); + if (auto *d = dyn_cast(def)) { + if (d->getCOFFSymbol().isFunctionDefinition()) + pub.Flags = PublicSymFlags::Function; + } else if (isa(def)) { + pub.Flags = PublicSymFlags::Function; + } + + OutputSection *os = def->getChunk()->getOutputSection(); + assert(os && "all publics should be in final image"); + pub.Offset = def->getRVA() - os->getRVA(); + pub.Segment = os->sectionIndex; + return pub; } // Add all object files to the PDB. Merge .debug$T sections into IpiData and // TpiData. void PDBLinker::addObjectsToPDB() { - ScopedTimer T1(AddObjectsTimer); - for (ObjFile *File : ObjFile::Instances) - addObjFile(File); + ScopedTimer t1(addObjectsTimer); + + 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 Publics; - Symtab->forEachSymbol([&Publics](Symbol *S) { + auto &gsiBuilder = builder.getGsiBuilder(); + std::vector publics; + symtab->forEachSymbol([&publics](Symbol *s) { // Only emit defined, live symbols that have a chunk. - auto *Def = dyn_cast(S); - if (Def && Def->isLive() && Def->getChunk()) - Publics.push_back(createPublic(Def)); + auto *def = dyn_cast(s); + if (def && def->isLive() && def->getChunk()) + publics.push_back(createPublic(def)); }); - if (!Publics.empty()) { + if (!publics.empty()) { + publicSymbols = publics.size(); // Sort the public symbols and add them to the stream. - sort(parallel::par, Publics.begin(), Publics.end(), - [](const PublicSym32 &L, const PublicSym32 &R) { - return L.Name < R.Name; - }); - for (const PublicSym32 &Pub : Publics) - GsiBuilder.addPublicSymbol(Pub); + parallelSort(publics, [](const PublicSym32 &l, const PublicSym32 &r) { + return l.Name < r.Name; + }); + for (const PublicSym32 &pub : publics) + gsiBuilder.addPublicSymbol(pub); } } +void PDBLinker::printStats() { + if (!config->showSummary) + return; + + SmallString<256> buffer; + raw_svector_ostream stream(buffer); + + stream << center_justify("Summary", 80) << '\n' + << std::string(80, '-') << '\n'; + + auto print = [&](uint64_t v, StringRef s) { + stream << format_decimal(v, 15) << " " << s << '\n'; + }; + + print(ObjFile::instances.size(), + "Input OBJ files (expanded from all cmd-line inputs)"); + print(typeServerIndexMappings.size(), "PDB type server dependencies"); + print(precompTypeIndexMappings.size(), "Precomp OBJ dependencies"); + print(tMerger.getTypeTable().size() + tMerger.getIDTable().size(), + "Merged TPI records"); + print(pdbStrTab.size(), "Output PDB strings"); + print(globalSymbols, "Global symbol records"); + print(moduleSymbols, "Module symbol records"); + print(publicSymbols, "Public symbol records"); + + message(buffer); +} + void PDBLinker::addNatvisFiles() { - for (StringRef File : Config->NatvisFiles) { - ErrorOr> DataOrErr = - MemoryBuffer::getFile(File); - if (!DataOrErr) { - warn("Cannot open input file: " + File); + for (StringRef file : config->natvisFiles) { + ErrorOr> dataOrErr = + MemoryBuffer::getFile(file); + if (!dataOrErr) { + warn("Cannot open input file: " + file); continue; } - Builder.addInjectedSource(File, std::move(*DataOrErr)); + builder.addInjectedSource(file, std::move(*dataOrErr)); } } -static codeview::CPUType toCodeViewMachine(COFF::MachineTypes Machine) { - switch (Machine) { +static codeview::CPUType toCodeViewMachine(COFF::MachineTypes machine) { + switch (machine) { case COFF::IMAGE_FILE_MACHINE_AMD64: return codeview::CPUType::X64; case COFF::IMAGE_FILE_MACHINE_ARM: @@ -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 Args) { - std::string R; - R.reserve(256); - for (StringRef A : Args) { - if (!R.empty()) - R.push_back(' '); - bool HasWS = A.find(' ') != StringRef::npos; - bool HasQ = A.find('"') != StringRef::npos; - if (HasWS || HasQ) - R.push_back('"'); - if (HasQ) { - SmallVector S; - A.split(S, '"'); - R.append(join(S, "\"\"")); +static std::string quote(ArrayRef args) { + std::string r; + r.reserve(256); + for (StringRef a : args) { + if (!r.empty()) + r.push_back(' '); + bool hasWS = a.find(' ') != StringRef::npos; + bool hasQ = a.find('"') != StringRef::npos; + if (hasWS || hasQ) + r.push_back('"'); + if (hasQ) { + SmallVector s; + a.split(s, '"'); + r.append(join(s, "\"\"")); } else { - R.append(A); + r.append(a); } - if (HasWS || HasQ) - R.push_back('"'); + if (hasWS || hasQ) + r.push_back('"'); } - return R; + return r; } -static void addCommonLinkerModuleSymbols(StringRef Path, - pdb::DbiModuleDescriptorBuilder &Mod, - BumpPtrAllocator &Allocator) { - ObjNameSym ONS(SymbolRecordKind::ObjNameSym); - Compile3Sym CS(SymbolRecordKind::Compile3Sym); - EnvBlockSym EBS(SymbolRecordKind::EnvBlockSym); - - ONS.Name = "* Linker *"; - ONS.Signature = 0; - - CS.Machine = toCodeViewMachine(Config->Machine); +static void fillLinkerVerRecord(Compile3Sym &cs) { + cs.Machine = toCodeViewMachine(config->machine); // Interestingly, if we set the string to 0.0.0.0, then when trying to view // local variables WinDbg emits an error that private symbols are not present. // By setting this to a valid MSVC linker version string, local variables are // displayed properly. As such, even though it is not representative of // LLVM's version information, we need this for compatibility. - CS.Flags = CompileSym3Flags::None; - CS.VersionBackendBuild = 25019; - CS.VersionBackendMajor = 14; - CS.VersionBackendMinor = 10; - CS.VersionBackendQFE = 0; + cs.Flags = CompileSym3Flags::None; + cs.VersionBackendBuild = 25019; + cs.VersionBackendMajor = 14; + cs.VersionBackendMinor = 10; + cs.VersionBackendQFE = 0; // MSVC also sets the frontend to 0.0.0.0 since this is specifically for the // linker module (which is by definition a backend), so we don't need to do // anything here. Also, it seems we can use "LLVM Linker" for the linker name // without any problems. Only the backend version has to be hardcoded to a // magic number. - CS.VersionFrontendBuild = 0; - CS.VersionFrontendMajor = 0; - CS.VersionFrontendMinor = 0; - CS.VersionFrontendQFE = 0; - CS.Version = "LLVM Linker"; - CS.setLanguage(SourceLanguage::Link); - - ArrayRef 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 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 outputSections) { + if (ImportFile::instances.empty()) + return; + + std::map dllToModuleDbi; + + for (ImportFile *file : ImportFile::instances) { + if (!file->live) + continue; + + if (!file->thunkSym) + continue; + + if (!file->thunkLive) + continue; + + std::string dll = StringRef(file->dllName).lower(); + llvm::pdb::DbiModuleDescriptorBuilder *&mod = dllToModuleDbi[dll]; + if (!mod) { + pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); + SmallString<128> libPath = file->parentName; + pdbMakeAbsolute(libPath); + sys::path::native(libPath); + + // Name modules similar to MSVC's link.exe. + // The first module is the simple dll filename + llvm::pdb::DbiModuleDescriptorBuilder &firstMod = + exitOnErr(dbiBuilder.addModuleInfo(file->dllName)); + firstMod.setObjFileName(libPath); + pdb::SectionContrib sc = + createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex); + firstMod.setFirstSectionContrib(sc); + + // The second module is where the import stream goes. + mod = &exitOnErr(dbiBuilder.addModuleInfo("Import:" + file->dllName)); + mod->setObjFileName(libPath); + } + + DefinedImportThunk *thunk = cast(file->thunkSym); + Chunk *thunkChunk = thunk->getChunk(); + OutputSection *thunkOS = thunkChunk->getOutputSection(); + + ObjNameSym ons(SymbolRecordKind::ObjNameSym); + Compile3Sym cs(SymbolRecordKind::Compile3Sym); + Thunk32Sym ts(SymbolRecordKind::Thunk32Sym); + ScopeEndSym es(SymbolRecordKind::ScopeEndSym); + + ons.Name = file->dllName; + ons.Signature = 0; + + fillLinkerVerRecord(cs); + + ts.Name = thunk->getName(); + ts.Parent = 0; + ts.End = 0; + ts.Next = 0; + ts.Thunk = ThunkOrdinal::Standard; + ts.Length = thunkChunk->getSize(); + ts.Segment = thunkOS->sectionIndex; + ts.Offset = thunkChunk->getRVA() - thunkOS->getRVA(); + + mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( + ons, alloc, CodeViewContainer::Pdb)); + mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol( + cs, alloc, CodeViewContainer::Pdb)); + + SmallVector scopes; + CVSymbol newSym = codeview::SymbolSerializer::writeOneSymbol( + ts, alloc, CodeViewContainer::Pdb); + scopeStackOpen(scopes, mod->getNextSymbolOffset(), newSym); + + mod->addSymbol(newSym); + + newSym = codeview::SymbolSerializer::writeOneSymbol(es, alloc, + CodeViewContainer::Pdb); + scopeStackClose(scopes, mod->getNextSymbolOffset(), file); + + mod->addSymbol(newSym); + + pdb::SectionContrib sc = + createSectionContrib(thunk->getChunk(), mod->getModuleIndex()); + mod->setFirstSectionContrib(sc); + } } // Creates a PDB file. -void coff::createPDB(SymbolTable *Symtab, - ArrayRef OutputSections, - ArrayRef SectionTable, - llvm::codeview::DebugInfo *BuildId) { - ScopedTimer T1(TotalPdbLinkTimer); - PDBLinker PDB(Symtab); - - 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 outputSections, + ArrayRef 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 OutputSections, - ArrayRef SectionTable) { +void PDBLinker::addSections(ArrayRef outputSections, + ArrayRef sectionTable) { // It's not entirely clear what this is, but the * Linker * module uses it. - pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder(); - NativePath = Config->PDBPath; - pdbMakeAbsolute(NativePath); - uint32_t PdbFilePathNI = DbiBuilder.addECName(NativePath); - auto &LinkerModule = ExitOnErr(DbiBuilder.addModuleInfo("* Linker *")); - LinkerModule.setPdbFilePathNI(PdbFilePathNI); - addCommonLinkerModuleSymbols(NativePath, LinkerModule, Alloc); + pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder(); + nativePath = config->pdbPath; + pdbMakeAbsolute(nativePath); + uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath); + auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *")); + linkerModule.setPdbFilePathNI(pdbFilePathNI); + addCommonLinkerModuleSymbols(nativePath, linkerModule, alloc); // Add section contributions. They must be ordered by ascending RVA. - for (OutputSection *OS : OutputSections) { - addLinkerModuleSectionSymbol(LinkerModule, *OS, Alloc); - for (Chunk *C : OS->Chunks) { - pdb::SectionContrib SC = - createSectionContrib(C, LinkerModule.getModuleIndex()); - Builder.getDbiBuilder().addSectionContrib(SC); + for (OutputSection *os : outputSections) { + addLinkerModuleSectionSymbol(linkerModule, *os, alloc); + for (Chunk *c : os->chunks) { + pdb::SectionContrib sc = + createSectionContrib(c, linkerModule.getModuleIndex()); + builder.getDbiBuilder().addSectionContrib(sc); } } + // The * Linker * first section contrib is only used along with /INCREMENTAL, + // to provide trampolines thunks for incremental function patching. Set this + // as "unused" because LLD doesn't support /INCREMENTAL link. + pdb::SectionContrib sc = + createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex); + linkerModule.setFirstSectionContrib(sc); + // Add Section Map stream. - ArrayRef Sections = { - (const object::coff_section *)SectionTable.data(), - SectionTable.size() / sizeof(object::coff_section)}; - SectionMap = pdb::DbiStreamBuilder::createSectionMap(Sections); - DbiBuilder.setSectionMap(SectionMap); + ArrayRef sections = { + (const object::coff_section *)sectionTable.data(), + sectionTable.size() / sizeof(object::coff_section)}; + sectionMap = pdb::DbiStreamBuilder::createSectionMap(sections); + dbiBuilder.setSectionMap(sectionMap); // Add COFF section header stream. - ExitOnErr( - DbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, SectionTable)); + exitOnErr( + dbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, sectionTable)); } -void PDBLinker::commit(codeview::GUID *Guid) { +void PDBLinker::commit(codeview::GUID *guid) { // Write to a file. - ExitOnErr(Builder.commit(Config->PDBPath, Guid)); -} - -static Expected -getFileName(const DebugStringTableSubsectionRef &Strings, - const DebugChecksumsSubsectionRef &Checksums, uint32_t FileID) { - auto Iter = Checksums.getArray().at(FileID); - if (Iter == Checksums.getArray().end()) - return make_error(cv_error_code::no_records); - uint32_t Offset = Iter->FileNameOffset; - return Strings.getString(Offset); + 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 Secrels; - for (const coff_relocation &R : DbgC->Relocs) { - if (R.Type != SecrelReloc) + // Build a mapping of SECREL relocations in dbgC that refer to `c`. + DenseMap secrels; + for (const coff_relocation &r : dbgC->getRelocs()) { + if (r.Type != secrelReloc) continue; - if (auto *S = dyn_cast_or_null( - C->File->getSymbols()[R.SymbolTableIndex])) - if (S->getChunk() == C) - Secrels[R.VirtualAddress] = S->getValue(); + if (auto *s = dyn_cast_or_null( + c->file->getSymbols()[r.SymbolTableIndex])) + if (s->getChunk() == c) + secrels[r.VirtualAddress] = s->getValue(); } - ArrayRef Contents = - consumeDebugMagic(DbgC->getContents(), ".debug$S"); - DebugSubsectionArray Subsections; - BinaryStreamReader Reader(Contents, support::little); - ExitOnErr(Reader.readArray(Subsections, Contents.size())); + ArrayRef contents = + SectionChunk::consumeDebugMagic(dbgC->getContents(), ".debug$S"); + DebugSubsectionArray subsections; + BinaryStreamReader reader(contents, support::little); + exitOnErr(reader.readArray(subsections, contents.size())); - for (const DebugSubsectionRecord &SS : Subsections) { - switch (SS.kind()) { + for (const DebugSubsectionRecord &ss : subsections) { + switch (ss.kind()) { case DebugSubsectionKind::StringTable: { - assert(!CVStrTab.valid() && + assert(!cVStrTab.valid() && "Encountered multiple string table subsections!"); - ExitOnErr(CVStrTab.initialize(SS.getRecordData())); + exitOnErr(cVStrTab.initialize(ss.getRecordData())); break; } case DebugSubsectionKind::FileChecksums: - assert(!Checksums.valid() && + assert(!checksums.valid() && "Encountered multiple checksum subsections!"); - ExitOnErr(Checksums.initialize(SS.getRecordData())); + exitOnErr(checksums.initialize(ss.getRecordData())); break; case DebugSubsectionKind::Lines: { - ArrayRef Bytes; - auto Ref = SS.getRecordData(); - ExitOnErr(Ref.readLongestContiguousChunk(0, Bytes)); - size_t OffsetInDbgC = Bytes.data() - DbgC->getContents().data(); + ArrayRef bytes; + auto ref = ss.getRecordData(); + exitOnErr(ref.readLongestContiguousChunk(0, bytes)); + size_t offsetInDbgC = bytes.data() - dbgC->getContents().data(); // Check whether this line table refers to C. - auto I = Secrels.find(OffsetInDbgC); - if (I == Secrels.end()) + auto i = secrels.find(offsetInDbgC); + if (i == secrels.end()) break; // Check whether this line table covers Addr in C. - DebugLinesSubsectionRef LinesTmp; - ExitOnErr(LinesTmp.initialize(BinaryStreamReader(Ref))); - uint32_t OffsetInC = I->second + LinesTmp.header()->RelocOffset; - if (Addr < OffsetInC || Addr >= OffsetInC + LinesTmp.header()->CodeSize) + DebugLinesSubsectionRef linesTmp; + exitOnErr(linesTmp.initialize(BinaryStreamReader(ref))); + uint32_t offsetInC = i->second + linesTmp.header()->RelocOffset; + if (addr < offsetInC || addr >= offsetInC + linesTmp.header()->CodeSize) break; - assert(!Lines.header() && + assert(!lines.header() && "Encountered multiple line tables for function!"); - ExitOnErr(Lines.initialize(BinaryStreamReader(Ref))); - OffsetInLinetable = Addr - OffsetInC; + exitOnErr(lines.initialize(BinaryStreamReader(ref))); + offsetInLinetable = addr - offsetInC; break; } default: break; } - if (CVStrTab.valid() && Checksums.valid() && Lines.header()) + if (cVStrTab.valid() && checksums.valid() && lines.header()) return true; } } @@ -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 coff::getFileLine(const SectionChunk *C, - uint32_t Addr) { - ExitOnError ExitOnErr; +std::pair coff::getFileLine(const SectionChunk *c, + uint32_t addr) { + ExitOnError exitOnErr; - DebugStringTableSubsectionRef CVStrTab; - DebugChecksumsSubsectionRef Checksums; - DebugLinesSubsectionRef Lines; - uint32_t OffsetInLinetable; + DebugStringTableSubsectionRef cVStrTab; + DebugChecksumsSubsectionRef checksums; + DebugLinesSubsectionRef lines; + uint32_t offsetInLinetable; - if (!findLineTable(C, Addr, CVStrTab, Checksums, Lines, OffsetInLinetable)) + if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable)) return {"", 0}; - Optional NameIndex; - Optional LineNumber; - for (LineColumnEntry &Entry : Lines) { - for (const LineNumberEntry &LN : Entry.LineNumbers) { - LineInfo LI(LN.Flags); - if (LN.Offset > OffsetInLinetable) { - if (!NameIndex) { - NameIndex = Entry.NameIndex; - LineNumber = LI.getStartLine(); + Optional nameIndex; + Optional lineNumber; + for (LineColumnEntry &entry : lines) { + for (const LineNumberEntry &ln : entry.LineNumbers) { + LineInfo li(ln.Flags); + if (ln.Offset > offsetInLinetable) { + if (!nameIndex) { + nameIndex = entry.NameIndex; + lineNumber = li.getStartLine(); } - StringRef Filename = - ExitOnErr(getFileName(CVStrTab, Checksums, *NameIndex)); - return {Filename, *LineNumber}; + StringRef filename = + exitOnErr(getFileName(cVStrTab, checksums, *nameIndex)); + return {filename, *lineNumber}; } - NameIndex = Entry.NameIndex; - LineNumber = LI.getStartLine(); + nameIndex = entry.NameIndex; + lineNumber = li.getStartLine(); } } - if (!NameIndex) + if (!nameIndex) return {"", 0}; - StringRef Filename = ExitOnErr(getFileName(CVStrTab, Checksums, *NameIndex)); - return {Filename, *LineNumber}; + StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex)); + return {filename, *lineNumber}; } diff --git a/COFF/PDB.h b/COFF/PDB.h index ea7a999..3ac1adc 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 OutputSections, - llvm::ArrayRef SectionTable, - llvm::codeview::DebugInfo *BuildId); +void createPDB(SymbolTable *symtab, + llvm::ArrayRef outputSections, + llvm::ArrayRef sectionTable, + llvm::codeview::DebugInfo *buildId); -std::pair getFileLine(const SectionChunk *C, - uint32_t Addr); +std::pair getFileLine(const SectionChunk *c, + uint32_t addr); } } diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp index 1a9e045..0aff164 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 @@ -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(File)) { - ObjFile::Instances.push_back(F); - } else if (auto *F = dyn_cast(File)) { - BitcodeFile::Instances.push_back(F); - } else if (auto *F = dyn_cast(File)) { - ImportFile::Instances.push_back(F); + if (auto *f = dyn_cast(file)) { + ObjFile::instances.push_back(f); + } else if (auto *f = dyn_cast(file)) { + BitcodeFile::instances.push_back(f); + } else if (auto *f = dyn_cast(file)) { + ImportFile::instances.push_back(f); } - StringRef S = File->getDirectives(); - if (S.empty()) - return; - - log("Directives: " + toString(File) + ": " + S); - Driver->parseDirectives(S); + driver->parseDirectives(file); } -static void errorOrWarn(const Twine &S) { - if (Config->ForceUnresolved) - warn(S); +static void errorOrWarn(const Twine &s) { + if (config->forceUnresolved) + warn(s); else - error(S); + error(s); } // Returns the symbol in SC whose value is <= Addr that is closest to Addr. // This is generally the global variable or function whose definition contains // Addr. -static Symbol *getSymbol(SectionChunk *SC, uint32_t Addr) { - DefinedRegular *Candidate = nullptr; +static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) { + DefinedRegular *candidate = nullptr; - for (Symbol *S : SC->File->getSymbols()) { - auto *D = dyn_cast_or_null(S); - if (!D || D->getChunk() != SC || D->getValue() > Addr || - (Candidate && D->getValue() < Candidate->getValue())) + for (Symbol *s : sc->file->getSymbols()) { + auto *d = dyn_cast_or_null(s); + if (!d || !d->data || d->getChunk() != sc || d->getValue() > addr || + (candidate && d->getValue() < candidate->getValue())) continue; - Candidate = D; + candidate = d; } - return Candidate; + return candidate; } -std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) { +// Given a file and the index of a symbol in that file, returns a description +// of all references to that symbol from that file. If no debug information is +// available, returns just the name of the file, else one string per actual +// reference as described in the debug info. +std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex) { struct Location { - Symbol *Sym; - std::pair FileLine; + Symbol *sym; + std::pair fileLine; }; - std::vector Locations; + std::vector locations; - for (Chunk *C : File->getChunks()) { - auto *SC = dyn_cast(C); - if (!SC) + for (Chunk *c : file->getChunks()) { + auto *sc = dyn_cast(c); + if (!sc) continue; - for (const coff_relocation &R : SC->Relocs) { - if (R.SymbolTableIndex != SymIndex) + for (const coff_relocation &r : sc->getRelocs()) { + if (r.SymbolTableIndex != symIndex) continue; - std::pair FileLine = - getFileLine(SC, R.VirtualAddress); - Symbol *Sym = getSymbol(SC, R.VirtualAddress); - if (!FileLine.first.empty() || Sym) - Locations.push_back({Sym, FileLine}); + std::pair fileLine = + getFileLine(sc, r.VirtualAddress); + Symbol *sym = getSymbol(sc, r.VirtualAddress); + if (!fileLine.first.empty() || sym) + locations.push_back({sym, fileLine}); } } - if (Locations.empty()) - return "\n>>> referenced by " + toString(File); + if (locations.empty()) + return std::vector({"\n>>> referenced by " + toString(file)}); - std::string Out; - llvm::raw_string_ostream OS(Out); - for (Location Loc : Locations) { - OS << "\n>>> referenced by "; - if (!Loc.FileLine.first.empty()) - OS << Loc.FileLine.first << ":" << Loc.FileLine.second + std::vector symbolLocations(locations.size()); + size_t i = 0; + for (Location loc : locations) { + llvm::raw_string_ostream os(symbolLocations[i++]); + os << "\n>>> referenced by "; + if (!loc.fileLine.first.empty()) + os << loc.fileLine.first << ":" << loc.fileLine.second << "\n>>> "; - OS << toString(File); - if (Loc.Sym) - OS << ":(" << toString(*Loc.Sym) << ')'; + os << toString(file); + if (loc.sym) + os << ":(" << toString(*loc.sym) << ')'; + } + return symbolLocations; +} + +// For an undefined symbol, stores all files referencing it and the index of +// the undefined symbol in each file. +struct UndefinedDiag { + Symbol *sym; + struct File { + ObjFile *oFile; + uint64_t symIndex; + }; + std::vector files; +}; + +static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) { + std::string out; + llvm::raw_string_ostream os(out); + os << "undefined symbol: " << toString(*undefDiag.sym); + + const size_t maxUndefReferences = 10; + size_t i = 0, numRefs = 0; + for (const UndefinedDiag::File &ref : undefDiag.files) { + std::vector symbolLocations = + getSymbolLocations(ref.oFile, ref.symIndex); + numRefs += symbolLocations.size(); + for (const std::string &s : symbolLocations) { + if (i >= maxUndefReferences) + break; + os << s; + i++; + } } - 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(Sym); - if (!Undef) + for (auto &i : symMap) { + Symbol *sym = i.second; + auto *undef = dyn_cast(sym); + if (!undef) continue; - if (!Sym->IsUsedInRegularObj) + if (!sym->isUsedInRegularObj) continue; - StringRef Name = Undef->getName(); + StringRef name = undef->getName(); - if (Name.startswith("__imp_")) + if (name.startswith("__imp_")) continue; // If we have an undefined symbol, but we have a Lazy representing a // symbol we could load from file, make sure to load that. - Lazy *L = dyn_cast_or_null(find(("__imp_" + Name).str())); - if (!L || L->PendingArchiveLoad) + Lazy *l = dyn_cast_or_null(find(("__imp_" + name).str())); + if (!l || l->pendingArchiveLoad) continue; - log("Loading lazy " + L->getName() + " from " + L->File->getName() + + log("Loading lazy " + l->getName() + " from " + l->file->getName() + " for automatic import"); - L->PendingArchiveLoad = true; - L->File->addMember(&L->Sym); + l->pendingArchiveLoad = true; + l->file->addMember(&l->sym); } } -bool SymbolTable::handleMinGWAutomaticImport(Symbol *Sym, StringRef Name) { - if (Name.startswith("__imp_")) +bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) { + if (name.startswith("__imp_")) return false; - Defined *Imp = dyn_cast_or_null(find(("__imp_" + Name).str())); - if (!Imp) + Defined *imp = dyn_cast_or_null(find(("__imp_" + name).str())); + if (!imp) return false; // Replace the reference directly to a variable with a reference // to the import address table instead. This obviously isn't right, - // but we mark the symbol as IsRuntimePseudoReloc, and a later pass + // but we mark the symbol as isRuntimePseudoReloc, and a later pass // will add runtime pseudo relocations for every relocation against // this Symbol. The runtime pseudo relocation framework expects the // reference itself to point at the IAT entry. - size_t ImpSize = 0; - if (isa(Imp)) { - log("Automatically importing " + Name + " from " + - cast(Imp)->getDLLName()); - ImpSize = sizeof(DefinedImportData); - } else if (isa(Imp)) { - log("Automatically importing " + Name + " from " + - toString(cast(Imp)->File)); - ImpSize = sizeof(DefinedRegular); + size_t impSize = 0; + if (isa(imp)) { + log("Automatically importing " + name + " from " + + cast(imp)->getDLLName()); + impSize = sizeof(DefinedImportData); + } else if (isa(imp)) { + log("Automatically importing " + name + " from " + + toString(cast(imp)->file)); + impSize = sizeof(DefinedRegular); } else { - warn("unable to automatically import " + Name + " from " + Imp->getName() + - " from " + toString(cast(Imp)->File) + + warn("unable to automatically import " + name + " from " + imp->getName() + + " from " + toString(cast(imp)->file) + "; unexpected symbol type"); return false; } - Sym->replaceKeepingName(Imp, ImpSize); - Sym->IsRuntimePseudoReloc = true; + sym->replaceKeepingName(imp, impSize); + sym->isRuntimePseudoReloc = true; // There may exist symbols named .refptr. which only consist // of a single pointer to . If it turns out is // automatically imported, we don't need to keep the .refptr. // pointer at all, but redirect all accesses to it to the IAT entry // for __imp_ instead, and drop the whole .refptr. chunk. - DefinedRegular *Refptr = - dyn_cast_or_null(find((".refptr." + Name).str())); - if (Refptr && Refptr->getChunk()->getSize() == Config->Wordsize) { - SectionChunk *SC = dyn_cast_or_null(Refptr->getChunk()); - if (SC && SC->Relocs.size() == 1 && *SC->symbols().begin() == Sym) { - log("Replacing .refptr." + Name + " with " + Imp->getName()); - Refptr->getChunk()->Live = false; - Refptr->replaceKeepingName(Imp, ImpSize); + DefinedRegular *refptr = + dyn_cast_or_null(find((".refptr." + name).str())); + if (refptr && refptr->getChunk()->getSize() == config->wordsize) { + SectionChunk *sc = dyn_cast_or_null(refptr->getChunk()); + if (sc && sc->getRelocs().size() == 1 && *sc->symbols().begin() == sym) { + log("Replacing .refptr." + name + " with " + imp->getName()); + refptr->getChunk()->live = false; + refptr->replaceKeepingName(imp, impSize); } } return true; } void SymbolTable::reportRemainingUndefines() { - SmallPtrSet Undefs; - DenseMap LocalImports; + SmallPtrSet undefs; + DenseMap localImports; - for (auto &I : SymMap) { - Symbol *Sym = I.second; - auto *Undef = dyn_cast(Sym); - if (!Undef) + for (auto &i : symMap) { + Symbol *sym = i.second; + auto *undef = dyn_cast(sym); + if (!undef) continue; - if (!Sym->IsUsedInRegularObj) + if (!sym->isUsedInRegularObj) continue; - StringRef Name = Undef->getName(); + StringRef name = undef->getName(); // A weak alias may have been resolved, so check for that. - if (Defined *D = Undef->getWeakAlias()) { + if (Defined *d = undef->getWeakAlias()) { // We want to replace Sym with D. However, we can't just blindly // copy sizeof(SymbolUnion) bytes from D to Sym because D may be an // internal symbol, and internal symbols are stored as "unparented" // Symbols. For that reason we need to check which type of symbol we // are dealing with and copy the correct number of bytes. - if (isa(D)) - memcpy(Sym, D, sizeof(DefinedRegular)); - else if (isa(D)) - memcpy(Sym, D, sizeof(DefinedAbsolute)); + if (isa(d)) + memcpy(sym, d, sizeof(DefinedRegular)); + else if (isa(d)) + memcpy(sym, d, sizeof(DefinedAbsolute)); else - memcpy(Sym, D, sizeof(SymbolUnion)); + memcpy(sym, d, sizeof(SymbolUnion)); continue; } // If we can resolve a symbol by removing __imp_ prefix, do that. // This odd rule is for compatibility with MSVC linker. - if (Name.startswith("__imp_")) { - Symbol *Imp = find(Name.substr(strlen("__imp_"))); - if (Imp && isa(Imp)) { - auto *D = cast(Imp); - replaceSymbol(Sym, Name, D); - LocalImportChunks.push_back(cast(Sym)->getChunk()); - LocalImports[Sym] = D; + if (name.startswith("__imp_")) { + Symbol *imp = find(name.substr(strlen("__imp_"))); + if (imp && isa(imp)) { + auto *d = cast(imp); + replaceSymbol(sym, name, d); + localImportChunks.push_back(cast(sym)->getChunk()); + localImports[sym] = d; continue; } } // We don't want to report missing Microsoft precompiled headers symbols. // A proper message will be emitted instead in PDBLinker::aquirePrecompObj - if (Name.contains("_PchSym_")) + if (name.contains("_PchSym_")) continue; - if (Config->MinGW && handleMinGWAutomaticImport(Sym, Name)) + if (config->mingw && handleMinGWAutomaticImport(sym, name)) continue; // Remaining undefined symbols are not fatal if /force is specified. // They are replaced with dummy defined symbols. - if (Config->ForceUnresolved) - replaceSymbol(Sym, Name, 0); - Undefs.insert(Sym); + if (config->forceUnresolved) + replaceSymbol(sym, name, 0); + undefs.insert(sym); } - if (Undefs.empty() && LocalImports.empty()) + if (undefs.empty() && localImports.empty()) return; - for (Symbol *B : Config->GCRoot) { - if (Undefs.count(B)) - errorOrWarn(": undefined symbol: " + toString(*B)); - if (Config->WarnLocallyDefinedImported) - if (Symbol *Imp = LocalImports.lookup(B)) - warn(": locally defined symbol imported: " + toString(*Imp) + - " (defined in " + toString(Imp->getFile()) + ") [LNK4217]"); + for (Symbol *b : config->gcroot) { + if (undefs.count(b)) + errorOrWarn(": undefined symbol: " + toString(*b)); + if (config->warnLocallyDefinedImported) + if (Symbol *imp = localImports.lookup(b)) + warn(": locally defined symbol imported: " + toString(*imp) + + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); } - for (ObjFile *File : ObjFile::Instances) { - size_t SymIndex = (size_t)-1; - for (Symbol *Sym : File->getSymbols()) { - ++SymIndex; - if (!Sym) + std::vector undefDiags; + DenseMap firstDiag; + + for (ObjFile *file : ObjFile::instances) { + size_t symIndex = (size_t)-1; + for (Symbol *sym : file->getSymbols()) { + ++symIndex; + if (!sym) continue; - if (Undefs.count(Sym)) - errorOrWarn("undefined symbol: " + toString(*Sym) + - getSymbolLocations(File, SymIndex)); - if (Config->WarnLocallyDefinedImported) - if (Symbol *Imp = LocalImports.lookup(Sym)) - warn(toString(File) + - ": locally defined symbol imported: " + toString(*Imp) + - " (defined in " + toString(Imp->getFile()) + ") [LNK4217]"); + if (undefs.count(sym)) { + auto it = firstDiag.find(sym); + if (it == firstDiag.end()) { + firstDiag[sym] = undefDiags.size(); + undefDiags.push_back({sym, {{file, symIndex}}}); + } else { + undefDiags[it->second].files.push_back({file, symIndex}); + } + } + if (config->warnLocallyDefinedImported) + if (Symbol *imp = localImports.lookup(sym)) + warn(toString(file) + + ": locally defined symbol imported: " + toString(*imp) + + " (defined in " + toString(imp->getFile()) + ") [LNK4217]"); } } + + for (const UndefinedDiag& undefDiag : undefDiags) + reportUndefinedSymbol(undefDiag); } -std::pair SymbolTable::insert(StringRef Name) { - bool Inserted = false; - Symbol *&Sym = SymMap[CachedHashStringRef(Name)]; - if (!Sym) { - Sym = reinterpret_cast(make()); - Sym->IsUsedInRegularObj = false; - Sym->PendingArchiveLoad = false; - Inserted = true; +std::pair SymbolTable::insert(StringRef name) { + bool inserted = false; + Symbol *&sym = symMap[CachedHashStringRef(name)]; + if (!sym) { + sym = reinterpret_cast(make()); + sym->isUsedInRegularObj = false; + sym->pendingArchiveLoad = false; + inserted = true; } - return {Sym, Inserted}; + return {sym, inserted}; } -std::pair SymbolTable::insert(StringRef Name, InputFile *File) { - std::pair Result = insert(Name); - if (!File || !isa(File)) - Result.first->IsUsedInRegularObj = true; - return Result; +std::pair SymbolTable::insert(StringRef name, InputFile *file) { + std::pair result = insert(name); + if (!file || !isa(file)) + result.first->isUsedInRegularObj = true; + return result; } -Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F, - bool IsWeakAlias) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(Name, F); - if (WasInserted || (isa(S) && IsWeakAlias)) { - replaceSymbol(S, Name); - return S; +Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f, + bool isWeakAlias) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name, f); + if (wasInserted || (isa(s) && isWeakAlias)) { + replaceSymbol(s, name); + return s; } - if (auto *L = dyn_cast(S)) { - if (!S->PendingArchiveLoad) { - S->PendingArchiveLoad = true; - L->File->addMember(&L->Sym); + if (auto *l = dyn_cast(s)) { + if (!s->pendingArchiveLoad) { + s->pendingArchiveLoad = true; + l->file->addMember(&l->sym); } } - return S; + return s; } -void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) { - StringRef Name = Sym.getName(); - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(Name); - if (WasInserted) { - replaceSymbol(S, F, Sym); +void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol sym) { + StringRef name = sym.getName(); + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name); + if (wasInserted) { + replaceSymbol(s, f, sym); return; } - auto *U = dyn_cast(S); - if (!U || U->WeakAlias || S->PendingArchiveLoad) + auto *u = dyn_cast(s); + if (!u || u->weakAlias || s->pendingArchiveLoad) return; - S->PendingArchiveLoad = true; - F->addMember(&Sym); + s->pendingArchiveLoad = true; + f->addMember(&sym); } -void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) { - std::string Msg = "duplicate symbol: " + toString(*Existing) + " in " + - toString(Existing->getFile()) + " and in " + - toString(NewFile); +void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) { + std::string msg = "duplicate symbol: " + toString(*existing) + " in " + + toString(existing->getFile()) + " and in " + + toString(newFile); - if (Config->ForceMultiple) - warn(Msg); + if (config->forceMultiple) + warn(msg); else - error(Msg); + error(msg); } -Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, nullptr); - S->IsUsedInRegularObj = true; - if (WasInserted || isa(S) || isa(S)) - replaceSymbol(S, N, Sym); - else if (!isa(S)) - reportDuplicate(S, nullptr); - return S; +Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) + replaceSymbol(s, n, sym); + else if (!isa(s)) + reportDuplicate(s, nullptr); + return s; } -Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, nullptr); - S->IsUsedInRegularObj = true; - if (WasInserted || isa(S) || isa(S)) - replaceSymbol(S, N, VA); - else if (!isa(S)) - reportDuplicate(S, nullptr); - return S; +Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) + replaceSymbol(s, n, va); + else if (!isa(s)) + reportDuplicate(s, nullptr); + return s; } -Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, nullptr); - S->IsUsedInRegularObj = true; - if (WasInserted || isa(S) || isa(S)) - replaceSymbol(S, N, C); - else if (!isa(S)) - reportDuplicate(S, nullptr); - return S; +Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) + replaceSymbol(s, n, c); + else if (!isa(s)) + reportDuplicate(s, nullptr); + return s; } -Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, - const coff_symbol_generic *Sym, - SectionChunk *C) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, F); - if (WasInserted || !isa(S)) - replaceSymbol(S, F, N, /*IsCOMDAT*/ false, - /*IsExternal*/ true, Sym, C); +Symbol *SymbolTable::addRegular(InputFile *f, StringRef n, + const coff_symbol_generic *sym, + SectionChunk *c) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, f); + if (wasInserted || !isa(s)) + replaceSymbol(s, f, n, /*IsCOMDAT*/ false, + /*IsExternal*/ true, sym, c); else - reportDuplicate(S, F); - return S; + reportDuplicate(s, f); + return s; } -std::pair -SymbolTable::addComdat(InputFile *F, StringRef N, - const coff_symbol_generic *Sym) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, F); - if (WasInserted || !isa(S)) { - replaceSymbol(S, F, N, /*IsCOMDAT*/ true, - /*IsExternal*/ true, Sym, nullptr); - return {S, true}; +std::pair +SymbolTable::addComdat(InputFile *f, StringRef n, + const coff_symbol_generic *sym) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, f); + if (wasInserted || !isa(s)) { + replaceSymbol(s, f, n, /*IsCOMDAT*/ true, + /*IsExternal*/ true, sym, nullptr); + return {cast(s), true}; } - if (!cast(S)->isCOMDAT()) - reportDuplicate(S, F); - return {S, false}; + auto *existingSymbol = cast(s); + if (!existingSymbol->isCOMDAT) + reportDuplicate(s, f); + return {existingSymbol, false}; } -Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size, - const coff_symbol_generic *Sym, CommonChunk *C) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, F); - if (WasInserted || !isa(S)) - replaceSymbol(S, F, N, Size, Sym, C); - else if (auto *DC = dyn_cast(S)) - if (Size > DC->getSize()) - replaceSymbol(S, F, N, Size, Sym, C); - return S; +Symbol *SymbolTable::addCommon(InputFile *f, StringRef n, uint64_t size, + const coff_symbol_generic *sym, CommonChunk *c) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, f); + if (wasInserted || !isa(s)) + replaceSymbol(s, f, n, size, sym, c); + else if (auto *dc = dyn_cast(s)) + if (size > dc->getSize()) + replaceSymbol(s, f, n, size, sym, c); + return s; } -Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, nullptr); - S->IsUsedInRegularObj = true; - if (WasInserted || isa(S) || isa(S)) { - replaceSymbol(S, N, F); - return S; +Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) { + replaceSymbol(s, n, f); + return s; } - reportDuplicate(S, F); + reportDuplicate(s, f); return nullptr; } -Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID, - uint16_t Machine) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(Name, nullptr); - S->IsUsedInRegularObj = true; - if (WasInserted || isa(S) || isa(S)) { - replaceSymbol(S, Name, ID, Machine); - return S; +Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id, + uint16_t machine) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name, nullptr); + s->isUsedInRegularObj = true; + if (wasInserted || isa(s) || isa(s)) { + replaceSymbol(s, name, id, machine); + return s; } - reportDuplicate(S, ID->File); + reportDuplicate(s, id->file); return nullptr; } std::vector SymbolTable::getChunks() { - std::vector Res; - for (ObjFile *File : ObjFile::Instances) { - ArrayRef V = File->getChunks(); - Res.insert(Res.end(), V.begin(), V.end()); + std::vector res; + for (ObjFile *file : ObjFile::instances) { + ArrayRef v = file->getChunks(); + res.insert(res.end(), v.begin(), v.end()); } - return Res; + return res; } -Symbol *SymbolTable::find(StringRef Name) { - return SymMap.lookup(CachedHashStringRef(Name)); +Symbol *SymbolTable::find(StringRef name) { + return symMap.lookup(CachedHashStringRef(name)); } -Symbol *SymbolTable::findUnderscore(StringRef Name) { - if (Config->Machine == I386) - return find(("_" + Name).str()); - return find(Name); +Symbol *SymbolTable::findUnderscore(StringRef name) { + if (config->machine == I386) + return find(("_" + name).str()); + return find(name); } -StringRef SymbolTable::findByPrefix(StringRef Prefix) { - for (auto Pair : SymMap) { - StringRef Name = Pair.first.val(); - if (Name.startswith(Prefix)) - return Name; +// Return all symbols that start with Prefix, possibly ignoring the first +// character of Prefix or the first character symbol. +std::vector SymbolTable::getSymsWithPrefix(StringRef prefix) { + std::vector syms; + for (auto pair : symMap) { + StringRef name = pair.first.val(); + if (name.startswith(prefix) || name.startswith(prefix.drop_front()) || + name.drop_front().startswith(prefix) || + name.drop_front().startswith(prefix.drop_front())) { + syms.push_back(pair.second); + } } - return ""; + return syms; } -StringRef SymbolTable::findMangle(StringRef Name) { - if (Symbol *Sym = find(Name)) - if (!isa(Sym)) - return Name; - if (Config->Machine != I386) - return findByPrefix(("?" + Name + "@@Y").str()); - if (!Name.startswith("_")) - return ""; +Symbol *SymbolTable::findMangle(StringRef name) { + if (Symbol *sym = find(name)) + if (!isa(sym)) + return sym; + + // Efficient fuzzy string lookup is impossible with a hash table, so iterate + // the symbol table once and collect all possibly matching symbols into this + // vector. Then compare each possibly matching symbol with each possible + // mangling. + std::vector syms = getSymsWithPrefix(name); + auto findByPrefix = [&syms](const Twine &t) -> Symbol * { + std::string prefix = t.str(); + for (auto *s : syms) + if (s->getName().startswith(prefix)) + return s; + return nullptr; + }; + + // For non-x86, just look for C++ functions. + if (config->machine != I386) + return findByPrefix("?" + name + "@@Y"); + + if (!name.startswith("_")) + return nullptr; // Search for x86 stdcall function. - StringRef S = findByPrefix((Name + "@").str()); - if (!S.empty()) - return S; + if (Symbol *s = findByPrefix(name + "@")) + return s; // Search for x86 fastcall function. - S = findByPrefix(("@" + Name.substr(1) + "@").str()); - if (!S.empty()) - return S; + if (Symbol *s = findByPrefix("@" + name.substr(1) + "@")) + return s; // Search for x86 vectorcall function. - S = findByPrefix((Name.substr(1) + "@@").str()); - if (!S.empty()) - return S; + if (Symbol *s = findByPrefix(name.substr(1) + "@@")) + return s; // Search for x86 C++ non-member function. - return findByPrefix(("?" + Name.substr(1) + "@@Y").str()); -} - -void SymbolTable::mangleMaybe(Symbol *B) { - auto *U = dyn_cast(B); - if (!U || U->WeakAlias) - return; - StringRef Alias = findMangle(U->getName()); - if (!Alias.empty()) { - log(U->getName() + " aliased to " + Alias); - U->WeakAlias = addUndefined(Alias); - } + 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 SymbolTable::compileBitcodeFiles() { - LTO.reset(new BitcodeCompiler); - for (BitcodeFile *F : BitcodeFile::Instances) - LTO->add(*F); - return LTO->compile(); + lto.reset(new BitcodeCompiler); + for (BitcodeFile *f : BitcodeFile::instances) + lto->add(*f); + return lto->compile(); } void SymbolTable::addCombinedLTOObjects() { - if (BitcodeFile::Instances.empty()) + if (BitcodeFile::instances.empty()) return; - ScopedTimer T(LTOTimer); - for (StringRef Object : compileBitcodeFiles()) { - auto *Obj = make(MemoryBufferRef(Object, "lto.tmp")); - Obj->parse(); - ObjFile::Instances.push_back(Obj); + ScopedTimer t(ltoTimer); + for (StringRef object : compileBitcodeFiles()) { + auto *obj = make(MemoryBufferRef(object, "lto.tmp")); + obj->parse(); + ObjFile::instances.push_back(obj); } } diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h index 00e55db..88f47cb 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 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 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 - 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 + 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 LocalImportChunks; + std::vector localImportChunks; // Iterates symbols in non-determinstic hash table order. - template void forEachSymbol(T Callback) { - for (auto &Pair : SymMap) - Callback(Pair.second); + template void forEachSymbol(T callback) { + for (auto &pair : symMap) + callback(pair.second); } private: /// Inserts symbol if not already present. - std::pair insert(StringRef Name); - /// Same as insert(Name), but also sets IsUsedInRegularObj. - std::pair insert(StringRef Name, InputFile *F); - StringRef findByPrefix(StringRef Prefix); + std::pair insert(StringRef name); + /// Same as insert(Name), but also sets isUsedInRegularObj. + std::pair insert(StringRef name, InputFile *f); + + std::vector getSymsWithPrefix(StringRef prefix); - llvm::DenseMap SymMap; - std::unique_ptr LTO; + llvm::DenseMap symMap; + std::unique_ptr lto; }; -extern SymbolTable *Symtab; +extern SymbolTable *symtab; -std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex); +std::vector getSymbolLocations(ObjFile *file, uint32_t symIndex); } // namespace coff } // namespace lld diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp index ccaf864..3583d4c 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 S = lld::demangleMSVC(B.getName())) - return ("\"" + *S + "\" (" + B.getName() + ")").str(); - return B.getName(); +std::string lld::toString(coff::Symbol &b) { + if (config->demangle) + if (Optional s = lld::demangleMSVC(b.getName())) + return *s; + return b.getName(); } namespace lld { @@ -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(this); - cast(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name); + if (nameData == nullptr) { + auto *d = cast(this); + StringRef nameStr; + cast(d->file)->getCOFFObj()->getSymbolName(d->sym, nameStr); + nameData = nameStr.data(); + nameSize = nameStr.size(); + assert(nameSize == nameStr.size() && "name length truncated"); } - return Name; + return StringRef(nameData, nameSize); } InputFile *Symbol::getFile() { - if (auto *Sym = dyn_cast(this)) - return Sym->File; - if (auto *Sym = dyn_cast(this)) - return Sym->File; + if (auto *sym = dyn_cast(this)) + return sym->file; + if (auto *sym = dyn_cast(this)) + return sym->file; return nullptr; } bool Symbol::isLive() const { - if (auto *R = dyn_cast(this)) - return R->getChunk()->Live; - if (auto *Imp = dyn_cast(this)) - return Imp->File->Live; - if (auto *Imp = dyn_cast(this)) - return Imp->WrappedSym->File->ThunkLive; + if (auto *r = dyn_cast(this)) + return r->getChunk()->live; + if (auto *imp = dyn_cast(this)) + return imp->file->live; + if (auto *imp = dyn_cast(this)) + return imp->wrappedSym->file->thunkLive; // Assume any other kind of symbol is live. return true; } // MinGW specific. -void Symbol::replaceKeepingName(Symbol *Other, size_t Size) { - StringRef OrigName = Name; - memcpy(this, Other, Size); - Name = OrigName; +void Symbol::replaceKeepingName(Symbol *other, size_t size) { + StringRef origName = getName(); + memcpy(this, other, size); + nameData = origName.data(); + nameSize = origName.size(); } COFFSymbolRef DefinedCOFF::getCOFFSymbol() { - size_t SymSize = cast(File)->getCOFFObj()->getSymbolTableEntrySize(); - if (SymSize == sizeof(coff_symbol16)) - return COFFSymbolRef(reinterpret_cast(Sym)); - assert(SymSize == sizeof(coff_symbol32)); - return COFFSymbolRef(reinterpret_cast(Sym)); + size_t symSize = cast(file)->getCOFFObj()->getSymbolTableEntrySize(); + if (symSize == sizeof(coff_symbol16)) + return COFFSymbolRef(reinterpret_cast(sym)); + assert(symSize == sizeof(coff_symbol32)); + return COFFSymbolRef(reinterpret_cast(sym)); } -uint16_t DefinedAbsolute::NumOutputSections; +uint16_t DefinedAbsolute::numOutputSections; -static Chunk *makeImportThunk(DefinedImportData *S, uint16_t Machine) { - if (Machine == AMD64) - return make(S); - if (Machine == I386) - return make(S); - if (Machine == ARM64) - return make(S); - assert(Machine == ARMNT); - return make(S); +static Chunk *makeImportThunk(DefinedImportData *s, uint16_t machine) { + if (machine == AMD64) + return make(s); + if (machine == I386) + return make(s); + if (machine == ARM64) + return make(s); + assert(machine == ARMNT); + return make(s); } -DefinedImportThunk::DefinedImportThunk(StringRef Name, DefinedImportData *S, - uint16_t Machine) - : Defined(DefinedImportThunkKind, Name), WrappedSym(S), - Data(makeImportThunk(S, Machine)) {} +DefinedImportThunk::DefinedImportThunk(StringRef name, DefinedImportData *s, + uint16_t machine) + : Defined(DefinedImportThunkKind, name), wrappedSym(s), + data(makeImportThunk(s, machine)) {} Defined *Undefined::getWeakAlias() { // A weak alias may be a weak alias to another symbol, so check recursively. - for (Symbol *A = WeakAlias; A; A = cast(A)->WeakAlias) - if (auto *D = dyn_cast(A)) - return D; + for (Symbol *a = weakAlias; a; a = cast(a)->weakAlias) + if (auto *d = dyn_cast(a)) + return d; return nullptr; } } // namespace coff diff --git a/COFF/Symbols.h b/COFF/Symbols.h index 4a8693e..86cd4f5 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(SymbolKind); } - - // Returns true if this is an external symbol. - bool isExternal() { return IsExternal; } + Kind kind() const { return static_cast(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(S)) {} + DefinedLocalImport(StringRef n, Defined *s) + : Defined(DefinedLocalImportKind, n), data(make(s)) {} - static bool classof(const Symbol *S) { - return S->kind() == DefinedLocalImportKind; + static bool classof(const Symbol *s) { + return s->kind() == DefinedLocalImportKind; } - uint64_t getRVA() { return Data->getRVA(); } - Chunk *getChunk() { return Data; } + uint64_t getRVA() { return data->getRVA(); } + Chunk *getChunk() { return data; } private: - LocalImportChunk *Data; + LocalImportChunk *data; }; inline uint64_t Defined::getRVA() { @@ -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 -void replaceSymbol(Symbol *S, ArgT &&... Arg) { +void replaceSymbol(Symbol *s, ArgT &&... arg) { static_assert(std::is_trivially_destructible(), "Symbol types must be trivially destructible"); static_assert(sizeof(T) <= sizeof(SymbolUnion), "Symbol too small"); @@ -426,11 +425,11 @@ void replaceSymbol(Symbol *S, ArgT &&... Arg) { "SymbolUnion not aligned enough"); assert(static_cast(static_cast(nullptr)) == nullptr && "Not a Symbol"); - new (S) T(std::forward(Arg)...); + new (s) T(std::forward(arg)...); } } // namespace coff -std::string toString(coff::Symbol &B); +std::string toString(coff::Symbol &b); } // namespace lld #endif diff --git a/COFF/TypeMerger.h b/COFF/TypeMerger.h new file mode 100644 index 0000000..e2cfe66 --- /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 tpiMap; + llvm::SmallVector 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 258796e..36ef87d 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 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 &R, bool WriteRepro) - : Records(R), WriteRepro(WriteRepro) {} + DebugDirectoryChunk(const std::vector &r, bool writeRepro) + : records(r), writeRepro(writeRepro) {} size_t getSize() const override { - return (Records.size() + int(WriteRepro)) * sizeof(debug_directory); + return (records.size() + int(writeRepro)) * sizeof(debug_directory); } - void writeTo(uint8_t *B) const override { - auto *D = reinterpret_cast(B + OutputSectionOff); + void writeTo(uint8_t *b) const override { + auto *d = reinterpret_cast(b); - for (const Chunk *Record : Records) { - OutputSection *OS = Record->getOutputSection(); - uint64_t Offs = OS->getFileOff() + (Record->getRVA() - OS->getRVA()); - fillEntry(D, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, Record->getSize(), - Record->getRVA(), Offs); - ++D; + for (const Chunk *record : records) { + OutputSection *os = record->getOutputSection(); + uint64_t offs = os->getFileOff() + (record->getRVA() - os->getRVA()); + fillEntry(d, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, record->getSize(), + record->getRVA(), offs); + ++d; } - if (WriteRepro) { + if (writeRepro) { // FIXME: The COFF spec allows either a 0-sized entry to just say // "the timestamp field is really a hash", or a 4-byte size field // followed by that many bytes containing a longer hash (with the // lowest 4 bytes usually being the timestamp in little-endian order). // Consider storing the full 8 bytes computed by xxHash64 here. - fillEntry(D, COFF::IMAGE_DEBUG_TYPE_REPRO, 0, 0, 0); + fillEntry(d, COFF::IMAGE_DEBUG_TYPE_REPRO, 0, 0, 0); } } - void setTimeDateStamp(uint32_t TimeDateStamp) { - for (support::ulittle32_t *TDS : TimeDateStamps) - *TDS = TimeDateStamp; + void setTimeDateStamp(uint32_t timeDateStamp) { + for (support::ulittle32_t *tds : timeDateStamps) + *tds = timeDateStamp; } private: - void fillEntry(debug_directory *D, COFF::DebugType DebugType, size_t Size, - uint64_t RVA, uint64_t Offs) const { - D->Characteristics = 0; - D->TimeDateStamp = 0; - D->MajorVersion = 0; - D->MinorVersion = 0; - D->Type = DebugType; - D->SizeOfData = Size; - D->AddressOfRawData = RVA; - D->PointerToRawData = Offs; - - TimeDateStamps.push_back(&D->TimeDateStamp); - } - - mutable std::vector TimeDateStamps; - const std::vector &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 timeDateStamps; + const std::vector &records; + bool writeRepro; }; -class CVDebugRecordChunk : public Chunk { +class CVDebugRecordChunk : public NonSectionChunk { public: size_t getSize() const override { - return sizeof(codeview::DebugInfo) + Config->PDBAltPath.size() + 1; + return sizeof(codeview::DebugInfo) + config->pdbAltPath.size() + 1; } - void writeTo(uint8_t *B) const override { + void writeTo(uint8_t *b) const override { // Save off the DebugInfo entry to backfill the file signature (build id) // in Writer::writeBuildId - BuildId = reinterpret_cast(B + OutputSectionOff); + buildId = reinterpret_cast(b); // variable sized field (PDB Path) - char *P = reinterpret_cast(B + OutputSectionOff + sizeof(*BuildId)); - if (!Config->PDBAltPath.empty()) - memcpy(P, Config->PDBAltPath.data(), Config->PDBAltPath.size()); - P[Config->PDBAltPath.size()] = '\0'; + char *p = reinterpret_cast(b + sizeof(*buildId)); + if (!config->pdbAltPath.empty()) + memcpy(p, config->pdbAltPath.data(), config->pdbAltPath.size()); + p[config->pdbAltPath.size()] = '\0'; } - mutable codeview::DebugInfo *BuildId = nullptr; + mutable codeview::DebugInfo *buildId = nullptr; +}; + +// PartialSection represents a group of chunks that contribute to an +// OutputSection. Collating a collection of PartialSections of same name and +// characteristics constitutes the OutputSection. +class PartialSectionKey { +public: + StringRef name; + unsigned characteristics; + + bool operator<(const PartialSectionKey &other) const { + int c = name.compare(other.name); + if (c == 1) + return false; + if (c == 0) + return characteristics < other.characteristics; + return true; + } }; // The writer writes a SymbolTable result to a file. class Writer { public: - Writer() : Buffer(errorHandler().OutputBuffer) {} + Writer() : buffer(errorHandler().outputBuffer) {} void run(); private: @@ -168,78 +193,81 @@ private: void createMiscChunks(); void createImportTables(); void appendImportThunks(); - void locateImportTables( - std::map, std::vector> &Map); + void locateImportTables(); void createExportTable(); void mergeSections(); - void readRelocTargets(); void removeUnusedSections(); void assignAddresses(); void finalizeAddresses(); void removeEmptySections(); + void assignOutputSectionIndices(); void createSymbolAndStringTable(); - void openFile(StringRef OutputPath); + void openFile(StringRef outputPath); template void writeHeader(); void createSEHTable(); void createRuntimePseudoRelocs(); void insertCtorDtorSymbols(); void createGuardCFTables(); - void markSymbolsForRVATable(ObjFile *File, - ArrayRef SymIdxChunks, - SymbolRVASet &TableSymbols); - void maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym, - StringRef CountSym); + void markSymbolsForRVATable(ObjFile *file, + ArrayRef symIdxChunks, + SymbolRVASet &tableSymbols); + void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, + StringRef countSym); void setSectionPermissions(); void writeSections(); void writeBuildId(); void sortExceptionTable(); - void sortCRTSectionChunks(std::vector &Chunks); + void sortCRTSectionChunks(std::vector &chunks); + void addSyntheticIdata(); + void fixPartialSectionChars(StringRef name, uint32_t chars); + bool fixGnuImportChunks(); + PartialSection *createPartialSection(StringRef name, uint32_t outChars); + PartialSection *findPartialSection(StringRef name, uint32_t outChars); - llvm::Optional createSymbol(Defined *D); - size_t addEntryToStringTable(StringRef Str); + llvm::Optional createSymbol(Defined *d); + size_t addEntryToStringTable(StringRef str); - OutputSection *findSection(StringRef Name); + OutputSection *findSection(StringRef name); void addBaserels(); - void addBaserelBlocks(std::vector &V); + void addBaserelBlocks(std::vector &v); uint32_t getSizeOfInitializedData(); - std::map> binImports(); - - std::unique_ptr &Buffer; - std::vector OutputSections; - std::vector Strtab; - std::vector OutputSymtab; - IdataContents Idata; - Chunk *ImportTableStart = nullptr; - uint64_t ImportTableSize = 0; - Chunk *IATStart = nullptr; - uint64_t IATSize = 0; - DelayLoadContents DelayIdata; - EdataContents Edata; - bool SetNoSEHCharacteristic = false; - - DebugDirectoryChunk *DebugDirectory = nullptr; - std::vector DebugRecords; - CVDebugRecordChunk *BuildId = nullptr; - ArrayRef 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 &buffer; + std::map partialSections; + std::vector strtab; + std::vector outputSymtab; + IdataContents idata; + Chunk *importTableStart = nullptr; + uint64_t importTableSize = 0; + Chunk *iatStart = nullptr; + uint64_t iatSize = 0; + DelayLoadContents delayIdata; + EdataContents edata; + bool setNoSEHCharacteristic = false; + + DebugDirectoryChunk *debugDirectory = nullptr; + std::vector debugRecords; + CVDebugRecordChunk *buildId = nullptr; + ArrayRef sectionTable; + + 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(Buf); - *Hdr = Header; - if (StringTableOff) { +void OutputSection::writeHeaderTo(uint8_t *buf) { + auto *hdr = reinterpret_cast(buf); + *hdr = header; + if (stringTableOff) { // If name is too long, write offset into the string table as a name. - sprintf(Hdr->Name, "/%d", StringTableOff); + sprintf(hdr->Name, "/%d", stringTableOff); } else { - assert(!Config->Debug || Name.size() <= COFF::NameSize || - (Hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0); - strncpy(Hdr->Name, Name.data(), - std::min(Name.size(), (size_t)COFF::NameSize)); + assert(!config->debug || name.size() <= COFF::NameSize || + (hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0); + strncpy(hdr->Name, name.data(), + std::min(name.size(), (size_t)COFF::NameSize)); } } +void OutputSection::addContributingPartialSection(PartialSection *sec) { + contribSections.push_back(sec); +} + } // namespace coff } // namespace lld // Check whether the target address S is in range from a relocation -// of type RelType at address P. -static bool isInRange(uint16_t RelType, uint64_t S, uint64_t P, int Margin) { - assert(Config->Machine == ARMNT); - int64_t Diff = AbsoluteDifference(S, P + 4) + Margin; - switch (RelType) { - case IMAGE_REL_ARM_BRANCH20T: - return isInt<21>(Diff); - case IMAGE_REL_ARM_BRANCH24T: - case IMAGE_REL_ARM_BLX23T: - return isInt<25>(Diff); - default: - return true; +// of type relType at address P. +static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) { + if (config->machine == ARMNT) { + int64_t diff = AbsoluteDifference(s, p + 4) + margin; + switch (relType) { + case IMAGE_REL_ARM_BRANCH20T: + return isInt<21>(diff); + case IMAGE_REL_ARM_BRANCH24T: + case IMAGE_REL_ARM_BLX23T: + return isInt<25>(diff); + default: + return true; + } + } else if (config->machine == ARM64) { + int64_t diff = AbsoluteDifference(s, p) + margin; + switch (relType) { + case IMAGE_REL_ARM64_BRANCH26: + return isInt<28>(diff); + case IMAGE_REL_ARM64_BRANCH19: + return isInt<21>(diff); + case IMAGE_REL_ARM64_BRANCH14: + return isInt<16>(diff); + default: + return true; + } + } else { + llvm_unreachable("Unexpected architecture"); } } // Return the last thunk for the given target if it is in range, // or create a new one. static std::pair -getThunk(DenseMap &LastThunks, Defined *Target, uint64_t P, - uint16_t Type, int Margin) { - Defined *&LastThunk = LastThunks[Target->getRVA()]; - if (LastThunk && isInRange(Type, LastThunk->getRVA(), P, Margin)) - return {LastThunk, false}; - RangeExtensionThunk *C = make(Target); - Defined *D = make("", C); - LastThunk = D; - return {D, true}; +getThunk(DenseMap &lastThunks, Defined *target, uint64_t p, + uint16_t type, int margin) { + Defined *&lastThunk = lastThunks[target->getRVA()]; + if (lastThunk && isInRange(type, lastThunk->getRVA(), p, margin)) + return {lastThunk, false}; + Chunk *c; + switch (config->machine) { + case ARMNT: + c = make(target); + break; + case ARM64: + c = make(target); + break; + default: + llvm_unreachable("Unexpected architecture"); + } + Defined *d = make("", c); + lastThunk = d; + return {d, true}; } // This checks all relocations, and for any relocation which isn't in range @@ -344,81 +400,124 @@ getThunk(DenseMap &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 &Chunks, int Margin) { - bool AddressesChanged = false; - DenseMap LastThunks; - size_t ThunksSize = 0; +static bool createThunks(OutputSection *os, int margin) { + bool addressesChanged = false; + DenseMap lastThunks; + DenseMap, uint32_t> thunkSymtabIndices; + size_t thunksSize = 0; // Recheck Chunks.size() each iteration, since we can insert more // elements into it. - for (size_t I = 0; I != Chunks.size(); ++I) { - SectionChunk *SC = dyn_cast_or_null(Chunks[I]); - if (!SC) + for (size_t i = 0; i != os->chunks.size(); ++i) { + SectionChunk *sc = dyn_cast_or_null(os->chunks[i]); + if (!sc) continue; - size_t ThunkInsertionSpot = I + 1; + size_t thunkInsertionSpot = i + 1; // Try to get a good enough estimate of where new thunks will be placed. // Offset this by the size of the new thunks added so far, to make the // estimate slightly better. - size_t ThunkInsertionRVA = SC->getRVA() + SC->getSize() + ThunksSize; - for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) { - const coff_relocation &Rel = SC->Relocs[J]; - Symbol *&RelocTarget = SC->RelocTargets[J]; + size_t thunkInsertionRVA = sc->getRVA() + sc->getSize() + thunksSize; + ObjFile *file = sc->file; + std::vector> relocReplacements; + ArrayRef originalRelocs = + file->getCOFFObj()->getRelocations(sc->header); + for (size_t j = 0, e = originalRelocs.size(); j < e; ++j) { + const coff_relocation &rel = originalRelocs[j]; + Symbol *relocTarget = file->getSymbol(rel.SymbolTableIndex); // The estimate of the source address P should be pretty accurate, // but we don't know whether the target Symbol address should be - // offset by ThunkSize or not (or by some of ThunksSize but not all of + // offset by thunksSize or not (or by some of thunksSize but not all of // it), giving us some uncertainty once we have added one thunk. - uint64_t P = SC->getRVA() + Rel.VirtualAddress + ThunksSize; + uint64_t p = sc->getRVA() + rel.VirtualAddress + thunksSize; - Defined *Sym = dyn_cast_or_null(RelocTarget); - if (!Sym) + Defined *sym = dyn_cast_or_null(relocTarget); + if (!sym) continue; - uint64_t S = Sym->getRVA(); + uint64_t s = sym->getRVA(); - if (isInRange(Rel.Type, S, P, Margin)) + if (isInRange(rel.Type, s, p, margin)) continue; // If the target isn't in range, hook it up to an existing or new // thunk. - Defined *Thunk; - bool WasNew; - std::tie(Thunk, WasNew) = getThunk(LastThunks, Sym, P, Rel.Type, Margin); - if (WasNew) { - Chunk *ThunkChunk = Thunk->getChunk(); - ThunkChunk->setRVA( - ThunkInsertionRVA); // Estimate of where it will be located. - Chunks.insert(Chunks.begin() + ThunkInsertionSpot, ThunkChunk); - ThunkInsertionSpot++; - ThunksSize += ThunkChunk->getSize(); - ThunkInsertionRVA += ThunkChunk->getSize(); - AddressesChanged = true; + Defined *thunk; + bool wasNew; + std::tie(thunk, wasNew) = getThunk(lastThunks, sym, p, rel.Type, margin); + if (wasNew) { + Chunk *thunkChunk = thunk->getChunk(); + thunkChunk->setRVA( + thunkInsertionRVA); // Estimate of where it will be located. + os->chunks.insert(os->chunks.begin() + thunkInsertionSpot, thunkChunk); + thunkInsertionSpot++; + thunksSize += thunkChunk->getSize(); + thunkInsertionRVA += thunkChunk->getSize(); + addressesChanged = true; } - RelocTarget = Thunk; + + // To redirect the relocation, add a symbol to the parent object file's + // symbol table, and replace the relocation symbol table index with the + // new index. + auto insertion = thunkSymtabIndices.insert({{file, thunk}, ~0U}); + uint32_t &thunkSymbolIndex = insertion.first->second; + if (insertion.second) + thunkSymbolIndex = file->addRangeThunkSymbol(thunk); + relocReplacements.push_back({j, thunkSymbolIndex}); + } + + // Get a writable copy of this section's relocations so they can be + // modified. If the relocations point into the object file, allocate new + // memory. Otherwise, this must be previously allocated memory that can be + // modified in place. + ArrayRef curRelocs = sc->getRelocs(); + MutableArrayRef newRelocs; + if (originalRelocs.data() == curRelocs.data()) { + newRelocs = makeMutableArrayRef( + bAlloc.Allocate(originalRelocs.size()), + originalRelocs.size()); + } else { + newRelocs = makeMutableArrayRef( + const_cast(curRelocs.data()), curRelocs.size()); } + + // Copy each relocation, but replace the symbol table indices which need + // thunks. + auto nextReplacement = relocReplacements.begin(); + auto endReplacement = relocReplacements.end(); + for (size_t i = 0, e = originalRelocs.size(); i != e; ++i) { + newRelocs[i] = originalRelocs[i]; + if (nextReplacement != endReplacement && nextReplacement->first == i) { + newRelocs[i].SymbolTableIndex = nextReplacement->second; + ++nextReplacement; + } + } + + sc->setRelocs(newRelocs); } - return AddressesChanged; + return addressesChanged; } // Verify that all relocations are in range, with no extra margin requirements. -static bool verifyRanges(const std::vector Chunks) { - for (Chunk *C : Chunks) { - SectionChunk *SC = dyn_cast_or_null(C); - if (!SC) +static bool verifyRanges(const std::vector chunks) { + for (Chunk *c : chunks) { + SectionChunk *sc = dyn_cast_or_null(c); + if (!sc) continue; - for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) { - const coff_relocation &Rel = SC->Relocs[J]; - Symbol *RelocTarget = SC->RelocTargets[J]; + ArrayRef relocs = sc->getRelocs(); + for (size_t j = 0, e = relocs.size(); j < e; ++j) { + const coff_relocation &rel = relocs[j]; + Symbol *relocTarget = sc->file->getSymbol(rel.SymbolTableIndex); - Defined *Sym = dyn_cast_or_null(RelocTarget); - if (!Sym) + Defined *sym = dyn_cast_or_null(relocTarget); + if (!sym) continue; - uint64_t P = SC->getRVA() + Rel.VirtualAddress; - uint64_t S = Sym->getRVA(); + uint64_t p = sc->getRVA() + rel.VirtualAddress; + uint64_t s = sym->getRVA(); - if (!isInRange(Rel.Type, S, P, 0)) + if (!isInRange(rel.Type, s, p, 0)) return false; } } @@ -428,71 +527,68 @@ static bool verifyRanges(const std::vector 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 verification above thought we needed thunks, we should have // added some. - assert(AddressesChanged); + assert(addressesChanged); // Recalculate the layout for the whole image (and verify the ranges at // the start of the next round). assignAddresses(); - Pass++; + pass++; } } // The main function of the writer. void Writer::run() { - ScopedTimer T1(CodeLayoutTimer); + ScopedTimer t1(codeLayoutTimer); createImportTables(); createSections(); @@ -500,19 +596,19 @@ void Writer::run() { appendImportThunks(); createExportTable(); mergeSections(); - readRelocTargets(); removeUnusedSections(); finalizeAddresses(); removeEmptySections(); + assignOutputSectionIndices(); setSectionPermissions(); createSymbolAndStringTable(); - if (FileSize > UINT32_MAX) - fatal("image size (" + Twine(FileSize) + ") " + + if (fileSize > UINT32_MAX) + fatal("image size (" + Twine(fileSize) + ") " + "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")"); - openFile(Config->OutputFile); - if (Config->is64()) { + openFile(config->outputFile); + if (config->is64()) { writeHeader(); } else { writeHeader(); @@ -520,42 +616,59 @@ void Writer::run() { writeSections(); sortExceptionTable(); - T1.stop(); + t1.stop(); - if (!Config->PDBPath.empty() && Config->Debug) { - assert(BuildId); - createPDB(Symtab, OutputSections, SectionTable, BuildId->BuildId); + if (!config->pdbPath.empty() && config->debug) { + assert(buildId); + createPDB(symtab, outputSections, sectionTable, buildId->buildId); } writeBuildId(); - writeMapFile(OutputSections); + writeMapFile(outputSections); - ScopedTimer T2(DiskCommitTimer); - if (auto E = Buffer->commit()) - fatal("failed to write the output file: " + toString(std::move(E))); + ScopedTimer t2(diskCommitTimer); + if (auto e = buffer->commit()) + fatal("failed to write the output file: " + toString(std::move(e))); } -static StringRef getOutputSectionName(StringRef Name) { - StringRef S = Name.split('$').first; +static StringRef getOutputSectionName(StringRef name) { + StringRef s = name.split('$').first; // Treat a later period as a separator for MinGW, for sections like // ".ctors.01234". - return S.substr(0, S.find('.', 1)); + return s.substr(0, s.find('.', 1)); } // For /order. -static void sortBySectionOrder(std::vector &Chunks) { - auto GetPriority = [](const Chunk *C) { - if (auto *Sec = dyn_cast(C)) - if (Sec->Sym) - return Config->Order.lookup(Sec->Sym->getName()); +static void sortBySectionOrder(std::vector &chunks) { + auto getPriority = [](const Chunk *c) { + if (auto *sec = dyn_cast(c)) + if (sec->sym) + return config->order.lookup(sec->sym->getName()); return 0; }; - std::stable_sort(Chunks.begin(), Chunks.end(), - [=](const Chunk *A, const Chunk *B) { - return GetPriority(A) < GetPriority(B); - }); + llvm::stable_sort(chunks, [=](const Chunk *a, const Chunk *b) { + return getPriority(a) < getPriority(b); + }); +} + +// Change the characteristics of existing PartialSections that belong to the +// section Name to Chars. +void Writer::fixPartialSectionChars(StringRef name, uint32_t chars) { + for (auto it : partialSections) { + PartialSection *pSec = it.second; + StringRef curName = pSec->name; + if (!curName.consume_front(name) || + (!curName.empty() && !curName.startswith("$"))) + continue; + if (pSec->characteristics == chars) + continue; + PartialSection *destSec = createPartialSection(pSec->name, chars); + destSec->chunks.insert(destSec->chunks.end(), pSec->chunks.begin(), + pSec->chunks.end()); + pSec->chunks.clear(); + } } // Sort concrete section chunks from GNU import libraries. @@ -568,249 +681,250 @@ static void sortBySectionOrder(std::vector &Chunks) { // be formed correctly, the section chunks within each .idata$* section need // to be grouped by library, and sorted alphabetically within each library // (which makes sure the header comes first and the trailer last). -static bool fixGnuImportChunks( - std::map, std::vector> &Map) { - uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; +bool Writer::fixGnuImportChunks() { + uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; // Make sure all .idata$* section chunks are mapped as RDATA in order to // be sorted into the same sections as our own synthesized .idata chunks. - for (auto &Pair : Map) { - StringRef SectionName = Pair.first.first; - uint32_t OutChars = Pair.first.second; - if (!SectionName.startswith(".idata")) - continue; - if (OutChars == RDATA) - continue; - std::vector &SrcVect = Pair.second; - std::vector &DestVect = Map[{SectionName, RDATA}]; - DestVect.insert(DestVect.end(), SrcVect.begin(), SrcVect.end()); - SrcVect.clear(); - } + fixPartialSectionChars(".idata", rdata); - bool HasIdata = false; + bool hasIdata = false; // Sort all .idata$* chunks, grouping chunks from the same library, // with alphabetical ordering of the object fils within a library. - for (auto &Pair : Map) { - StringRef SectionName = Pair.first.first; - if (!SectionName.startswith(".idata")) + for (auto it : partialSections) { + PartialSection *pSec = it.second; + if (!pSec->name.startswith(".idata")) continue; - std::vector &Chunks = Pair.second; - if (!Chunks.empty()) - HasIdata = true; - std::stable_sort(Chunks.begin(), Chunks.end(), [&](Chunk *S, Chunk *T) { - SectionChunk *SC1 = dyn_cast_or_null(S); - SectionChunk *SC2 = dyn_cast_or_null(T); - if (!SC1 || !SC2) { + if (!pSec->chunks.empty()) + hasIdata = true; + llvm::stable_sort(pSec->chunks, [&](Chunk *s, Chunk *t) { + SectionChunk *sc1 = dyn_cast_or_null(s); + SectionChunk *sc2 = dyn_cast_or_null(t); + if (!sc1 || !sc2) { // if SC1, order them ascending. If SC2 or both null, // S is not less than T. - return SC1 != nullptr; + return sc1 != nullptr; } // Make a string with "libraryname/objectfile" for sorting, achieving // both grouping by library and sorting of objects within a library, // at once. - std::string Key1 = - (SC1->File->ParentName + "/" + SC1->File->getName()).str(); - std::string Key2 = - (SC2->File->ParentName + "/" + SC2->File->getName()).str(); - return Key1 < Key2; + std::string key1 = + (sc1->file->parentName + "/" + sc1->file->getName()).str(); + std::string key2 = + (sc2->file->parentName + "/" + sc2->file->getName()).str(); + return key1 < key2; }); } - return HasIdata; + return hasIdata; } // Add generated idata chunks, for imported symbols and DLLs, and a // terminator in .idata$2. -static void addSyntheticIdata( - IdataContents &Idata, - std::map, std::vector> &Map) { - uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; - Idata.create(); +void Writer::addSyntheticIdata() { + uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; + idata.create(); // Add the .idata content in the right section groups, to allow // chunks from other linked in object files to be grouped together. // See Microsoft PE/COFF spec 5.4 for details. - auto Add = [&](StringRef N, std::vector &V) { - std::vector &DestVect = Map[{N, RDATA}]; - DestVect.insert(DestVect.end(), V.begin(), V.end()); + auto add = [&](StringRef n, std::vector &v) { + PartialSection *pSec = createPartialSection(n, rdata); + pSec->chunks.insert(pSec->chunks.end(), v.begin(), v.end()); }; // The loader assumes a specific order of data. // Add each type in the correct order. - Add(".idata$2", Idata.Dirs); - Add(".idata$4", Idata.Lookups); - Add(".idata$5", Idata.Addresses); - Add(".idata$6", Idata.Hints); - Add(".idata$7", Idata.DLLNames); + add(".idata$2", idata.dirs); + add(".idata$4", idata.lookups); + add(".idata$5", idata.addresses); + add(".idata$6", idata.hints); + add(".idata$7", idata.dllNames); } // Locate the first Chunk and size of the import directory list and the // IAT. -void Writer::locateImportTables( - std::map, std::vector> &Map) { - uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; - std::vector &ImportTables = Map[{".idata$2", RDATA}]; - if (!ImportTables.empty()) - ImportTableStart = ImportTables.front(); - for (Chunk *C : ImportTables) - ImportTableSize += C->getSize(); - - std::vector &IAT = Map[{".idata$5", RDATA}]; - if (!IAT.empty()) - IATStart = IAT.front(); - for (Chunk *C : IAT) - IATSize += C->getSize(); +void Writer::locateImportTables() { + uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; + + if (PartialSection *importDirs = findPartialSection(".idata$2", rdata)) { + if (!importDirs->chunks.empty()) + importTableStart = importDirs->chunks.front(); + for (Chunk *c : importDirs->chunks) + importTableSize += c->getSize(); + } + + if (PartialSection *importAddresses = findPartialSection(".idata$5", rdata)) { + if (!importAddresses->chunks.empty()) + iatStart = importAddresses->chunks.front(); + for (Chunk *c : importAddresses->chunks) + iatSize += c->getSize(); + } } // Create output section objects and add them to OutputSections. void Writer::createSections() { // First, create the builtin sections. - const uint32_t DATA = IMAGE_SCN_CNT_INITIALIZED_DATA; - const uint32_t BSS = IMAGE_SCN_CNT_UNINITIALIZED_DATA; - const uint32_t CODE = IMAGE_SCN_CNT_CODE; - const uint32_t DISCARDABLE = IMAGE_SCN_MEM_DISCARDABLE; - const uint32_t R = IMAGE_SCN_MEM_READ; - const uint32_t W = IMAGE_SCN_MEM_WRITE; - const uint32_t X = IMAGE_SCN_MEM_EXECUTE; - - SmallDenseMap, OutputSection *> Sections; - auto CreateSection = [&](StringRef Name, uint32_t OutChars) { - OutputSection *&Sec = Sections[{Name, OutChars}]; - if (!Sec) { - Sec = make(Name, OutChars); - OutputSections.push_back(Sec); + const uint32_t data = IMAGE_SCN_CNT_INITIALIZED_DATA; + const uint32_t bss = IMAGE_SCN_CNT_UNINITIALIZED_DATA; + const uint32_t code = IMAGE_SCN_CNT_CODE; + const uint32_t discardable = IMAGE_SCN_MEM_DISCARDABLE; + const uint32_t r = IMAGE_SCN_MEM_READ; + const uint32_t w = IMAGE_SCN_MEM_WRITE; + const uint32_t x = IMAGE_SCN_MEM_EXECUTE; + + SmallDenseMap, OutputSection *> sections; + auto createSection = [&](StringRef name, uint32_t outChars) { + OutputSection *&sec = sections[{name, outChars}]; + if (!sec) { + sec = make(name, outChars); + outputSections.push_back(sec); } - return Sec; + return sec; }; // Try to match the section order used by link.exe. - TextSec = CreateSection(".text", CODE | R | X); - CreateSection(".bss", BSS | R | W); - RdataSec = CreateSection(".rdata", DATA | R); - BuildidSec = CreateSection(".buildid", DATA | R); - DataSec = CreateSection(".data", DATA | R | W); - PdataSec = CreateSection(".pdata", DATA | R); - IdataSec = CreateSection(".idata", DATA | R); - EdataSec = CreateSection(".edata", DATA | R); - DidatSec = CreateSection(".didat", DATA | R); - RsrcSec = CreateSection(".rsrc", DATA | R); - RelocSec = CreateSection(".reloc", DATA | DISCARDABLE | R); - CtorsSec = CreateSection(".ctors", DATA | R | W); - DtorsSec = CreateSection(".dtors", DATA | R | W); + textSec = createSection(".text", code | r | x); + createSection(".bss", bss | r | w); + rdataSec = createSection(".rdata", data | r); + buildidSec = createSection(".buildid", data | r); + dataSec = createSection(".data", data | r | w); + pdataSec = createSection(".pdata", data | r); + idataSec = createSection(".idata", data | r); + edataSec = createSection(".edata", data | r); + didatSec = createSection(".didat", data | r); + rsrcSec = createSection(".rsrc", data | r); + relocSec = createSection(".reloc", data | discardable | r); + ctorsSec = createSection(".ctors", data | r | w); + dtorsSec = createSection(".dtors", data | r | w); // Then bin chunks by name and output characteristics. - std::map, std::vector> Map; - for (Chunk *C : Symtab->getChunks()) { - auto *SC = dyn_cast(C); - if (SC && !SC->Live) { - if (Config->Verbose) - SC->printDiscardedMessage(); + for (Chunk *c : symtab->getChunks()) { + auto *sc = dyn_cast(c); + if (sc && !sc->live) { + if (config->verbose) + sc->printDiscardedMessage(); continue; } - Map[{C->getSectionName(), C->getOutputCharacteristics()}].push_back(C); + StringRef name = c->getSectionName(); + // On MinGW, comdat groups are formed by putting the comdat group name + // after the '$' in the section name. Such a section name suffix shouldn't + // imply separate alphabetical sorting of those section chunks though. + if (config->mingw && sc && sc->isCOMDAT()) + name = name.split('$').first; + PartialSection *pSec = createPartialSection(name, + c->getOutputCharacteristics()); + pSec->chunks.push_back(c); } + fixPartialSectionChars(".rsrc", data | r); // Even in non MinGW cases, we might need to link against GNU import // libraries. - bool HasIdata = fixGnuImportChunks(Map); - if (!Idata.empty()) - HasIdata = true; + bool hasIdata = fixGnuImportChunks(); + if (!idata.empty()) + hasIdata = true; - if (HasIdata) - addSyntheticIdata(Idata, Map); + if (hasIdata) + addSyntheticIdata(); // Process an /order option. - if (!Config->Order.empty()) - for (auto &Pair : Map) - sortBySectionOrder(Pair.second); + if (!config->order.empty()) + for (auto it : partialSections) + sortBySectionOrder(it.second->chunks); - if (HasIdata) - locateImportTables(Map); + if (hasIdata) + locateImportTables(); // Then create an OutputSection for each section. // '$' and all following characters in input section names are // discarded when determining output section. So, .text$foo // contributes to .text, for example. See PE/COFF spec 3.2. - for (auto &Pair : Map) { - StringRef Name = getOutputSectionName(Pair.first.first); - uint32_t OutChars = Pair.first.second; + for (auto it : partialSections) { + PartialSection *pSec = it.second; + StringRef name = getOutputSectionName(pSec->name); + uint32_t outChars = pSec->characteristics; - if (Name == ".CRT") { + if (name == ".CRT") { // In link.exe, there is a special case for the I386 target where .CRT // sections are treated as if they have output characteristics DATA | R if // their characteristics are DATA | R | W. This implements the same // special case for all architectures. - OutChars = DATA | R; + outChars = data | r; - log("Processing section " + Pair.first.first + " -> " + Name); + log("Processing section " + pSec->name + " -> " + name); - sortCRTSectionChunks(Pair.second); + sortCRTSectionChunks(pSec->chunks); } - OutputSection *Sec = CreateSection(Name, OutChars); - std::vector &Chunks = Pair.second; - for (Chunk *C : Chunks) - Sec->addChunk(C); + OutputSection *sec = createSection(name, outChars); + for (Chunk *c : pSec->chunks) + sec->addChunk(c); + + sec->addContributingPartialSection(pSec); } // Finally, move some output sections to the end. - auto SectionOrder = [&](OutputSection *S) { - // Move DISCARDABLE (or non-memory-mapped) sections to the end of file because - // the loader cannot handle holes. Stripping can remove other discardable ones - // than .reloc, which is first of them (created early). - if (S->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) + auto sectionOrder = [&](const OutputSection *s) { + // Move DISCARDABLE (or non-memory-mapped) sections to the end of file + // because the loader cannot handle holes. Stripping can remove other + // discardable ones than .reloc, which is first of them (created early). + if (s->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) return 2; // .rsrc should come at the end of the non-discardable sections because its // size may change by the Win32 UpdateResources() function, causing // subsequent sections to move (see https://crbug.com/827082). - if (S == RsrcSec) + if (s == rsrcSec) return 1; return 0; }; - std::stable_sort(OutputSections.begin(), OutputSections.end(), - [&](OutputSection *S, OutputSection *T) { - return SectionOrder(S) < SectionOrder(T); - }); + llvm::stable_sort(outputSections, + [&](const OutputSection *s, const OutputSection *t) { + return sectionOrder(s) < sectionOrder(t); + }); } void Writer::createMiscChunks() { - for (auto &P : MergeChunk::Instances) - RdataSec->addChunk(P.second); + for (MergeChunk *p : MergeChunk::instances) { + if (p) { + p->finalizeContents(); + rdataSec->addChunk(p); + } + } // Create thunks for locally-dllimported symbols. - if (!Symtab->LocalImportChunks.empty()) { - for (Chunk *C : Symtab->LocalImportChunks) - RdataSec->addChunk(C); + if (!symtab->localImportChunks.empty()) { + for (Chunk *c : symtab->localImportChunks) + rdataSec->addChunk(c); } // Create Debug Information Chunks - OutputSection *DebugInfoSec = Config->MinGW ? BuildidSec : RdataSec; - if (Config->Debug || Config->Repro) { - DebugDirectory = make(DebugRecords, Config->Repro); - DebugInfoSec->addChunk(DebugDirectory); + OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec; + if (config->debug || config->repro) { + debugDirectory = make(debugRecords, config->repro); + debugInfoSec->addChunk(debugDirectory); } - if (Config->Debug) { + if (config->debug) { // Make a CVDebugRecordChunk even when /DEBUG:CV is not specified. We // output a PDB no matter what, and this chunk provides the only means of // allowing a debugger to match a PDB and an executable. So we need it even // if we're ultimately not going to write CodeView data to the PDB. - BuildId = make(); - DebugRecords.push_back(BuildId); + buildId = make(); + debugRecords.push_back(buildId); - for (Chunk *C : DebugRecords) - DebugInfoSec->addChunk(C); + for (Chunk *c : debugRecords) + debugInfoSec->addChunk(c); } // Create SEH table. x86-only. - if (Config->Machine == I386) + if (config->safeSEH) createSEHTable(); // Create /guard:cf tables if requested. - if (Config->GuardCF != GuardCFLevel::Off) + if (config->guardCF != GuardCFLevel::Off) createGuardCFTables(); - if (Config->MinGW) { + if (config->mingw) { createRuntimePseudoRelocs(); insertCtorDtorSymbols(); @@ -825,106 +939,123 @@ void Writer::createImportTables() { // Initialize DLLOrder so that import entries are ordered in // the same order as in the command line. (That affects DLL // initialization order, and this ordering is MSVC-compatible.) - for (ImportFile *File : ImportFile::Instances) { - if (!File->Live) + for (ImportFile *file : ImportFile::instances) { + if (!file->live) continue; - std::string DLL = StringRef(File->DLLName).lower(); - if (Config->DLLOrder.count(DLL) == 0) - Config->DLLOrder[DLL] = Config->DLLOrder.size(); - - if (File->ImpSym && !isa(File->ImpSym)) - fatal(toString(*File->ImpSym) + " was replaced"); - DefinedImportData *ImpSym = cast_or_null(File->ImpSym); - if (Config->DelayLoads.count(StringRef(File->DLLName).lower())) { - if (!File->ThunkSym) - fatal("cannot delay-load " + toString(File) + - " due to import of data: " + toString(*ImpSym)); - DelayIdata.add(ImpSym); + std::string dll = StringRef(file->dllName).lower(); + if (config->dllOrder.count(dll) == 0) + config->dllOrder[dll] = config->dllOrder.size(); + + if (file->impSym && !isa(file->impSym)) + fatal(toString(*file->impSym) + " was replaced"); + DefinedImportData *impSym = cast_or_null(file->impSym); + if (config->delayLoads.count(StringRef(file->dllName).lower())) { + if (!file->thunkSym) + fatal("cannot delay-load " + toString(file) + + " due to import of data: " + toString(*impSym)); + delayIdata.add(impSym); } else { - Idata.add(ImpSym); + idata.add(impSym); } } } void Writer::appendImportThunks() { - if (ImportFile::Instances.empty()) + if (ImportFile::instances.empty()) return; - for (ImportFile *File : ImportFile::Instances) { - if (!File->Live) + for (ImportFile *file : ImportFile::instances) { + if (!file->live) continue; - if (!File->ThunkSym) + if (!file->thunkSym) continue; - if (!isa(File->ThunkSym)) - fatal(toString(*File->ThunkSym) + " was replaced"); - DefinedImportThunk *Thunk = cast(File->ThunkSym); - if (File->ThunkLive) - TextSec->addChunk(Thunk->getChunk()); + if (!isa(file->thunkSym)) + fatal(toString(*file->thunkSym) + " was replaced"); + DefinedImportThunk *thunk = cast(file->thunkSym); + if (file->thunkLive) + textSec->addChunk(thunk->getChunk()); } - if (!DelayIdata.empty()) { - Defined *Helper = cast(Config->DelayLoadHelper); - DelayIdata.create(Helper); - for (Chunk *C : DelayIdata.getChunks()) - DidatSec->addChunk(C); - for (Chunk *C : DelayIdata.getDataChunks()) - DataSec->addChunk(C); - for (Chunk *C : DelayIdata.getCodeChunks()) - TextSec->addChunk(C); + if (!delayIdata.empty()) { + Defined *helper = cast(config->delayLoadHelper); + delayIdata.create(helper); + for (Chunk *c : delayIdata.getChunks()) + didatSec->addChunk(c); + for (Chunk *c : delayIdata.getDataChunks()) + dataSec->addChunk(c); + for (Chunk *c : delayIdata.getCodeChunks()) + textSec->addChunk(c); } } void Writer::createExportTable() { - if (Config->Exports.empty()) + if (config->exports.empty()) return; - for (Chunk *C : Edata.Chunks) - EdataSec->addChunk(C); + for (Chunk *c : edata.chunks) + edataSec->addChunk(c); } void Writer::removeUnusedSections() { // Remove sections that we can be sure won't get content, to avoid // allocating space for their section headers. - auto IsUnused = [this](OutputSection *S) { - if (S == RelocSec) + auto isUnused = [this](OutputSection *s) { + if (s == relocSec) return false; // This section is populated later. // MergeChunks have zero size at this point, as their size is finalized // later. Only remove sections that have no Chunks at all. - return S->Chunks.empty(); + return s->chunks.empty(); }; - OutputSections.erase( - std::remove_if(OutputSections.begin(), OutputSections.end(), IsUnused), - OutputSections.end()); + outputSections.erase( + std::remove_if(outputSections.begin(), outputSections.end(), isUnused), + outputSections.end()); } // The Windows loader doesn't seem to like empty sections, // so we remove them if any. void Writer::removeEmptySections() { - auto IsEmpty = [](OutputSection *S) { return S->getVirtualSize() == 0; }; - OutputSections.erase( - std::remove_if(OutputSections.begin(), OutputSections.end(), IsEmpty), - OutputSections.end()); - uint32_t Idx = 1; - for (OutputSection *Sec : OutputSections) - Sec->SectionIndex = Idx++; + auto isEmpty = [](OutputSection *s) { return s->getVirtualSize() == 0; }; + outputSections.erase( + std::remove_if(outputSections.begin(), outputSections.end(), isEmpty), + outputSections.end()); } -size_t Writer::addEntryToStringTable(StringRef Str) { - assert(Str.size() > COFF::NameSize); - size_t OffsetOfEntry = Strtab.size() + 4; // +4 for the size field - Strtab.insert(Strtab.end(), Str.begin(), Str.end()); - Strtab.push_back('\0'); - return OffsetOfEntry; +void Writer::assignOutputSectionIndices() { + // Assign final output section indices, and assign each chunk to its output + // section. + uint32_t idx = 1; + for (OutputSection *os : outputSections) { + os->sectionIndex = idx; + for (Chunk *c : os->chunks) + c->setOutputSectionIdx(idx); + ++idx; + } + + // Merge chunks are containers of chunks, so assign those an output section + // too. + for (MergeChunk *mc : MergeChunk::instances) + if (mc) + for (SectionChunk *sc : mc->sections) + if (sc && sc->live) + sc->setOutputSectionIdx(mc->getOutputSectionIdx()); } -Optional Writer::createSymbol(Defined *Def) { - coff_symbol16 Sym; - switch (Def->kind()) { +size_t Writer::addEntryToStringTable(StringRef str) { + assert(str.size() > COFF::NameSize); + size_t offsetOfEntry = strtab.size() + 4; // +4 for the size field + strtab.insert(strtab.end(), str.begin(), str.end()); + strtab.push_back('\0'); + return offsetOfEntry; +} + +Optional Writer::createSymbol(Defined *def) { + coff_symbol16 sym; + switch (def->kind()) { case Symbol::DefinedAbsoluteKind: - Sym.Value = Def->getRVA(); - Sym.SectionNumber = IMAGE_SYM_ABSOLUTE; + sym.Value = def->getRVA(); + sym.SectionNumber = IMAGE_SYM_ABSOLUTE; break; case Symbol::DefinedSyntheticKind: // Relative symbols are unrepresentable in a COFF symbol table. @@ -932,38 +1063,38 @@ Optional Writer::createSymbol(Defined *Def) { default: { // Don't write symbols that won't be written to the output to the symbol // table. - Chunk *C = Def->getChunk(); - if (!C) + Chunk *c = def->getChunk(); + if (!c) return None; - OutputSection *OS = C->getOutputSection(); - if (!OS) + OutputSection *os = c->getOutputSection(); + if (!os) return None; - Sym.Value = Def->getRVA() - OS->getRVA(); - Sym.SectionNumber = OS->SectionIndex; + sym.Value = def->getRVA() - os->getRVA(); + sym.SectionNumber = os->sectionIndex; break; } } - StringRef Name = Def->getName(); - if (Name.size() > COFF::NameSize) { - Sym.Name.Offset.Zeroes = 0; - Sym.Name.Offset.Offset = addEntryToStringTable(Name); + StringRef name = def->getName(); + if (name.size() > COFF::NameSize) { + sym.Name.Offset.Zeroes = 0; + sym.Name.Offset.Offset = addEntryToStringTable(name); } else { - memset(Sym.Name.ShortName, 0, COFF::NameSize); - memcpy(Sym.Name.ShortName, Name.data(), Name.size()); + memset(sym.Name.ShortName, 0, COFF::NameSize); + memcpy(sym.Name.ShortName, name.data(), name.size()); } - if (auto *D = dyn_cast(Def)) { - COFFSymbolRef Ref = D->getCOFFSymbol(); - Sym.Type = Ref.getType(); - Sym.StorageClass = Ref.getStorageClass(); + if (auto *d = dyn_cast(def)) { + COFFSymbolRef ref = d->getCOFFSymbol(); + sym.Type = ref.getType(); + sym.StorageClass = ref.getStorageClass(); } else { - Sym.Type = IMAGE_SYM_TYPE_NULL; - Sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; + sym.Type = IMAGE_SYM_TYPE_NULL; + sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; } - Sym.NumberOfAuxSymbols = 0; - return Sym; + sym.NumberOfAuxSymbols = 0; + return sym; } void Writer::createSymbolAndStringTable() { @@ -975,113 +1106,120 @@ void Writer::createSymbolAndStringTable() { // solution where discardable sections have long names preserved and // non-discardable sections have their names truncated, to ensure that any // section which is mapped at runtime also has its name mapped at runtime. - for (OutputSection *Sec : OutputSections) { - if (Sec->Name.size() <= COFF::NameSize) + for (OutputSection *sec : outputSections) { + if (sec->name.size() <= COFF::NameSize) continue; - if ((Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) + if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) continue; - Sec->setStringTableOff(addEntryToStringTable(Sec->Name)); + sec->setStringTableOff(addEntryToStringTable(sec->name)); } - if (Config->DebugDwarf || Config->DebugSymtab) { - for (ObjFile *File : ObjFile::Instances) { - for (Symbol *B : File->getSymbols()) { - auto *D = dyn_cast_or_null(B); - if (!D || D->WrittenToSymtab) + if (config->debugDwarf || config->debugSymtab) { + for (ObjFile *file : ObjFile::instances) { + for (Symbol *b : file->getSymbols()) { + auto *d = dyn_cast_or_null(b); + if (!d || d->writtenToSymtab) continue; - D->WrittenToSymtab = true; + d->writtenToSymtab = true; - if (Optional Sym = createSymbol(D)) - OutputSymtab.push_back(*Sym); + if (Optional sym = createSymbol(d)) + outputSymtab.push_back(*sym); } } } - if (OutputSymtab.empty() && Strtab.empty()) + if (outputSymtab.empty() && strtab.empty()) return; // We position the symbol table to be adjacent to the end of the last section. - uint64_t FileOff = FileSize; - PointerToSymbolTable = FileOff; - FileOff += OutputSymtab.size() * sizeof(coff_symbol16); - FileOff += 4 + Strtab.size(); - FileSize = alignTo(FileOff, SectorSize); + uint64_t fileOff = fileSize; + pointerToSymbolTable = fileOff; + fileOff += outputSymtab.size() * sizeof(coff_symbol16); + fileOff += 4 + strtab.size(); + fileSize = alignTo(fileOff, config->fileAlign); } void Writer::mergeSections() { - if (!PdataSec->Chunks.empty()) { - FirstPdata = PdataSec->Chunks.front(); - LastPdata = PdataSec->Chunks.back(); + if (!pdataSec->chunks.empty()) { + firstPdata = pdataSec->chunks.front(); + lastPdata = pdataSec->chunks.back(); } - for (auto &P : Config->Merge) { - StringRef ToName = P.second; - if (P.first == ToName) + for (auto &p : config->merge) { + StringRef toName = p.second; + if (p.first == toName) continue; - StringSet<> Names; + StringSet<> names; while (1) { - if (!Names.insert(ToName).second) - fatal("/merge: cycle found for section '" + P.first + "'"); - auto I = Config->Merge.find(ToName); - if (I == Config->Merge.end()) + if (!names.insert(toName).second) + fatal("/merge: cycle found for section '" + p.first + "'"); + auto i = config->merge.find(toName); + if (i == config->merge.end()) break; - ToName = I->second; + toName = i->second; } - OutputSection *From = findSection(P.first); - OutputSection *To = findSection(ToName); - if (!From) + OutputSection *from = findSection(p.first); + OutputSection *to = findSection(toName); + if (!from) continue; - if (!To) { - From->Name = ToName; + if (!to) { + from->name = toName; continue; } - To->merge(From); + to->merge(from); } } -// Visits all sections to initialize their relocation targets. -void Writer::readRelocTargets() { - for (OutputSection *Sec : OutputSections) - for_each(parallel::par, Sec->Chunks.begin(), Sec->Chunks.end(), - [&](Chunk *C) { C->readRelocTargets(); }); -} - // Visits all sections to assign incremental, non-overlapping RVAs and // file offsets. void Writer::assignAddresses() { - SizeOfHeaders = DOSStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + - sizeof(data_directory) * NumberOfDataDirectory + - sizeof(coff_section) * OutputSections.size(); - SizeOfHeaders += - Config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); - SizeOfHeaders = alignTo(SizeOfHeaders, SectorSize); - uint64_t RVA = PageSize; // The first page is kept unmapped. - FileSize = SizeOfHeaders; - - for (OutputSection *Sec : OutputSections) { - if (Sec == RelocSec) + sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + + sizeof(data_directory) * numberOfDataDirectory + + sizeof(coff_section) * outputSections.size(); + sizeOfHeaders += + config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); + sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign); + uint64_t rva = pageSize; // The first page is kept unmapped. + fileSize = sizeOfHeaders; + + for (OutputSection *sec : outputSections) { + if (sec == relocSec) addBaserels(); - uint64_t RawSize = 0, VirtualSize = 0; - Sec->Header.VirtualAddress = RVA; - for (Chunk *C : Sec->Chunks) { - VirtualSize = alignTo(VirtualSize, C->Alignment); - C->setRVA(RVA + VirtualSize); - C->OutputSectionOff = VirtualSize; - C->finalizeContents(); - VirtualSize += C->getSize(); - if (C->hasData()) - RawSize = alignTo(VirtualSize, SectorSize); + uint64_t rawSize = 0, virtualSize = 0; + sec->header.VirtualAddress = rva; + + // If /FUNCTIONPADMIN is used, functions are padded in order to create a + // hotpatchable image. + const bool isCodeSection = + (sec->header.Characteristics & IMAGE_SCN_CNT_CODE) && + (sec->header.Characteristics & IMAGE_SCN_MEM_READ) && + (sec->header.Characteristics & IMAGE_SCN_MEM_EXECUTE); + uint32_t padding = isCodeSection ? config->functionPadMin : 0; + + for (Chunk *c : sec->chunks) { + if (padding && c->isHotPatchable()) + virtualSize += padding; + virtualSize = alignTo(virtualSize, c->getAlignment()); + c->setRVA(rva + virtualSize); + virtualSize += c->getSize(); + if (c->hasData) + rawSize = alignTo(virtualSize, config->fileAlign); } - if (VirtualSize > UINT32_MAX) - error("section larger than 4 GiB: " + Sec->Name); - Sec->Header.VirtualSize = VirtualSize; - Sec->Header.SizeOfRawData = RawSize; - if (RawSize != 0) - Sec->Header.PointerToRawData = FileSize; - RVA += alignTo(VirtualSize, PageSize); - FileSize += alignTo(RawSize, SectorSize); - } - SizeOfImage = alignTo(RVA, PageSize); + if (virtualSize > UINT32_MAX) + error("section larger than 4 GiB: " + sec->name); + sec->header.VirtualSize = virtualSize; + sec->header.SizeOfRawData = rawSize; + if (rawSize != 0) + sec->header.PointerToRawData = fileSize; + rva += alignTo(virtualSize, pageSize); + fileSize += alignTo(rawSize, config->fileAlign); + } + sizeOfImage = alignTo(rva, pageSize); + + // Assign addresses to sections in MergeChunks. + for (MergeChunk *mc : MergeChunk::instances) + if (mc) + mc->assignSubsectionRVAs(); } template void Writer::writeHeader() { @@ -1090,275 +1228,299 @@ template void Writer::writeHeader() { // under DOS, that program gets run (usually to just print an error message). // When run under Windows, the loader looks at AddressOfNewExeHeader and uses // the PE header instead. - uint8_t *Buf = Buffer->getBufferStart(); - auto *DOS = reinterpret_cast(Buf); - Buf += sizeof(dos_header); - DOS->Magic[0] = 'M'; - DOS->Magic[1] = 'Z'; - DOS->UsedBytesInTheLastPage = DOSStubSize % 512; - DOS->FileSizeInPages = divideCeil(DOSStubSize, 512); - DOS->HeaderSizeInParagraphs = sizeof(dos_header) / 16; - - DOS->AddressOfRelocationTable = sizeof(dos_header); - DOS->AddressOfNewExeHeader = DOSStubSize; + uint8_t *buf = buffer->getBufferStart(); + auto *dos = reinterpret_cast(buf); + buf += sizeof(dos_header); + dos->Magic[0] = 'M'; + dos->Magic[1] = 'Z'; + dos->UsedBytesInTheLastPage = dosStubSize % 512; + dos->FileSizeInPages = divideCeil(dosStubSize, 512); + dos->HeaderSizeInParagraphs = sizeof(dos_header) / 16; + + dos->AddressOfRelocationTable = sizeof(dos_header); + dos->AddressOfNewExeHeader = dosStubSize; // Write DOS program. - memcpy(Buf, DOSProgram, sizeof(DOSProgram)); - Buf += sizeof(DOSProgram); + memcpy(buf, dosProgram, sizeof(dosProgram)); + buf += sizeof(dosProgram); // Write PE magic - memcpy(Buf, PEMagic, sizeof(PEMagic)); - Buf += sizeof(PEMagic); + memcpy(buf, PEMagic, sizeof(PEMagic)); + buf += sizeof(PEMagic); // Write COFF header - auto *COFF = reinterpret_cast(Buf); - Buf += sizeof(*COFF); - COFF->Machine = Config->Machine; - COFF->NumberOfSections = OutputSections.size(); - COFF->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE; - if (Config->LargeAddressAware) - COFF->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE; - if (!Config->is64()) - COFF->Characteristics |= IMAGE_FILE_32BIT_MACHINE; - if (Config->DLL) - COFF->Characteristics |= IMAGE_FILE_DLL; - if (!Config->Relocatable) - COFF->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED; - COFF->SizeOfOptionalHeader = - sizeof(PEHeaderTy) + sizeof(data_directory) * NumberOfDataDirectory; + auto *coff = reinterpret_cast(buf); + buf += sizeof(*coff); + coff->Machine = config->machine; + coff->NumberOfSections = outputSections.size(); + coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE; + if (config->largeAddressAware) + coff->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE; + if (!config->is64()) + coff->Characteristics |= IMAGE_FILE_32BIT_MACHINE; + if (config->dll) + coff->Characteristics |= IMAGE_FILE_DLL; + if (!config->relocatable) + coff->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED; + if (config->swaprunCD) + coff->Characteristics |= IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP; + if (config->swaprunNet) + coff->Characteristics |= IMAGE_FILE_NET_RUN_FROM_SWAP; + coff->SizeOfOptionalHeader = + sizeof(PEHeaderTy) + sizeof(data_directory) * numberOfDataDirectory; // Write PE header - auto *PE = reinterpret_cast(Buf); - Buf += sizeof(*PE); - PE->Magic = Config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32; + auto *pe = reinterpret_cast(buf); + buf += sizeof(*pe); + pe->Magic = config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32; // If {Major,Minor}LinkerVersion is left at 0.0, then for some // reason signing the resulting PE file with Authenticode produces a // signature that fails to validate on Windows 7 (but is OK on 10). // Set it to 14.0, which is what VS2015 outputs, and which avoids // that problem. - PE->MajorLinkerVersion = 14; - PE->MinorLinkerVersion = 0; - - PE->ImageBase = Config->ImageBase; - PE->SectionAlignment = PageSize; - PE->FileAlignment = SectorSize; - PE->MajorImageVersion = Config->MajorImageVersion; - PE->MinorImageVersion = Config->MinorImageVersion; - PE->MajorOperatingSystemVersion = Config->MajorOSVersion; - PE->MinorOperatingSystemVersion = Config->MinorOSVersion; - PE->MajorSubsystemVersion = Config->MajorOSVersion; - PE->MinorSubsystemVersion = Config->MinorOSVersion; - PE->Subsystem = Config->Subsystem; - PE->SizeOfImage = SizeOfImage; - PE->SizeOfHeaders = SizeOfHeaders; - if (!Config->NoEntry) { - Defined *Entry = cast(Config->Entry); - PE->AddressOfEntryPoint = Entry->getRVA(); + pe->MajorLinkerVersion = 14; + pe->MinorLinkerVersion = 0; + + pe->ImageBase = config->imageBase; + pe->SectionAlignment = pageSize; + pe->FileAlignment = config->fileAlign; + pe->MajorImageVersion = config->majorImageVersion; + pe->MinorImageVersion = config->minorImageVersion; + pe->MajorOperatingSystemVersion = config->majorOSVersion; + pe->MinorOperatingSystemVersion = config->minorOSVersion; + pe->MajorSubsystemVersion = config->majorOSVersion; + pe->MinorSubsystemVersion = config->minorOSVersion; + pe->Subsystem = config->subsystem; + pe->SizeOfImage = sizeOfImage; + pe->SizeOfHeaders = sizeOfHeaders; + if (!config->noEntry) { + Defined *entry = cast(config->entry); + pe->AddressOfEntryPoint = entry->getRVA(); // Pointer to thumb code must have the LSB set, so adjust it. - if (Config->Machine == ARMNT) - PE->AddressOfEntryPoint |= 1; - } - PE->SizeOfStackReserve = Config->StackReserve; - PE->SizeOfStackCommit = Config->StackCommit; - PE->SizeOfHeapReserve = Config->HeapReserve; - PE->SizeOfHeapCommit = Config->HeapCommit; - if (Config->AppContainer) - PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER; - if (Config->DynamicBase) - PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE; - if (Config->HighEntropyVA) - PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA; - if (!Config->AllowBind) - PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_BIND; - if (Config->NxCompat) - PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT; - if (!Config->AllowIsolation) - PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION; - if (Config->GuardCF != GuardCFLevel::Off) - PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF; - if (Config->IntegrityCheck) - PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY; - if (SetNoSEHCharacteristic) - PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH; - if (Config->TerminalServerAware) - PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; - PE->NumberOfRvaAndSize = NumberOfDataDirectory; - if (TextSec->getVirtualSize()) { - PE->BaseOfCode = TextSec->getRVA(); - PE->SizeOfCode = TextSec->getRawSize(); - } - PE->SizeOfInitializedData = getSizeOfInitializedData(); + if (config->machine == ARMNT) + pe->AddressOfEntryPoint |= 1; + } + pe->SizeOfStackReserve = config->stackReserve; + pe->SizeOfStackCommit = config->stackCommit; + pe->SizeOfHeapReserve = config->heapReserve; + pe->SizeOfHeapCommit = config->heapCommit; + if (config->appContainer) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER; + if (config->dynamicBase) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE; + if (config->highEntropyVA) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA; + if (!config->allowBind) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_BIND; + if (config->nxCompat) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT; + if (!config->allowIsolation) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION; + if (config->guardCF != GuardCFLevel::Off) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF; + if (config->integrityCheck) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY; + if (setNoSEHCharacteristic) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH; + if (config->terminalServerAware) + pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; + pe->NumberOfRvaAndSize = numberOfDataDirectory; + if (textSec->getVirtualSize()) { + pe->BaseOfCode = textSec->getRVA(); + pe->SizeOfCode = textSec->getRawSize(); + } + pe->SizeOfInitializedData = getSizeOfInitializedData(); // Write data directory - auto *Dir = reinterpret_cast(Buf); - Buf += sizeof(*Dir) * NumberOfDataDirectory; - if (!Config->Exports.empty()) { - Dir[EXPORT_TABLE].RelativeVirtualAddress = Edata.getRVA(); - Dir[EXPORT_TABLE].Size = Edata.getSize(); - } - if (ImportTableStart) { - Dir[IMPORT_TABLE].RelativeVirtualAddress = ImportTableStart->getRVA(); - Dir[IMPORT_TABLE].Size = ImportTableSize; - } - if (IATStart) { - Dir[IAT].RelativeVirtualAddress = IATStart->getRVA(); - Dir[IAT].Size = IATSize; - } - if (RsrcSec->getVirtualSize()) { - Dir[RESOURCE_TABLE].RelativeVirtualAddress = RsrcSec->getRVA(); - Dir[RESOURCE_TABLE].Size = RsrcSec->getVirtualSize(); - } - if (FirstPdata) { - Dir[EXCEPTION_TABLE].RelativeVirtualAddress = FirstPdata->getRVA(); - Dir[EXCEPTION_TABLE].Size = - LastPdata->getRVA() + LastPdata->getSize() - FirstPdata->getRVA(); - } - if (RelocSec->getVirtualSize()) { - Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = RelocSec->getRVA(); - Dir[BASE_RELOCATION_TABLE].Size = RelocSec->getVirtualSize(); - } - if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) { - if (Defined *B = dyn_cast(Sym)) { - Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA(); - Dir[TLS_TABLE].Size = Config->is64() + auto *dir = reinterpret_cast(buf); + buf += sizeof(*dir) * numberOfDataDirectory; + if (!config->exports.empty()) { + dir[EXPORT_TABLE].RelativeVirtualAddress = edata.getRVA(); + dir[EXPORT_TABLE].Size = edata.getSize(); + } + if (importTableStart) { + dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA(); + dir[IMPORT_TABLE].Size = importTableSize; + } + if (iatStart) { + dir[IAT].RelativeVirtualAddress = iatStart->getRVA(); + dir[IAT].Size = iatSize; + } + if (rsrcSec->getVirtualSize()) { + dir[RESOURCE_TABLE].RelativeVirtualAddress = rsrcSec->getRVA(); + dir[RESOURCE_TABLE].Size = rsrcSec->getVirtualSize(); + } + if (firstPdata) { + dir[EXCEPTION_TABLE].RelativeVirtualAddress = firstPdata->getRVA(); + dir[EXCEPTION_TABLE].Size = + lastPdata->getRVA() + lastPdata->getSize() - firstPdata->getRVA(); + } + if (relocSec->getVirtualSize()) { + dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA(); + dir[BASE_RELOCATION_TABLE].Size = relocSec->getVirtualSize(); + } + if (Symbol *sym = symtab->findUnderscore("_tls_used")) { + if (Defined *b = dyn_cast(sym)) { + dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA(); + dir[TLS_TABLE].Size = config->is64() ? sizeof(object::coff_tls_directory64) : sizeof(object::coff_tls_directory32); } } - if (DebugDirectory) { - Dir[DEBUG_DIRECTORY].RelativeVirtualAddress = DebugDirectory->getRVA(); - Dir[DEBUG_DIRECTORY].Size = DebugDirectory->getSize(); + if (debugDirectory) { + dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA(); + dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize(); } - if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) { - if (auto *B = dyn_cast(Sym)) { - SectionChunk *SC = B->getChunk(); - assert(B->getRVA() >= SC->getRVA()); - uint64_t OffsetInChunk = B->getRVA() - SC->getRVA(); - if (!SC->hasData() || OffsetInChunk + 4 > SC->getSize()) + if (Symbol *sym = symtab->findUnderscore("_load_config_used")) { + if (auto *b = dyn_cast(sym)) { + SectionChunk *sc = b->getChunk(); + assert(b->getRVA() >= sc->getRVA()); + uint64_t offsetInChunk = b->getRVA() - sc->getRVA(); + if (!sc->hasData || offsetInChunk + 4 > sc->getSize()) fatal("_load_config_used is malformed"); - ArrayRef SecContents = SC->getContents(); - uint32_t LoadConfigSize = - *reinterpret_cast(&SecContents[OffsetInChunk]); - if (OffsetInChunk + LoadConfigSize > SC->getSize()) + ArrayRef secContents = sc->getContents(); + uint32_t loadConfigSize = + *reinterpret_cast(&secContents[offsetInChunk]); + if (offsetInChunk + loadConfigSize > sc->getSize()) fatal("_load_config_used is too large"); - Dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = B->getRVA(); - Dir[LOAD_CONFIG_TABLE].Size = LoadConfigSize; + dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = b->getRVA(); + dir[LOAD_CONFIG_TABLE].Size = loadConfigSize; } } - if (!DelayIdata.empty()) { - Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress = - DelayIdata.getDirRVA(); - Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize(); + if (!delayIdata.empty()) { + dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress = + delayIdata.getDirRVA(); + dir[DELAY_IMPORT_DESCRIPTOR].Size = delayIdata.getDirSize(); } // Write section table - for (OutputSection *Sec : OutputSections) { - Sec->writeHeaderTo(Buf); - Buf += sizeof(coff_section); + for (OutputSection *sec : outputSections) { + sec->writeHeaderTo(buf); + buf += sizeof(coff_section); } - SectionTable = ArrayRef( - Buf - OutputSections.size() * sizeof(coff_section), Buf); + sectionTable = ArrayRef( + buf - outputSections.size() * sizeof(coff_section), buf); - if (OutputSymtab.empty() && Strtab.empty()) + if (outputSymtab.empty() && strtab.empty()) return; - COFF->PointerToSymbolTable = PointerToSymbolTable; - uint32_t NumberOfSymbols = OutputSymtab.size(); - COFF->NumberOfSymbols = NumberOfSymbols; - auto *SymbolTable = reinterpret_cast( - Buffer->getBufferStart() + COFF->PointerToSymbolTable); - for (size_t I = 0; I != NumberOfSymbols; ++I) - SymbolTable[I] = OutputSymtab[I]; + coff->PointerToSymbolTable = pointerToSymbolTable; + uint32_t numberOfSymbols = outputSymtab.size(); + coff->NumberOfSymbols = numberOfSymbols; + auto *symbolTable = reinterpret_cast( + buffer->getBufferStart() + coff->PointerToSymbolTable); + for (size_t i = 0; i != numberOfSymbols; ++i) + symbolTable[i] = outputSymtab[i]; // Create the string table, it follows immediately after the symbol table. // The first 4 bytes is length including itself. - Buf = reinterpret_cast(&SymbolTable[NumberOfSymbols]); - write32le(Buf, Strtab.size() + 4); - if (!Strtab.empty()) - memcpy(Buf + 4, Strtab.data(), Strtab.size()); + buf = reinterpret_cast(&symbolTable[numberOfSymbols]); + write32le(buf, strtab.size() + 4); + if (!strtab.empty()) + memcpy(buf + 4, strtab.data(), strtab.size()); } -void Writer::openFile(StringRef Path) { - Buffer = CHECK( - FileOutputBuffer::create(Path, FileSize, FileOutputBuffer::F_executable), - "failed to open " + Path); +void Writer::openFile(StringRef path) { + buffer = CHECK( + FileOutputBuffer::create(path, fileSize, FileOutputBuffer::F_executable), + "failed to open " + path); } void Writer::createSEHTable() { - // Set the no SEH characteristic on x86 binaries unless we find exception - // handlers. - SetNoSEHCharacteristic = true; - - SymbolRVASet Handlers; - for (ObjFile *File : ObjFile::Instances) { - // FIXME: We should error here instead of earlier unless /safeseh:no was - // passed. - if (!File->hasSafeSEH()) - return; - - markSymbolsForRVATable(File, File->getSXDataChunks(), Handlers); + SymbolRVASet handlers; + for (ObjFile *file : ObjFile::instances) { + if (!file->hasSafeSEH()) + error("/safeseh: " + file->getName() + " is not compatible with SEH"); + markSymbolsForRVATable(file, file->getSXDataChunks(), handlers); } - // Remove the "no SEH" characteristic if all object files were built with - // safeseh, we found some exception handlers, and there is a load config in - // the object. - SetNoSEHCharacteristic = - Handlers.empty() || !Symtab->findUnderscore("_load_config_used"); + // Set the "no SEH" characteristic if there really were no handlers, or if + // there is no load config object to point to the table of handlers. + setNoSEHCharacteristic = + handlers.empty() || !symtab->findUnderscore("_load_config_used"); - maybeAddRVATable(std::move(Handlers), "__safe_se_handler_table", + maybeAddRVATable(std::move(handlers), "__safe_se_handler_table", "__safe_se_handler_count"); } // Add a symbol to an RVA set. Two symbols may have the same RVA, but an RVA set // cannot contain duplicates. Therefore, the set is uniqued by Chunk and the // symbol's offset into that Chunk. -static void addSymbolToRVASet(SymbolRVASet &RVASet, Defined *S) { - Chunk *C = S->getChunk(); - if (auto *SC = dyn_cast(C)) - C = SC->Repl; // Look through ICF replacement. - uint32_t Off = S->getRVA() - (C ? C->getRVA() : 0); - RVASet.insert({C, Off}); +static void addSymbolToRVASet(SymbolRVASet &rvaSet, Defined *s) { + Chunk *c = s->getChunk(); + if (auto *sc = dyn_cast(c)) + c = sc->repl; // Look through ICF replacement. + uint32_t off = s->getRVA() - (c ? c->getRVA() : 0); + rvaSet.insert({c, off}); } // Given a symbol, add it to the GFIDs table if it is a live, defined, function // symbol in an executable section. -static void maybeAddAddressTakenFunction(SymbolRVASet &AddressTakenSyms, - Symbol *S) { - auto *D = dyn_cast_or_null(S); - - // Ignore undefined symbols and references to non-functions (e.g. globals and - // labels). - if (!D || - D->getCOFFSymbol().getComplexType() != COFF::IMAGE_SYM_DTYPE_FUNCTION) +static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms, + Symbol *s) { + if (!s) return; - // Mark the symbol as address taken if it's in an executable section. - Chunk *RefChunk = D->getChunk(); - OutputSection *OS = RefChunk ? RefChunk->getOutputSection() : nullptr; - if (OS && OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE) - addSymbolToRVASet(AddressTakenSyms, D); + switch (s->kind()) { + case Symbol::DefinedLocalImportKind: + case Symbol::DefinedImportDataKind: + // Defines an __imp_ pointer, so it is data, so it is ignored. + break; + case Symbol::DefinedCommonKind: + // Common is always data, so it is ignored. + break; + case Symbol::DefinedAbsoluteKind: + case Symbol::DefinedSyntheticKind: + // Absolute is never code, synthetic generally isn't and usually isn't + // determinable. + break; + case Symbol::LazyKind: + case Symbol::UndefinedKind: + // Undefined symbols resolve to zero, so they don't have an RVA. Lazy + // symbols shouldn't have relocations. + break; + + case Symbol::DefinedImportThunkKind: + // Thunks are always code, include them. + addSymbolToRVASet(addressTakenSyms, cast(s)); + break; + + case Symbol::DefinedRegularKind: { + // This is a regular, defined, symbol from a COFF file. Mark the symbol as + // address taken if the symbol type is function and it's in an executable + // section. + auto *d = cast(s); + if (d->getCOFFSymbol().getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION) { + SectionChunk *sc = dyn_cast(d->getChunk()); + if (sc && sc->live && + sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) + addSymbolToRVASet(addressTakenSyms, d); + } + break; + } + } } // Visit all relocations from all section contributions of this object file and // mark the relocation target as address-taken. -static void markSymbolsWithRelocations(ObjFile *File, - SymbolRVASet &UsedSymbols) { - for (Chunk *C : File->getChunks()) { +static void markSymbolsWithRelocations(ObjFile *file, + SymbolRVASet &usedSymbols) { + for (Chunk *c : file->getChunks()) { // We only care about live section chunks. Common chunks and other chunks // don't generally contain relocations. - SectionChunk *SC = dyn_cast(C); - if (!SC || !SC->Live) + SectionChunk *sc = dyn_cast(c); + if (!sc || !sc->live) continue; - for (const coff_relocation &Reloc : SC->Relocs) { - if (Config->Machine == I386 && Reloc.Type == COFF::IMAGE_REL_I386_REL32) + for (const coff_relocation &reloc : sc->getRelocs()) { + if (config->machine == I386 && reloc.Type == COFF::IMAGE_REL_I386_REL32) // Ignore relative relocations on x86. On x86_64 they can't be ignored // since they're also used to compute absolute addresses. continue; - Symbol *Ref = SC->File->getSymbol(Reloc.SymbolTableIndex); - maybeAddAddressTakenFunction(UsedSymbols, Ref); + Symbol *ref = sc->file->getSymbol(reloc.SymbolTableIndex); + maybeAddAddressTakenFunction(usedSymbols, ref); } } } @@ -1367,109 +1529,109 @@ static void markSymbolsWithRelocations(ObjFile *File, // address-taken functions. It is sorted and uniqued, just like the safe SEH // table. void Writer::createGuardCFTables() { - SymbolRVASet AddressTakenSyms; - SymbolRVASet LongJmpTargets; - for (ObjFile *File : ObjFile::Instances) { + SymbolRVASet addressTakenSyms; + SymbolRVASet longJmpTargets; + for (ObjFile *file : ObjFile::instances) { // If the object was compiled with /guard:cf, the address taken symbols // are in .gfids$y sections, and the longjmp targets are in .gljmp$y // sections. If the object was not compiled with /guard:cf, we assume there // were no setjmp targets, and that all code symbols with relocations are // possibly address-taken. - if (File->hasGuardCF()) { - markSymbolsForRVATable(File, File->getGuardFidChunks(), AddressTakenSyms); - markSymbolsForRVATable(File, File->getGuardLJmpChunks(), LongJmpTargets); + if (file->hasGuardCF()) { + markSymbolsForRVATable(file, file->getGuardFidChunks(), addressTakenSyms); + markSymbolsForRVATable(file, file->getGuardLJmpChunks(), longJmpTargets); } else { - markSymbolsWithRelocations(File, AddressTakenSyms); + markSymbolsWithRelocations(file, addressTakenSyms); } } // Mark the image entry as address-taken. - if (Config->Entry) - maybeAddAddressTakenFunction(AddressTakenSyms, Config->Entry); + if (config->entry) + maybeAddAddressTakenFunction(addressTakenSyms, config->entry); // Mark exported symbols in executable sections as address-taken. - for (Export &E : Config->Exports) - maybeAddAddressTakenFunction(AddressTakenSyms, E.Sym); + for (Export &e : config->exports) + maybeAddAddressTakenFunction(addressTakenSyms, e.sym); // Ensure sections referenced in the gfid table are 16-byte aligned. - for (const ChunkAndOffset &C : AddressTakenSyms) - if (C.InputChunk->Alignment < 16) - C.InputChunk->Alignment = 16; + for (const ChunkAndOffset &c : addressTakenSyms) + if (c.inputChunk->getAlignment() < 16) + c.inputChunk->setAlignment(16); - maybeAddRVATable(std::move(AddressTakenSyms), "__guard_fids_table", + maybeAddRVATable(std::move(addressTakenSyms), "__guard_fids_table", "__guard_fids_count"); // Add the longjmp target table unless the user told us not to. - if (Config->GuardCF == GuardCFLevel::Full) - maybeAddRVATable(std::move(LongJmpTargets), "__guard_longjmp_table", + if (config->guardCF == GuardCFLevel::Full) + maybeAddRVATable(std::move(longJmpTargets), "__guard_longjmp_table", "__guard_longjmp_count"); // Set __guard_flags, which will be used in the load config to indicate that // /guard:cf was enabled. - uint32_t GuardFlags = uint32_t(coff_guard_flags::CFInstrumented) | + uint32_t guardFlags = uint32_t(coff_guard_flags::CFInstrumented) | uint32_t(coff_guard_flags::HasFidTable); - if (Config->GuardCF == GuardCFLevel::Full) - GuardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable); - Symbol *FlagSym = Symtab->findUnderscore("__guard_flags"); - cast(FlagSym)->setVA(GuardFlags); + if (config->guardCF == GuardCFLevel::Full) + guardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable); + Symbol *flagSym = symtab->findUnderscore("__guard_flags"); + cast(flagSym)->setVA(guardFlags); } // Take a list of input sections containing symbol table indices and add those // symbols to an RVA table. The challenge is that symbol RVAs are not known and // depend on the table size, so we can't directly build a set of integers. -void Writer::markSymbolsForRVATable(ObjFile *File, - ArrayRef SymIdxChunks, - SymbolRVASet &TableSymbols) { - for (SectionChunk *C : SymIdxChunks) { +void Writer::markSymbolsForRVATable(ObjFile *file, + ArrayRef symIdxChunks, + SymbolRVASet &tableSymbols) { + for (SectionChunk *c : symIdxChunks) { // Skip sections discarded by linker GC. This comes up when a .gfids section // is associated with something like a vtable and the vtable is discarded. // In this case, the associated gfids section is discarded, and we don't // mark the virtual member functions as address-taken by the vtable. - if (!C->Live) + if (!c->live) continue; // Validate that the contents look like symbol table indices. - ArrayRef Data = C->getContents(); - if (Data.size() % 4 != 0) { - warn("ignoring " + C->getSectionName() + - " symbol table index section in object " + toString(File)); + ArrayRef data = c->getContents(); + if (data.size() % 4 != 0) { + warn("ignoring " + c->getSectionName() + + " symbol table index section in object " + toString(file)); continue; } // Read each symbol table index and check if that symbol was included in the // final link. If so, add it to the table symbol set. - ArrayRef SymIndices( - reinterpret_cast(Data.data()), Data.size() / 4); - ArrayRef ObjSymbols = File->getSymbols(); - for (uint32_t SymIndex : SymIndices) { - if (SymIndex >= ObjSymbols.size()) { + ArrayRef symIndices( + reinterpret_cast(data.data()), data.size() / 4); + ArrayRef objSymbols = file->getSymbols(); + for (uint32_t symIndex : symIndices) { + if (symIndex >= objSymbols.size()) { warn("ignoring invalid symbol table index in section " + - C->getSectionName() + " in object " + toString(File)); + c->getSectionName() + " in object " + toString(file)); continue; } - if (Symbol *S = ObjSymbols[SymIndex]) { - if (S->isLive()) - addSymbolToRVASet(TableSymbols, cast(S)); + if (Symbol *s = objSymbols[symIndex]) { + if (s->isLive()) + addSymbolToRVASet(tableSymbols, cast(s)); } } } } // Replace the absolute table symbol with a synthetic symbol pointing to -// TableChunk so that we can emit base relocations for it and resolve section +// tableChunk so that we can emit base relocations for it and resolve section // relative relocations. -void Writer::maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym, - StringRef CountSym) { - if (TableSymbols.empty()) +void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, + StringRef countSym) { + if (tableSymbols.empty()) return; - RVATableChunk *TableChunk = make(std::move(TableSymbols)); - RdataSec->addChunk(TableChunk); + RVATableChunk *tableChunk = make(std::move(tableSymbols)); + rdataSec->addChunk(tableChunk); - Symbol *T = Symtab->findUnderscore(TableSym); - Symbol *C = Symtab->findUnderscore(CountSym); - replaceSymbol(T, T->getName(), TableChunk); - cast(C)->setVA(TableChunk->getSize() / 4); + Symbol *t = symtab->findUnderscore(tableSym); + Symbol *c = symtab->findUnderscore(countSym); + replaceSymbol(t, t->getName(), tableChunk); + cast(c)->setVA(tableChunk->getSize() / 4); } // MinGW specific. Gather all relocations that are imported from a DLL even @@ -1477,26 +1639,26 @@ void Writer::maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym, // uses for fixing them up, and provide the synthetic symbols that the // runtime uses for finding the table. void Writer::createRuntimePseudoRelocs() { - std::vector Rels; + std::vector rels; - for (Chunk *C : Symtab->getChunks()) { - auto *SC = dyn_cast(C); - if (!SC || !SC->Live) + for (Chunk *c : symtab->getChunks()) { + auto *sc = dyn_cast(c); + if (!sc || !sc->live) continue; - SC->getRuntimePseudoRelocs(Rels); + sc->getRuntimePseudoRelocs(rels); } - if (!Rels.empty()) - log("Writing " + Twine(Rels.size()) + " runtime pseudo relocations"); - PseudoRelocTableChunk *Table = make(Rels); - RdataSec->addChunk(Table); - EmptyChunk *EndOfList = make(); - RdataSec->addChunk(EndOfList); - - Symbol *HeadSym = Symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__"); - Symbol *EndSym = Symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__"); - replaceSymbol(HeadSym, HeadSym->getName(), Table); - replaceSymbol(EndSym, EndSym->getName(), EndOfList); + if (!rels.empty()) + log("Writing " + Twine(rels.size()) + " runtime pseudo relocations"); + PseudoRelocTableChunk *table = make(rels); + rdataSec->addChunk(table); + EmptyChunk *endOfList = make(); + rdataSec->addChunk(endOfList); + + Symbol *headSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__"); + Symbol *endSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__"); + replaceSymbol(headSym, headSym->getName(), table); + replaceSymbol(endSym, endSym->getName(), endOfList); } // MinGW specific. @@ -1505,32 +1667,32 @@ void Writer::createRuntimePseudoRelocs() { // There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__ // and __DTOR_LIST__ respectively. void Writer::insertCtorDtorSymbols() { - AbsolutePointerChunk *CtorListHead = make(-1); - AbsolutePointerChunk *CtorListEnd = make(0); - AbsolutePointerChunk *DtorListHead = make(-1); - AbsolutePointerChunk *DtorListEnd = make(0); - CtorsSec->insertChunkAtStart(CtorListHead); - CtorsSec->addChunk(CtorListEnd); - DtorsSec->insertChunkAtStart(DtorListHead); - DtorsSec->addChunk(DtorListEnd); - - Symbol *CtorListSym = Symtab->findUnderscore("__CTOR_LIST__"); - Symbol *DtorListSym = Symtab->findUnderscore("__DTOR_LIST__"); - replaceSymbol(CtorListSym, CtorListSym->getName(), - CtorListHead); - replaceSymbol(DtorListSym, DtorListSym->getName(), - DtorListHead); + AbsolutePointerChunk *ctorListHead = make(-1); + AbsolutePointerChunk *ctorListEnd = make(0); + AbsolutePointerChunk *dtorListHead = make(-1); + AbsolutePointerChunk *dtorListEnd = make(0); + ctorsSec->insertChunkAtStart(ctorListHead); + ctorsSec->addChunk(ctorListEnd); + dtorsSec->insertChunkAtStart(dtorListHead); + dtorsSec->addChunk(dtorListEnd); + + Symbol *ctorListSym = symtab->findUnderscore("__CTOR_LIST__"); + Symbol *dtorListSym = symtab->findUnderscore("__DTOR_LIST__"); + replaceSymbol(ctorListSym, ctorListSym->getName(), + ctorListHead); + replaceSymbol(dtorListSym, dtorListSym->getName(), + dtorListHead); } // Handles /section options to allow users to overwrite // section attributes. void Writer::setSectionPermissions() { - for (auto &P : Config->Section) { - StringRef Name = P.first; - uint32_t Perm = P.second; - for (OutputSection *Sec : OutputSections) - if (Sec->Name == Name) - Sec->setPermissions(Perm); + for (auto &p : config->section) { + StringRef name = p.first; + uint32_t perm = p.second; + for (OutputSection *sec : outputSections) + if (sec->name == name) + sec->setPermissions(perm); } } @@ -1538,18 +1700,19 @@ void Writer::setSectionPermissions() { void Writer::writeSections() { // Record the number of sections to apply section index relocations // against absolute symbols. See applySecIdx in Chunks.cpp.. - DefinedAbsolute::NumOutputSections = OutputSections.size(); + DefinedAbsolute::numOutputSections = outputSections.size(); - uint8_t *Buf = Buffer->getBufferStart(); - for (OutputSection *Sec : OutputSections) { - uint8_t *SecBuf = Buf + Sec->getFileOff(); + uint8_t *buf = buffer->getBufferStart(); + for (OutputSection *sec : outputSections) { + uint8_t *secBuf = buf + sec->getFileOff(); // Fill gaps between functions in .text with INT3 instructions // instead of leaving as NUL bytes (which can be interpreted as // ADD instructions). - if (Sec->Header.Characteristics & IMAGE_SCN_CNT_CODE) - memset(SecBuf, 0xCC, Sec->getRawSize()); - for_each(parallel::par, Sec->Chunks.begin(), Sec->Chunks.end(), - [&](Chunk *C) { C->writeTo(SecBuf); }); + if (sec->header.Characteristics & IMAGE_SCN_CNT_CODE) + memset(secBuf, 0xCC, sec->getRawSize()); + parallelForEach(sec->chunks, [&](Chunk *c) { + c->writeTo(secBuf + c->getRVA() - sec->getRVA()); + }); } } @@ -1560,8 +1723,8 @@ void Writer::writeBuildId() { // 2) In all cases, the PE COFF file header also contains a timestamp. // For reproducibility, instead of a timestamp we want to use a hash of the // PE contents. - if (Config->Debug) { - assert(BuildId && "BuildId is not set!"); + if (config->debug) { + assert(buildId && "BuildId is not set!"); // BuildId->BuildId was filled in when the PDB was written. } @@ -1569,62 +1732,65 @@ void Writer::writeBuildId() { // "timestamp" in the COFF file header, and the ones in the coff debug // directory. Now we can hash the file and write that hash to the various // timestamp fields in the file. - StringRef OutputFileData( - reinterpret_cast(Buffer->getBufferStart()), - Buffer->getBufferSize()); + StringRef outputFileData( + reinterpret_cast(buffer->getBufferStart()), + buffer->getBufferSize()); - uint32_t Timestamp = Config->Timestamp; - uint64_t Hash = 0; - bool GenerateSyntheticBuildId = - Config->MinGW && Config->Debug && Config->PDBPath.empty(); + uint32_t timestamp = config->timestamp; + uint64_t hash = 0; + bool generateSyntheticBuildId = + config->mingw && config->debug && config->pdbPath.empty(); - if (Config->Repro || GenerateSyntheticBuildId) - Hash = xxHash64(OutputFileData); + if (config->repro || generateSyntheticBuildId) + hash = xxHash64(outputFileData); - if (Config->Repro) - Timestamp = static_cast(Hash); + if (config->repro) + timestamp = static_cast(hash); - if (GenerateSyntheticBuildId) { + if (generateSyntheticBuildId) { // For MinGW builds without a PDB file, we still generate a build id // to allow associating a crash dump to the executable. - BuildId->BuildId->PDB70.CVSignature = OMF::Signature::PDB70; - BuildId->BuildId->PDB70.Age = 1; - memcpy(BuildId->BuildId->PDB70.Signature, &Hash, 8); + buildId->buildId->PDB70.CVSignature = OMF::Signature::PDB70; + buildId->buildId->PDB70.Age = 1; + memcpy(buildId->buildId->PDB70.Signature, &hash, 8); // xxhash only gives us 8 bytes, so put some fixed data in the other half. - memcpy(&BuildId->BuildId->PDB70.Signature[8], "LLD PDB.", 8); + memcpy(&buildId->buildId->PDB70.Signature[8], "LLD PDB.", 8); } - if (DebugDirectory) - DebugDirectory->setTimeDateStamp(Timestamp); + if (debugDirectory) + debugDirectory->setTimeDateStamp(timestamp); - uint8_t *Buf = Buffer->getBufferStart(); - Buf += DOSStubSize + sizeof(PEMagic); - object::coff_file_header *CoffHeader = - reinterpret_cast(Buf); - CoffHeader->TimeDateStamp = Timestamp; + uint8_t *buf = buffer->getBufferStart(); + buf += dosStubSize + sizeof(PEMagic); + object::coff_file_header *coffHeader = + reinterpret_cast(buf); + coffHeader->TimeDateStamp = timestamp; } // Sort .pdata section contents according to PE/COFF spec 5.5. void Writer::sortExceptionTable() { - if (!FirstPdata) + if (!firstPdata) return; // We assume .pdata contains function table entries only. - auto BufAddr = [&](Chunk *C) { - return Buffer->getBufferStart() + C->getOutputSection()->getFileOff() + - C->getRVA() - C->getOutputSection()->getRVA(); + auto bufAddr = [&](Chunk *c) { + OutputSection *os = c->getOutputSection(); + return buffer->getBufferStart() + os->getFileOff() + c->getRVA() - + os->getRVA(); }; - uint8_t *Begin = BufAddr(FirstPdata); - uint8_t *End = BufAddr(LastPdata) + LastPdata->getSize(); - if (Config->Machine == AMD64) { - struct Entry { ulittle32_t Begin, End, Unwind; }; - sort(parallel::par, (Entry *)Begin, (Entry *)End, - [](const Entry &A, const Entry &B) { return A.Begin < B.Begin; }); + uint8_t *begin = bufAddr(firstPdata); + uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize(); + if (config->machine == AMD64) { + struct Entry { ulittle32_t begin, end, unwind; }; + parallelSort( + MutableArrayRef((Entry *)begin, (Entry *)end), + [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); return; } - if (Config->Machine == ARMNT || Config->Machine == ARM64) { - struct Entry { ulittle32_t Begin, Unwind; }; - sort(parallel::par, (Entry *)Begin, (Entry *)End, - [](const Entry &A, const Entry &B) { return A.Begin < B.Begin; }); + if (config->machine == ARMNT || config->machine == ARM64) { + struct Entry { ulittle32_t begin, unwind; }; + parallelSort( + MutableArrayRef((Entry *)begin, (Entry *)end), + [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); return; } errs() << "warning: don't know how to handle .pdata.\n"; @@ -1644,76 +1810,92 @@ void Writer::sortExceptionTable() { // pointers in the order that they are listed in the object file (top to // bottom), otherwise global objects might not be initialized in the // correct order. -void Writer::sortCRTSectionChunks(std::vector &Chunks) { - auto SectionChunkOrder = [](const Chunk *A, const Chunk *B) { - auto SA = dyn_cast(A); - auto SB = dyn_cast(B); - assert(SA && SB && "Non-section chunks in CRT section!"); +void Writer::sortCRTSectionChunks(std::vector &chunks) { + auto sectionChunkOrder = [](const Chunk *a, const Chunk *b) { + auto sa = dyn_cast(a); + auto sb = dyn_cast(b); + assert(sa && sb && "Non-section chunks in CRT section!"); - StringRef SAObj = SA->File->MB.getBufferIdentifier(); - StringRef SBObj = SB->File->MB.getBufferIdentifier(); + StringRef sAObj = sa->file->mb.getBufferIdentifier(); + StringRef sBObj = sb->file->mb.getBufferIdentifier(); - return SAObj == SBObj && SA->getSectionNumber() < SB->getSectionNumber(); + return sAObj == sBObj && sa->getSectionNumber() < sb->getSectionNumber(); }; - std::stable_sort(Chunks.begin(), Chunks.end(), SectionChunkOrder); + llvm::stable_sort(chunks, sectionChunkOrder); - if (Config->Verbose) { - for (auto &C : Chunks) { - auto SC = dyn_cast(C); - log(" " + SC->File->MB.getBufferIdentifier().str() + - ", SectionID: " + Twine(SC->getSectionNumber())); + if (config->verbose) { + for (auto &c : chunks) { + auto sc = dyn_cast(c); + log(" " + sc->file->mb.getBufferIdentifier().str() + + ", SectionID: " + Twine(sc->getSectionNumber())); } } } -OutputSection *Writer::findSection(StringRef Name) { - for (OutputSection *Sec : OutputSections) - if (Sec->Name == Name) - return Sec; +OutputSection *Writer::findSection(StringRef name) { + for (OutputSection *sec : outputSections) + if (sec->name == name) + return sec; return nullptr; } uint32_t Writer::getSizeOfInitializedData() { - uint32_t Res = 0; - for (OutputSection *S : OutputSections) - if (S->Header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) - Res += S->getRawSize(); - return Res; + uint32_t res = 0; + for (OutputSection *s : outputSections) + if (s->header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) + res += s->getRawSize(); + return res; } // Add base relocations to .reloc section. void Writer::addBaserels() { - if (!Config->Relocatable) + if (!config->relocatable) return; - RelocSec->Chunks.clear(); - std::vector V; - for (OutputSection *Sec : OutputSections) { - if (Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) + relocSec->chunks.clear(); + std::vector v; + for (OutputSection *sec : outputSections) { + if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) continue; // Collect all locations for base relocations. - for (Chunk *C : Sec->Chunks) - C->getBaserels(&V); + for (Chunk *c : sec->chunks) + c->getBaserels(&v); // Add the addresses to .reloc section. - if (!V.empty()) - addBaserelBlocks(V); - V.clear(); + if (!v.empty()) + addBaserelBlocks(v); + v.clear(); } } // Add addresses to .reloc section. Note that addresses are grouped by page. -void Writer::addBaserelBlocks(std::vector &V) { - const uint32_t Mask = ~uint32_t(PageSize - 1); - uint32_t Page = V[0].RVA & Mask; - size_t I = 0, J = 1; - for (size_t E = V.size(); J < E; ++J) { - uint32_t P = V[J].RVA & Mask; - if (P == Page) +void Writer::addBaserelBlocks(std::vector &v) { + const uint32_t mask = ~uint32_t(pageSize - 1); + uint32_t page = v[0].rva & mask; + size_t i = 0, j = 1; + for (size_t e = v.size(); j < e; ++j) { + uint32_t p = v[j].rva & mask; + if (p == page) continue; - RelocSec->addChunk(make(Page, &V[I], &V[0] + J)); - I = J; - Page = P; + relocSec->addChunk(make(page, &v[i], &v[0] + j)); + i = j; + page = p; } - if (I == J) + if (i == j) return; - RelocSec->addChunk(make(Page, &V[I], &V[0] + J)); + relocSec->addChunk(make(page, &v[i], &v[0] + j)); +} + +PartialSection *Writer::createPartialSection(StringRef name, + uint32_t outChars) { + PartialSection *&pSec = partialSections[{name, outChars}]; + if (pSec) + return pSec; + pSec = make(name, outChars); + return pSec; +} + +PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) { + auto it = partialSections.find({name, outChars}); + if (it != partialSections.end()) + return it->second; + return nullptr; } diff --git a/COFF/Writer.h b/COFF/Writer.h index 7275824..96389df 100644 --- a/COFF/Writer.h +++ b/COFF/Writer.h @@ -1,9 +1,8 @@ //===- Writer.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 // //===----------------------------------------------------------------------===// @@ -19,10 +18,19 @@ namespace lld { namespace coff { -static const int PageSize = 4096; +static const int pageSize = 4096; void writeResult(); +class PartialSection { +public: + PartialSection(StringRef n, uint32_t chars) + : name(n), characteristics(chars) {} + StringRef name; + unsigned characteristics; + std::vector chunks; +}; + // OutputSection represents a section in an output file. It's a // container of chunks. OutputSection and Chunk are 1:N relationship. // Chunks cannot belong to more than one OutputSections. The writer @@ -30,46 +38,48 @@ void writeResult(); // non-overlapping file offsets and RVAs. class OutputSection { public: - OutputSection(llvm::StringRef N, uint32_t Chars) : Name(N) { - Header.Characteristics = Chars; + OutputSection(llvm::StringRef n, uint32_t chars) : name(n) { + header.Characteristics = chars; } - void addChunk(Chunk *C); - void insertChunkAtStart(Chunk *C); - void merge(OutputSection *Other); - void addPermissions(uint32_t C); - void setPermissions(uint32_t C); - uint64_t getRVA() { return Header.VirtualAddress; } - uint64_t getFileOff() { return Header.PointerToRawData; } - void writeHeaderTo(uint8_t *Buf); + void addChunk(Chunk *c); + void insertChunkAtStart(Chunk *c); + void merge(OutputSection *other); + void setPermissions(uint32_t c); + uint64_t getRVA() { return header.VirtualAddress; } + uint64_t getFileOff() { return header.PointerToRawData; } + void writeHeaderTo(uint8_t *buf); + void addContributingPartialSection(PartialSection *sec); // Returns the size of this section in an executable memory image. // This may be smaller than the raw size (the raw size is multiple // of disk sector size, so there may be padding at end), or may be // larger (if that's the case, the loader reserves spaces after end // of raw data). - uint64_t getVirtualSize() { return Header.VirtualSize; } + uint64_t getVirtualSize() { return header.VirtualSize; } // Returns the size of the section in the output file. - uint64_t getRawSize() { return Header.SizeOfRawData; } + uint64_t getRawSize() { return header.SizeOfRawData; } // Set offset into the string table storing this section name. // Used only when the name is longer than 8 bytes. - void setStringTableOff(uint32_t V) { StringTableOff = V; } + void setStringTableOff(uint32_t v) { stringTableOff = v; } // N.B. The section index is one based. - uint32_t SectionIndex = 0; + uint32_t sectionIndex = 0; + + llvm::StringRef name; + llvm::object::coff_section header = {}; - llvm::StringRef Name; - llvm::object::coff_section Header = {}; + std::vector chunks; + std::vector origChunks; - std::vector Chunks; - std::vector OrigChunks; + std::vector contribSections; private: - uint32_t StringTableOff = 0; + uint32_t stringTableOff = 0; }; -} -} +} // namespace coff +} // namespace lld #endif diff --git a/Common/Args.cpp b/Common/Args.cpp index 3f0671d..4ea3a43 100644 --- a/Common/Args.cpp +++ b/Common/Args.cpp @@ -1,9 +1,8 @@ //===- Args.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 // //===----------------------------------------------------------------------===// @@ -18,56 +17,66 @@ using namespace llvm; using namespace lld; -int lld::args::getInteger(opt::InputArgList &Args, unsigned Key, int Default) { - auto *A = Args.getLastArg(Key); - if (!A) +// TODO(sbc): Remove this once CGOptLevel can be set completely based on bitcode +// function metadata. +CodeGenOpt::Level lld::args::getCGOptLevel(int optLevelLTO) { + if (optLevelLTO == 3) + return CodeGenOpt::Aggressive; + assert(optLevelLTO < 3); + return CodeGenOpt::Default; +} + +int64_t lld::args::getInteger(opt::InputArgList &args, unsigned key, + int64_t Default) { + auto *a = args.getLastArg(key); + if (!a) return Default; - int V; - if (to_integer(A->getValue(), V, 10)) - return V; + int64_t v; + if (to_integer(a->getValue(), v, 10)) + return v; - StringRef Spelling = Args.getArgString(A->getIndex()); - error(Spelling + ": number expected, but got '" + A->getValue() + "'"); + StringRef spelling = args.getArgString(a->getIndex()); + error(spelling + ": number expected, but got '" + a->getValue() + "'"); return 0; } -std::vector lld::args::getStrings(opt::InputArgList &Args, int Id) { - std::vector V; - for (auto *Arg : Args.filtered(Id)) - V.push_back(Arg->getValue()); - return V; +std::vector lld::args::getStrings(opt::InputArgList &args, int id) { + std::vector v; + for (auto *arg : args.filtered(id)) + v.push_back(arg->getValue()); + return v; } -uint64_t lld::args::getZOptionValue(opt::InputArgList &Args, int Id, - StringRef Key, uint64_t Default) { - for (auto *Arg : Args.filtered_reverse(Id)) { - std::pair KV = StringRef(Arg->getValue()).split('='); - if (KV.first == Key) { - uint64_t Result = Default; - if (!to_integer(KV.second, Result)) - error("invalid " + Key + ": " + KV.second); - return Result; +uint64_t lld::args::getZOptionValue(opt::InputArgList &args, int id, + StringRef key, uint64_t Default) { + for (auto *arg : args.filtered_reverse(id)) { + std::pair kv = StringRef(arg->getValue()).split('='); + if (kv.first == key) { + uint64_t result = Default; + if (!to_integer(kv.second, result)) + error("invalid " + key + ": " + kv.second); + return result; } } return Default; } -std::vector lld::args::getLines(MemoryBufferRef MB) { - SmallVector Arr; - MB.getBuffer().split(Arr, '\n'); +std::vector lld::args::getLines(MemoryBufferRef mb) { + SmallVector arr; + mb.getBuffer().split(arr, '\n'); - std::vector Ret; - for (StringRef S : Arr) { - S = S.trim(); - if (!S.empty() && S[0] != '#') - Ret.push_back(S); + std::vector ret; + for (StringRef s : arr) { + s = s.trim(); + if (!s.empty() && s[0] != '#') + ret.push_back(s); } - return Ret; + return ret; } -StringRef lld::args::getFilenameWithoutExe(StringRef Path) { - if (Path.endswith_lower(".exe")) - return sys::path::stem(Path); - return sys::path::filename(Path); +StringRef lld::args::getFilenameWithoutExe(StringRef path) { + if (path.endswith_lower(".exe")) + return sys::path::stem(path); + return sys::path::filename(path); } diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt index a45fe20..70849cc 100644 --- a/Common/CMakeLists.txt +++ b/Common/CMakeLists.txt @@ -2,15 +2,42 @@ if(NOT LLD_BUILT_STANDALONE) set(tablegen_deps intrinsics_gen) endif() +find_first_existing_vc_file("${LLVM_MAIN_SRC_DIR}" llvm_vc) +find_first_existing_vc_file("${LLD_SOURCE_DIR}" lld_vc) + +set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/VCSVersion.inc") +set(generate_vcs_version_script "${LLVM_CMAKE_PATH}/GenerateVersionFromVCS.cmake") + +if(lld_vc) + set(lld_source_dir ${LLD_SOURCE_DIR}) +endif() + +add_custom_command(OUTPUT "${version_inc}" + DEPENDS "${lld_vc}" "${generate_vcs_version_script}" + COMMAND ${CMAKE_COMMAND} "-DNAMES=LLD" + "-DLLD_SOURCE_DIR=${LLD_SOURCE_DIR}" + "-DHEADER_FILE=${version_inc}" + -P "${generate_vcs_version_script}") + +# Mark the generated header as being generated. +set_source_files_properties("${version_inc}" + PROPERTIES GENERATED TRUE + HEADER_FILE_ONLY TRUE) + +set_property(SOURCE Version.cpp APPEND PROPERTY + COMPILE_DEFINITIONS "HAVE_VCS_VERSION_INC") + add_lld_library(lldCommon Args.cpp ErrorHandler.cpp + Filesystem.cpp Memory.cpp Reproduce.cpp Strings.cpp TargetOptionsCommandFlags.cpp Threads.cpp Timer.cpp + VCSVersion.inc Version.cpp ADDITIONAL_HEADER_DIRS diff --git a/Common/ErrorHandler.cpp b/Common/ErrorHandler.cpp index c059516..c87c060 100644 --- a/Common/ErrorHandler.cpp +++ b/Common/ErrorHandler.cpp @@ -1,9 +1,8 @@ //===- ErrorHandler.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 "llvm/Support/ManagedStatic.h" #include "llvm/Support/raw_ostream.h" #include +#include #if !defined(_MSC_VER) && !defined(__MINGW32__) #include @@ -27,29 +27,29 @@ using namespace lld; // The functions defined in this file can be called from multiple threads, // but outs() or errs() are not thread-safe. We protect them using a mutex. -static std::mutex Mu; +static std::mutex mu; // Prints "\n" or does nothing, depending on Msg contents of // the previous call of this function. -static void newline(raw_ostream *ErrorOS, const Twine &Msg) { +static void newline(raw_ostream *errorOS, const Twine &msg) { // True if the previous error message contained "\n". // We want to separate multi-line error messages with a newline. - static bool Flag; + static bool flag; - if (Flag) - *ErrorOS << "\n"; - Flag = StringRef(Msg.str()).contains('\n'); + if (flag) + *errorOS << "\n"; + flag = StringRef(msg.str()).contains('\n'); } ErrorHandler &lld::errorHandler() { - static ErrorHandler Handler; - return Handler; + static ErrorHandler handler; + return handler; } -void lld::exitLld(int Val) { +void lld::exitLld(int val) { // Delete any temporary file, while keeping the memory mapping open. - if (errorHandler().OutputBuffer) - errorHandler().OutputBuffer->discard(); + if (errorHandler().outputBuffer) + errorHandler().outputBuffer->discard(); // Dealloc/destroy ManagedStatic variables before calling // _exit(). In a non-LTO build, this is a nop. In an LTO @@ -58,87 +58,121 @@ void lld::exitLld(int Val) { outs().flush(); errs().flush(); - _exit(Val); + _exit(val); } -void lld::diagnosticHandler(const DiagnosticInfo &DI) { - SmallString<128> S; - raw_svector_ostream OS(S); - DiagnosticPrinterRawOStream DP(OS); - DI.print(DP); - switch (DI.getSeverity()) { +void lld::diagnosticHandler(const DiagnosticInfo &di) { + SmallString<128> s; + raw_svector_ostream os(s); + DiagnosticPrinterRawOStream dp(os); + di.print(dp); + switch (di.getSeverity()) { case DS_Error: - error(S); + error(s); break; case DS_Warning: - warn(S); + warn(s); break; case DS_Remark: case DS_Note: - message(S); + message(s); break; } } -void lld::checkError(Error E) { - handleAllErrors(std::move(E), - [&](ErrorInfoBase &EIB) { error(EIB.message()); }); +void lld::checkError(Error e) { + handleAllErrors(std::move(e), + [&](ErrorInfoBase &eib) { error(eib.message()); }); +} + +static std::string getLocation(std::string msg, std::string defaultMsg) { + static std::vector Regexes{ + std::regex(R"(^undefined symbol:.*\n>>> referenced by (\S+):(\d+)\n.*)"), + std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)"), + std::regex( + R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"), + std::regex( + R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+).*)"), + std::regex( + R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"), + std::regex( + R"(^undefined (internal|hidden|protected) symbol: .*\n>>> referenced by (\S+):(\d+)\n.*)"), + std::regex(R"((\S+):(\d+): unclosed quote)"), + }; + + std::smatch Match; + for (std::regex &Re : Regexes) { + if (std::regex_search(msg, Match, Re)) { + return Match.size() > 2 ? Match.str(1) + "(" + Match.str(2) + ")" + : Match.str(1); + } + } + return defaultMsg; } -void ErrorHandler::print(StringRef S, raw_ostream::Colors C) { - *ErrorOS << LogName << ": "; - if (ColorDiagnostics) { - ErrorOS->changeColor(C, true); - *ErrorOS << S; - ErrorOS->resetColor(); +void ErrorHandler::printHeader(StringRef s, raw_ostream::Colors c, + const Twine &msg) { + + if (vsDiagnostics) { + // A Visual Studio-style error message starts with an error location. + // If a location cannot be extracted then we default to LogName. + *errorOS << getLocation(msg.str(), logName) << ": "; + } else { + *errorOS << logName << ": "; + } + + if (colorDiagnostics) { + errorOS->changeColor(c, true); + *errorOS << s; + errorOS->resetColor(); } else { - *ErrorOS << S; + *errorOS << s; } } -void ErrorHandler::log(const Twine &Msg) { - if (Verbose) { - std::lock_guard Lock(Mu); - *ErrorOS << LogName << ": " << Msg << "\n"; +void ErrorHandler::log(const Twine &msg) { + if (verbose) { + std::lock_guard lock(mu); + *errorOS << logName << ": " << msg << "\n"; } } -void ErrorHandler::message(const Twine &Msg) { - std::lock_guard Lock(Mu); - outs() << Msg << "\n"; +void ErrorHandler::message(const Twine &msg) { + std::lock_guard lock(mu); + outs() << msg << "\n"; outs().flush(); } -void ErrorHandler::warn(const Twine &Msg) { - if (FatalWarnings) { - error(Msg); +void ErrorHandler::warn(const Twine &msg) { + if (fatalWarnings) { + error(msg); return; } - std::lock_guard Lock(Mu); - newline(ErrorOS, Msg); - print("warning: ", raw_ostream::MAGENTA); - *ErrorOS << Msg << "\n"; + std::lock_guard lock(mu); + newline(errorOS, msg); + printHeader("warning: ", raw_ostream::MAGENTA, msg); + *errorOS << msg << "\n"; } -void ErrorHandler::error(const Twine &Msg) { - std::lock_guard Lock(Mu); - newline(ErrorOS, Msg); - - if (ErrorLimit == 0 || ErrorCount < ErrorLimit) { - print("error: ", raw_ostream::RED); - *ErrorOS << Msg << "\n"; - } else if (ErrorCount == ErrorLimit) { - print("error: ", raw_ostream::RED); - *ErrorOS << ErrorLimitExceededMsg << "\n"; - if (ExitEarly) +void ErrorHandler::error(const Twine &msg) { + std::lock_guard lock(mu); + newline(errorOS, msg); + + if (errorLimit == 0 || errorCount < errorLimit) { + printHeader("error: ", raw_ostream::RED, msg); + *errorOS << msg << "\n"; + } else if (errorCount == errorLimit) { + printHeader("error: ", raw_ostream::RED, msg); + *errorOS << errorLimitExceededMsg << "\n"; + if (exitEarly) exitLld(1); } - ++ErrorCount; + ++errorCount; } -void ErrorHandler::fatal(const Twine &Msg) { - error(Msg); +void ErrorHandler::fatal(const Twine &msg) { + error(msg); exitLld(1); } diff --git a/Common/Filesystem.cpp b/Common/Filesystem.cpp new file mode 100644 index 0000000..c3d3f99 --- /dev/null +++ b/Common/Filesystem.cpp @@ -0,0 +1,99 @@ +//===- Filesystem.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains a few utility functions to handle files. +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Filesystem.h" +#include "lld/Common/Threads.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/FileSystem.h" +#if LLVM_ON_UNIX +#include +#endif +#include + +using namespace llvm; +using namespace lld; + +// Removes a given file asynchronously. This is a performance hack, +// so remove this when operating systems are improved. +// +// On Linux (and probably on other Unix-like systems), unlink(2) is a +// noticeably slow system call. As of 2016, unlink takes 250 +// milliseconds to remove a 1 GB file on ext4 filesystem on my machine. +// +// To create a new result file, we first remove existing file. So, if +// you repeatedly link a 1 GB program in a regular compile-link-debug +// cycle, every cycle wastes 250 milliseconds only to remove a file. +// Since LLD can link a 1 GB binary in about 5 seconds, that waste +// actually counts. +// +// This function spawns a background thread to remove the file. +// The calling thread returns almost immediately. +void lld::unlinkAsync(StringRef path) { +// Removing a file is async on windows. +#if defined(_WIN32) + sys::fs::remove(path); +#else + if (!threadsEnabled || !sys::fs::exists(path) || + !sys::fs::is_regular_file(path)) + return; + + // We cannot just remove path from a different thread because we are now going + // to create path as a new file. + // Instead we open the file and unlink it on this thread. The unlink is fast + // since the open fd guarantees that it is not removing the last reference. + int fd; + std::error_code ec = sys::fs::openFileForRead(path, fd); + sys::fs::remove(path); + + if (ec) + return; + + // close and therefore remove TempPath in background. + std::mutex m; + std::condition_variable cv; + bool started = false; + std::thread([&, fd] { + { + std::lock_guard l(m); + started = true; + cv.notify_all(); + } + ::close(fd); + }).detach(); + + // GLIBC 2.26 and earlier have race condition that crashes an entire process + // if the main thread calls exit(2) while other thread is starting up. + std::unique_lock l(m); + cv.wait(l, [&] { return started; }); +#endif +} + +// Simulate file creation to see if Path is writable. +// +// Determining whether a file is writable or not is amazingly hard, +// and after all the only reliable way of doing that is to actually +// create a file. But we don't want to do that in this function +// because LLD shouldn't update any file if it will end in a failure. +// We also don't want to reimplement heuristics to determine if a +// file is writable. So we'll let FileOutputBuffer do the work. +// +// FileOutputBuffer doesn't touch a desitnation file until commit() +// is called. We use that class without calling commit() to predict +// if the given file is writable. +std::error_code lld::tryCreateFile(StringRef path) { + if (path.empty()) + return std::error_code(); + if (path == "-") + return std::error_code(); + return errorToErrorCode(FileOutputBuffer::create(path, 1).takeError()); +} diff --git a/Common/Memory.cpp b/Common/Memory.cpp index efc5bcc..c53e1d3 100644 --- a/Common/Memory.cpp +++ b/Common/Memory.cpp @@ -1,9 +1,8 @@ //===- Memory.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 // //===----------------------------------------------------------------------===// @@ -12,12 +11,12 @@ using namespace llvm; using namespace lld; -BumpPtrAllocator lld::BAlloc; -StringSaver lld::Saver{BAlloc}; -std::vector lld::SpecificAllocBase::Instances; +BumpPtrAllocator lld::bAlloc; +StringSaver lld::saver{bAlloc}; +std::vector lld::SpecificAllocBase::instances; void lld::freeArena() { - for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances) - Alloc->reset(); - BAlloc.Reset(); + for (SpecificAllocBase *alloc : SpecificAllocBase::instances) + alloc->reset(); + bAlloc.Reset(); } diff --git a/Common/Reproduce.cpp b/Common/Reproduce.cpp index 7be4ea6..24210c4 100644 --- a/Common/Reproduce.cpp +++ b/Common/Reproduce.cpp @@ -1,9 +1,8 @@ //===- Reproduce.cpp - Utilities for creating reproducers -----------------===// // -// 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,41 @@ using namespace llvm::sys; // assuming that the current directory is "/home/john/bar". // Returned string is a forward slash separated path even on Windows to avoid // a mess with backslash-as-escape and backslash-as-path-separator. -std::string lld::relativeToRoot(StringRef Path) { - SmallString<128> Abs = Path; - if (fs::make_absolute(Abs)) - return Path; - path::remove_dots(Abs, /*remove_dot_dot=*/true); +std::string lld::relativeToRoot(StringRef path) { + SmallString<128> abs = path; + if (fs::make_absolute(abs)) + return path; + path::remove_dots(abs, /*remove_dot_dot=*/true); // This is Windows specific. root_name() returns a drive letter // (e.g. "c:") or a UNC name (//net). We want to keep it as part // of the result. - SmallString<128> Res; - StringRef Root = path::root_name(Abs); - if (Root.endswith(":")) - Res = Root.drop_back(); - else if (Root.startswith("//")) - Res = Root.substr(2); - - path::append(Res, path::relative_path(Abs)); - return path::convert_to_slash(Res); + SmallString<128> res; + StringRef root = path::root_name(abs); + if (root.endswith(":")) + res = root.drop_back(); + else if (root.startswith("//")) + res = root.substr(2); + + path::append(res, path::relative_path(abs)); + return path::convert_to_slash(res); } // Quote a given string if it contains a space character. -std::string lld::quote(StringRef S) { - if (S.contains(' ')) - return ("\"" + S + "\"").str(); - return S; -} - -std::string lld::rewritePath(StringRef S) { - if (fs::exists(S)) - return relativeToRoot(S); - return S; +std::string lld::quote(StringRef s) { + if (s.contains(' ')) + return ("\"" + s + "\"").str(); + return s; } -std::string lld::toString(const opt::Arg &Arg) { - std::string K = Arg.getSpelling(); - if (Arg.getNumValues() == 0) - return K; - std::string V = quote(Arg.getValue()); - if (Arg.getOption().getRenderStyle() == opt::Option::RenderJoinedStyle) - return K + V; - return K + " " + V; +// Converts an Arg to a string representation suitable for a response file. +// To show an Arg in a diagnostic, use Arg::getAsString() instead. +std::string lld::toString(const opt::Arg &arg) { + std::string k = arg.getSpelling(); + if (arg.getNumValues() == 0) + return k; + std::string v = quote(arg.getValue()); + if (arg.getOption().getRenderStyle() == opt::Option::RenderJoinedStyle) + return k + v; + return k + " " + v; } diff --git a/Common/Strings.cpp b/Common/Strings.cpp index 6f74865..0bf0662 100644 --- a/Common/Strings.cpp +++ b/Common/Strings.cpp @@ -1,9 +1,8 @@ //===- Strings.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 // //===----------------------------------------------------------------------===// @@ -20,85 +19,85 @@ using namespace llvm; using namespace lld; // Returns the demangled C++ symbol name for Name. -Optional lld::demangleItanium(StringRef Name) { +Optional lld::demangleItanium(StringRef name) { // itaniumDemangle can be used to demangle strings other than symbol // names which do not necessarily start with "_Z". Name can be // either a C or C++ symbol. Don't call itaniumDemangle if the name // does not look like a C++ symbol name to avoid getting unexpected // result for a C symbol that happens to match a mangled type name. - if (!Name.startswith("_Z")) + if (!name.startswith("_Z")) return None; - char *Buf = itaniumDemangle(Name.str().c_str(), nullptr, nullptr, nullptr); - if (!Buf) + char *buf = itaniumDemangle(name.str().c_str(), nullptr, nullptr, nullptr); + if (!buf) return None; - std::string S(Buf); - free(Buf); - return S; + std::string s(buf); + free(buf); + return s; } -Optional lld::demangleMSVC(StringRef Name) { - std::string Prefix; - if (Name.consume_front("__imp_")) - Prefix = "__declspec(dllimport) "; +Optional lld::demangleMSVC(StringRef name) { + std::string prefix; + if (name.consume_front("__imp_")) + prefix = "__declspec(dllimport) "; // Demangle only C++ names. - if (!Name.startswith("?")) + if (!name.startswith("?")) return None; - char *Buf = microsoftDemangle(Name.str().c_str(), nullptr, nullptr, nullptr); - if (!Buf) + char *buf = microsoftDemangle(name.str().c_str(), nullptr, nullptr, nullptr); + if (!buf) return None; - std::string S(Buf); - free(Buf); - return Prefix + S; + std::string s(buf); + free(buf); + return prefix + s; } -StringMatcher::StringMatcher(ArrayRef Pat) { - for (StringRef S : Pat) { - Expected Pat = GlobPattern::create(S); - if (!Pat) - error(toString(Pat.takeError())); +StringMatcher::StringMatcher(ArrayRef pat) { + for (StringRef s : pat) { + Expected pat = GlobPattern::create(s); + if (!pat) + error(toString(pat.takeError())); else - Patterns.push_back(*Pat); + patterns.push_back(*pat); } } -bool StringMatcher::match(StringRef S) const { - for (const GlobPattern &Pat : Patterns) - if (Pat.match(S)) +bool StringMatcher::match(StringRef s) const { + for (const GlobPattern &pat : patterns) + if (pat.match(s)) return true; return false; } // Converts a hex string (e.g. "deadbeef") to a vector. -std::vector lld::parseHex(StringRef S) { - std::vector Hex; - while (!S.empty()) { - StringRef B = S.substr(0, 2); - S = S.substr(2); - uint8_t H; - if (!to_integer(B, H, 16)) { - error("not a hexadecimal value: " + B); +std::vector lld::parseHex(StringRef s) { + std::vector hex; + while (!s.empty()) { + StringRef b = s.substr(0, 2); + s = s.substr(2); + uint8_t h; + if (!to_integer(b, h, 16)) { + error("not a hexadecimal value: " + b); return {}; } - Hex.push_back(H); + hex.push_back(h); } - return Hex; + return hex; } // Returns true if S is valid as a C language identifier. -bool lld::isValidCIdentifier(StringRef S) { - return !S.empty() && (isAlpha(S[0]) || S[0] == '_') && - std::all_of(S.begin() + 1, S.end(), - [](char C) { return C == '_' || isAlnum(C); }); +bool lld::isValidCIdentifier(StringRef s) { + return !s.empty() && (isAlpha(s[0]) || s[0] == '_') && + std::all_of(s.begin() + 1, s.end(), + [](char c) { return c == '_' || isAlnum(c); }); } // Write the contents of the a buffer to a file -void lld::saveBuffer(StringRef Buffer, const Twine &Path) { - std::error_code EC; - raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None); - if (EC) - error("cannot create " + Path + ": " + EC.message()); - OS << Buffer; +void lld::saveBuffer(StringRef buffer, const Twine &path) { + std::error_code ec; + raw_fd_ostream os(path.str(), ec, sys::fs::OpenFlags::F_None); + if (ec) + error("cannot create " + path + ": " + ec.message()); + os << buffer; } diff --git a/Common/TargetOptionsCommandFlags.cpp b/Common/TargetOptionsCommandFlags.cpp index 7a3fc51..d4c29d7 100644 --- a/Common/TargetOptionsCommandFlags.cpp +++ b/Common/TargetOptionsCommandFlags.cpp @@ -1,9 +1,8 @@ //===-- TargetOptionsCommandFlags.cpp ---------------------------*- 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 // //===----------------------------------------------------------------------===// // @@ -20,16 +19,17 @@ #include "llvm/Target/TargetOptions.h" // Define an externally visible version of -// InitTargetOptionsFromCodeGenFlags, so that its functionality can be +// initTargetOptionsFromCodeGenFlags, so that its functionality can be // used without having to include llvm/CodeGen/CommandFlags.inc, which // would lead to multiple definitions of the command line flags. -llvm::TargetOptions lld::InitTargetOptionsFromCodeGenFlags() { +llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() { return ::InitTargetOptionsFromCodeGenFlags(); } -llvm::Optional lld::GetCodeModelFromCMModel() { +llvm::Optional lld::getCodeModelFromCMModel() { return getCodeModel(); } -std::string lld::GetCPUStr() { return ::getCPUStr(); } -std::vector lld::GetMAttrs() { return ::MAttrs; } +std::string lld::getCPUStr() { return ::getCPUStr(); } + +std::vector lld::getMAttrs() { return ::MAttrs; } diff --git a/Common/Threads.cpp b/Common/Threads.cpp index c64b8c3..af04972 100644 --- a/Common/Threads.cpp +++ b/Common/Threads.cpp @@ -1,12 +1,11 @@ //===- Threads.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 "lld/Common/Threads.h" -bool lld::ThreadsEnabled = true; +bool lld::threadsEnabled = true; diff --git a/Common/Timer.cpp b/Common/Timer.cpp index 89f9829..4b7d110 100644 --- a/Common/Timer.cpp +++ b/Common/Timer.cpp @@ -1,9 +1,8 @@ //===- Timer.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 // //===----------------------------------------------------------------------===// @@ -14,43 +13,43 @@ using namespace lld; using namespace llvm; -ScopedTimer::ScopedTimer(Timer &T) : T(&T) { T.start(); } +ScopedTimer::ScopedTimer(Timer &t) : t(&t) { t.start(); } void ScopedTimer::stop() { - if (!T) + if (!t) return; - T->stop(); - T = nullptr; + t->stop(); + t = nullptr; } ScopedTimer::~ScopedTimer() { stop(); } -Timer::Timer(llvm::StringRef Name) : Name(Name), Parent(nullptr) {} -Timer::Timer(llvm::StringRef Name, Timer &Parent) - : Name(Name), Parent(&Parent) {} +Timer::Timer(llvm::StringRef name) : name(name), parent(nullptr) {} +Timer::Timer(llvm::StringRef name, Timer &parent) + : name(name), parent(&parent) {} void Timer::start() { - if (Parent && Total.count() == 0) - Parent->Children.push_back(this); - StartTime = std::chrono::high_resolution_clock::now(); + if (parent && total.count() == 0) + parent->children.push_back(this); + startTime = std::chrono::high_resolution_clock::now(); } void Timer::stop() { - Total += (std::chrono::high_resolution_clock::now() - StartTime); + total += (std::chrono::high_resolution_clock::now() - startTime); } Timer &Timer::root() { - static Timer RootTimer("Total Link Time"); - return RootTimer; + static Timer rootTimer("Total Link Time"); + return rootTimer; } void Timer::print() { - double TotalDuration = static_cast(root().millis()); + double totalDuration = static_cast(root().millis()); // We want to print the grand total under all the intermediate phases, so we // print all children first, then print the total under that. - for (const auto &Child : Children) - Child->print(1, TotalDuration); + for (const auto &child : children) + child->print(1, totalDuration); message(std::string(49, '-')); @@ -59,22 +58,22 @@ void Timer::print() { double Timer::millis() const { return std::chrono::duration_cast>( - Total) + total) .count(); } -void Timer::print(int Depth, double TotalDuration, bool Recurse) const { - double P = 100.0 * millis() / TotalDuration; +void Timer::print(int depth, double totalDuration, bool recurse) const { + double p = 100.0 * millis() / totalDuration; - SmallString<32> Str; - llvm::raw_svector_ostream Stream(Str); - std::string S = std::string(Depth * 2, ' ') + Name + std::string(":"); - Stream << format("%-30s%5d ms (%5.1f%%)", S.c_str(), (int)millis(), P); + SmallString<32> str; + llvm::raw_svector_ostream stream(str); + std::string s = std::string(depth * 2, ' ') + name + std::string(":"); + stream << format("%-30s%5d ms (%5.1f%%)", s.c_str(), (int)millis(), p); - message(Str); + message(str); - if (Recurse) { - for (const auto &Child : Children) - Child->print(Depth + 1, TotalDuration); + if (recurse) { + for (const auto &child : children) + child->print(depth + 1, totalDuration); } } diff --git a/Common/Version.cpp b/Common/Version.cpp index 6226c9a..ae10f2f 100644 --- a/Common/Version.cpp +++ b/Common/Version.cpp @@ -1,9 +1,8 @@ //===- lib/Common/Version.cpp - LLD Version Number ---------------*- C++-=====// // -// The LLVM Compiler Infrastructure -// -// 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 // //===----------------------------------------------------------------------===// // @@ -13,31 +12,16 @@ #include "lld/Common/Version.h" -using namespace llvm; - -// Returns an SVN repository path, which is usually "trunk". -static std::string getRepositoryPath() { - StringRef S = LLD_REPOSITORY_STRING; - size_t Pos = S.find("lld/"); - if (Pos != StringRef::npos) - return S.substr(Pos + 4); - return S; -} - -// Returns an SVN repository name, e.g., " (trunk 284614)" -// or an empty string if no repository info is available. -static std::string getRepository() { - std::string Repo = getRepositoryPath(); - std::string Rev = LLD_REVISION_STRING; - - if (Repo.empty() && Rev.empty()) - return ""; - if (!Repo.empty() && !Rev.empty()) - return " (" + Repo + " " + Rev + ")"; - return " (" + Repo + Rev + ")"; -} +#ifdef HAVE_VCS_VERSION_INC +#include "VCSVersion.inc" +#endif -// Returns a version string, e.g., "LLD 4.0 (lld/trunk 284614)". +// Returns a version string, e.g.: +// lld 9.0.0 (https://github.com/llvm/llvm-project.git 9efdd7ac5e914d3c9fa1ef) std::string lld::getLLDVersion() { - return "LLD " + std::string(LLD_VERSION_STRING) + getRepository(); +#if defined(LLD_REPOSITORY) && defined(LLD_REVISION) + return "LLD " LLD_VERSION_STRING " (" LLD_REPOSITORY " " LLD_REVISION ")"; +#else + return "LLD " LLD_VERSION_STRING; +#endif } diff --git a/ELF/AArch64ErrataFix.cpp b/ELF/AArch64ErrataFix.cpp index ac753cb..b2eda4d 100644 --- a/ELF/AArch64ErrataFix.cpp +++ b/ELF/AArch64ErrataFix.cpp @@ -1,9 +1,8 @@ //===- AArch64ErrataFix.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 // //===----------------------------------------------------------------------===// // This file implements Section Patching for the purpose of working around @@ -57,8 +56,8 @@ using namespace lld::elf; // ADRP // | 1 | immlo (2) | 1 | 0 0 0 0 | immhi (19) | Rd (5) | -static bool isADRP(uint32_t Instr) { - return (Instr & 0x9f000000) == 0x90000000; +static bool isADRP(uint32_t instr) { + return (instr & 0x9f000000) == 0x90000000; } // Load and store bit patterns from ARMv8-A ARM ARM. @@ -67,8 +66,8 @@ static bool isADRP(uint32_t Instr) { // All loads and stores have 1 (at bit postion 27), (0 at bit position 25). // | op0 x op1 (2) | 1 op2 0 op3 (2) | x | op4 (5) | xxxx | op5 (2) | x (10) | -static bool isLoadStoreClass(uint32_t Instr) { - return (Instr & 0x0a000000) == 0x08000000; +static bool isLoadStoreClass(uint32_t instr) { + return (instr & 0x0a000000) == 0x08000000; } // LDN/STN multiple no offset @@ -83,20 +82,20 @@ static bool isLoadStoreClass(uint32_t Instr) { // opcode == 0110 ST1 3 registers. // opcode == 0111 ST1 1 register. // opcode == 1010 ST1 2 registers. -static bool isST1MultipleOpcode(uint32_t Instr) { - return (Instr & 0x0000f000) == 0x00002000 || - (Instr & 0x0000f000) == 0x00006000 || - (Instr & 0x0000f000) == 0x00007000 || - (Instr & 0x0000f000) == 0x0000a000; +static bool isST1MultipleOpcode(uint32_t instr) { + return (instr & 0x0000f000) == 0x00002000 || + (instr & 0x0000f000) == 0x00006000 || + (instr & 0x0000f000) == 0x00007000 || + (instr & 0x0000f000) == 0x0000a000; } -static bool isST1Multiple(uint32_t Instr) { - return (Instr & 0xbfff0000) == 0x0c000000 && isST1MultipleOpcode(Instr); +static bool isST1Multiple(uint32_t instr) { + return (instr & 0xbfff0000) == 0x0c000000 && isST1MultipleOpcode(instr); } // Writes to Rn (writeback). -static bool isST1MultiplePost(uint32_t Instr) { - return (Instr & 0xbfe00000) == 0x0c800000 && isST1MultipleOpcode(Instr); +static bool isST1MultiplePost(uint32_t instr) { + return (instr & 0xbfe00000) == 0x0c800000 && isST1MultipleOpcode(instr); } // LDN/STN single no offset @@ -111,41 +110,41 @@ static bool isST1MultiplePost(uint32_t Instr) { // opcode == 000 ST1 8-bit. // opcode == 010 ST1 16-bit. // opcode == 100 ST1 32 or 64-bit (Size determines which). -static bool isST1SingleOpcode(uint32_t Instr) { - return (Instr & 0x0040e000) == 0x00000000 || - (Instr & 0x0040e000) == 0x00004000 || - (Instr & 0x0040e000) == 0x00008000; +static bool isST1SingleOpcode(uint32_t instr) { + return (instr & 0x0040e000) == 0x00000000 || + (instr & 0x0040e000) == 0x00004000 || + (instr & 0x0040e000) == 0x00008000; } -static bool isST1Single(uint32_t Instr) { - return (Instr & 0xbfff0000) == 0x0d000000 && isST1SingleOpcode(Instr); +static bool isST1Single(uint32_t instr) { + return (instr & 0xbfff0000) == 0x0d000000 && isST1SingleOpcode(instr); } // Writes to Rn (writeback). -static bool isST1SinglePost(uint32_t Instr) { - return (Instr & 0xbfe00000) == 0x0d800000 && isST1SingleOpcode(Instr); +static bool isST1SinglePost(uint32_t instr) { + return (instr & 0xbfe00000) == 0x0d800000 && isST1SingleOpcode(instr); } -static bool isST1(uint32_t Instr) { - return isST1Multiple(Instr) || isST1MultiplePost(Instr) || - isST1Single(Instr) || isST1SinglePost(Instr); +static bool isST1(uint32_t instr) { + return isST1Multiple(instr) || isST1MultiplePost(instr) || + isST1Single(instr) || isST1SinglePost(instr); } // Load/store exclusive // | size (2) 00 | 1000 | o2 L o1 | Rs (5) | o0 | Rt2 (5) | Rn (5) | Rt (5) | // L == 0 for Stores. -static bool isLoadStoreExclusive(uint32_t Instr) { - return (Instr & 0x3f000000) == 0x08000000; +static bool isLoadStoreExclusive(uint32_t instr) { + return (instr & 0x3f000000) == 0x08000000; } -static bool isLoadExclusive(uint32_t Instr) { - return (Instr & 0x3f400000) == 0x08400000; +static bool isLoadExclusive(uint32_t instr) { + return (instr & 0x3f400000) == 0x08400000; } // Load register literal // | opc (2) 01 | 1 V 00 | imm19 | Rt (5) | -static bool isLoadLiteral(uint32_t Instr) { - return (Instr & 0x3b000000) == 0x18000000; +static bool isLoadLiteral(uint32_t instr) { + return (instr & 0x3b000000) == 0x18000000; } // Load/store no-allocate pair @@ -153,8 +152,8 @@ static bool isLoadLiteral(uint32_t Instr) { // | opc (2) 10 | 1 V 00 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | // L == 0 for stores. // Never writes to register -static bool isSTNP(uint32_t Instr) { - return (Instr & 0x3bc00000) == 0x28000000; +static bool isSTNP(uint32_t instr) { + return (instr & 0x3bc00000) == 0x28000000; } // Load/store register pair @@ -162,69 +161,69 @@ static bool isSTNP(uint32_t Instr) { // | opc (2) 10 | 1 V 00 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | // L == 0 for stores, V == 0 for Scalar, V == 1 for Simd/FP // Writes to Rn. -static bool isSTPPost(uint32_t Instr) { - return (Instr & 0x3bc00000) == 0x28800000; +static bool isSTPPost(uint32_t instr) { + return (instr & 0x3bc00000) == 0x28800000; } // (offset) // | opc (2) 10 | 1 V 01 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | -static bool isSTPOffset(uint32_t Instr) { - return (Instr & 0x3bc00000) == 0x29000000; +static bool isSTPOffset(uint32_t instr) { + return (instr & 0x3bc00000) == 0x29000000; } // (pre-index) // | opc (2) 10 | 1 V 01 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) | // Writes to Rn. -static bool isSTPPre(uint32_t Instr) { - return (Instr & 0x3bc00000) == 0x29800000; +static bool isSTPPre(uint32_t instr) { + return (instr & 0x3bc00000) == 0x29800000; } -static bool isSTP(uint32_t Instr) { - return isSTPPost(Instr) || isSTPOffset(Instr) || isSTPPre(Instr); +static bool isSTP(uint32_t instr) { + return isSTPPost(instr) || isSTPOffset(instr) || isSTPPre(instr); } // Load/store register (unscaled immediate) // | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 00 | Rn (5) | Rt (5) | // V == 0 for Scalar, V == 1 for Simd/FP. -static bool isLoadStoreUnscaled(uint32_t Instr) { - return (Instr & 0x3b000c00) == 0x38000000; +static bool isLoadStoreUnscaled(uint32_t instr) { + return (instr & 0x3b000c00) == 0x38000000; } // Load/store register (immediate post-indexed) // | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 01 | Rn (5) | Rt (5) | -static bool isLoadStoreImmediatePost(uint32_t Instr) { - return (Instr & 0x3b200c00) == 0x38000400; +static bool isLoadStoreImmediatePost(uint32_t instr) { + return (instr & 0x3b200c00) == 0x38000400; } // Load/store register (unprivileged) // | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 10 | Rn (5) | Rt (5) | -static bool isLoadStoreUnpriv(uint32_t Instr) { - return (Instr & 0x3b200c00) == 0x38000800; +static bool isLoadStoreUnpriv(uint32_t instr) { + return (instr & 0x3b200c00) == 0x38000800; } // Load/store register (immediate pre-indexed) // | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 11 | Rn (5) | Rt (5) | -static bool isLoadStoreImmediatePre(uint32_t Instr) { - return (Instr & 0x3b200c00) == 0x38000c00; +static bool isLoadStoreImmediatePre(uint32_t instr) { + return (instr & 0x3b200c00) == 0x38000c00; } // Load/store register (register offset) // | size (2) 11 | 1 V 00 | opc (2) 1 | Rm (5) | option (3) S | 10 | Rn | Rt | -static bool isLoadStoreRegisterOff(uint32_t Instr) { - return (Instr & 0x3b200c00) == 0x38200800; +static bool isLoadStoreRegisterOff(uint32_t instr) { + return (instr & 0x3b200c00) == 0x38200800; } // Load/store register (unsigned immediate) // | size (2) 11 | 1 V 01 | opc (2) | imm12 | Rn (5) | Rt (5) | -static bool isLoadStoreRegisterUnsigned(uint32_t Instr) { - return (Instr & 0x3b000000) == 0x39000000; +static bool isLoadStoreRegisterUnsigned(uint32_t instr) { + return (instr & 0x3b000000) == 0x39000000; } // Rt is always in bit position 0 - 4. -static uint32_t getRt(uint32_t Instr) { return (Instr & 0x1f); } +static uint32_t getRt(uint32_t instr) { return (instr & 0x1f); } // Rn is always in bit position 5 - 9. -static uint32_t getRn(uint32_t Instr) { return (Instr >> 5) & 0x1f; } +static uint32_t getRn(uint32_t instr) { return (instr >> 5) & 0x1f; } // C4.1.2 Branches, Exception Generating and System instructions // | op0 (3) 1 | 01 op1 (4) | x (22) | @@ -233,41 +232,41 @@ static uint32_t getRn(uint32_t Instr) { return (Instr >> 5) & 0x1f; } // op0 == x00 101 op1 == xxxx Unconditional Branch immediate. // op0 == x01 101 op1 == 0xxx Compare and branch immediate. // op0 == x01 101 op1 == 1xxx Test and branch immediate. -static bool isBranch(uint32_t Instr) { - return ((Instr & 0xfe000000) == 0xd6000000) || // Cond branch. - ((Instr & 0xfe000000) == 0x54000000) || // Uncond branch reg. - ((Instr & 0x7c000000) == 0x14000000) || // Uncond branch imm. - ((Instr & 0x7c000000) == 0x34000000); // Compare and test branch. +static bool isBranch(uint32_t instr) { + return ((instr & 0xfe000000) == 0xd6000000) || // Cond branch. + ((instr & 0xfe000000) == 0x54000000) || // Uncond branch reg. + ((instr & 0x7c000000) == 0x14000000) || // Uncond branch imm. + ((instr & 0x7c000000) == 0x34000000); // Compare and test branch. } -static bool isV8SingleRegisterNonStructureLoadStore(uint32_t Instr) { - return isLoadStoreUnscaled(Instr) || isLoadStoreImmediatePost(Instr) || - isLoadStoreUnpriv(Instr) || isLoadStoreImmediatePre(Instr) || - isLoadStoreRegisterOff(Instr) || isLoadStoreRegisterUnsigned(Instr); +static bool isV8SingleRegisterNonStructureLoadStore(uint32_t instr) { + return isLoadStoreUnscaled(instr) || isLoadStoreImmediatePost(instr) || + isLoadStoreUnpriv(instr) || isLoadStoreImmediatePre(instr) || + isLoadStoreRegisterOff(instr) || isLoadStoreRegisterUnsigned(instr); } // Note that this function refers to v8.0 only and does not include the // additional load and store instructions added for in later revisions of // the architecture such as the Atomic memory operations introduced // in v8.1. -static bool isV8NonStructureLoad(uint32_t Instr) { - if (isLoadExclusive(Instr)) +static bool isV8NonStructureLoad(uint32_t instr) { + if (isLoadExclusive(instr)) return true; - if (isLoadLiteral(Instr)) + if (isLoadLiteral(instr)) return true; - else if (isV8SingleRegisterNonStructureLoadStore(Instr)) { + else if (isV8SingleRegisterNonStructureLoadStore(instr)) { // For Load and Store single register, Loads are derived from a // combination of the Size, V and Opc fields. - uint32_t Size = (Instr >> 30) & 0xff; - uint32_t V = (Instr >> 26) & 0x1; - uint32_t Opc = (Instr >> 22) & 0x3; + uint32_t size = (instr >> 30) & 0xff; + uint32_t v = (instr >> 26) & 0x1; + uint32_t opc = (instr >> 22) & 0x3; // For the load and store instructions that we are decoding. // Opc == 0 are all stores. // Opc == 1 with a couple of exceptions are loads. The exceptions are: // Size == 00 (0), V == 1, Opc == 10 (2) which is a store and // Size == 11 (3), V == 0, Opc == 10 (2) which is a prefetch. - return Opc != 0 && !(Size == 0 && V == 1 && Opc == 2) && - !(Size == 3 && V == 0 && Opc == 2); + return opc != 0 && !(size == 0 && v == 1 && opc == 2) && + !(size == 3 && v == 0 && opc == 2); } return false; } @@ -276,18 +275,18 @@ static bool isV8NonStructureLoad(uint32_t Instr) { // needed for errata 843419. // Instruction with writeback updates the index register after the load/store. -static bool hasWriteback(uint32_t Instr) { - return isLoadStoreImmediatePre(Instr) || isLoadStoreImmediatePost(Instr) || - isSTPPre(Instr) || isSTPPost(Instr) || isST1SinglePost(Instr) || - isST1MultiplePost(Instr); +static bool hasWriteback(uint32_t instr) { + return isLoadStoreImmediatePre(instr) || isLoadStoreImmediatePost(instr) || + isSTPPre(instr) || isSTPPost(instr) || isST1SinglePost(instr) || + isST1MultiplePost(instr); } // For the load and store class of instructions, a load can write to the // destination register, a load and a store can write to the base register when // the instruction has writeback. -static bool doesLoadStoreWriteToReg(uint32_t Instr, uint32_t Reg) { - return (isV8NonStructureLoad(Instr) && getRt(Instr) == Reg) || - (hasWriteback(Instr) && getRn(Instr) == Reg); +static bool doesLoadStoreWriteToReg(uint32_t instr, uint32_t reg) { + return (isV8NonStructureLoad(instr) && getRt(instr) == reg) || + (hasWriteback(instr) && getRn(instr) == reg); } // Scanner for Cortex-A53 errata 843419 @@ -319,18 +318,18 @@ static bool doesLoadStoreWriteToReg(uint32_t Instr, uint32_t Reg) { // Return true if the Instruction sequence Adrp, Instr2, and Instr4 match // the erratum sequence. The Adrp, Instr2 and Instr4 correspond to 1.), 2.), // and 4.) in the Scanner for Cortex-A53 errata comment above. -static bool is843419ErratumSequence(uint32_t Instr1, uint32_t Instr2, - uint32_t Instr4) { - if (!isADRP(Instr1)) +static bool is843419ErratumSequence(uint32_t instr1, uint32_t instr2, + uint32_t instr4) { + if (!isADRP(instr1)) return false; - uint32_t Rn = getRt(Instr1); - return isLoadStoreClass(Instr2) && - (isLoadStoreExclusive(Instr2) || isLoadLiteral(Instr2) || - isV8SingleRegisterNonStructureLoadStore(Instr2) || isSTP(Instr2) || - isSTNP(Instr2) || isST1(Instr2)) && - !doesLoadStoreWriteToReg(Instr2, Rn) && - isLoadStoreRegisterUnsigned(Instr4) && getRn(Instr4) == Rn; + uint32_t rn = getRt(instr1); + return isLoadStoreClass(instr2) && + (isLoadStoreExclusive(instr2) || isLoadLiteral(instr2) || + isV8SingleRegisterNonStructureLoadStore(instr2) || isSTP(instr2) || + isSTNP(instr2) || isST1(instr2)) && + !doesLoadStoreWriteToReg(instr2, rn) && + isLoadStoreRegisterUnsigned(instr4) && getRn(instr4) == rn; } // Scan the instruction sequence starting at Offset Off from the base of @@ -339,143 +338,143 @@ static bool is843419ErratumSequence(uint32_t Instr1, uint32_t Instr2, // instructions we've scanned. // Return the offset of the load or store instruction in IS that we want to // patch or 0 if no patch required. -static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off, - uint64_t Limit) { - uint64_t ISAddr = IS->getVA(0); +static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off, + uint64_t limit) { + uint64_t isecAddr = isec->getVA(0); // Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8. - uint64_t InitialPageOff = (ISAddr + Off) & 0xfff; - if (InitialPageOff < 0xff8) - Off += 0xff8 - InitialPageOff; + uint64_t initialPageOff = (isecAddr + off) & 0xfff; + if (initialPageOff < 0xff8) + off += 0xff8 - initialPageOff; - bool OptionalAllowed = Limit - Off > 12; - if (Off >= Limit || Limit - Off < 12) { + bool optionalAllowed = limit - off > 12; + if (off >= limit || limit - off < 12) { // Need at least 3 4-byte sized instructions to trigger erratum. - Off = Limit; + off = limit; return 0; } - uint64_t PatchOff = 0; - const uint8_t *Buf = IS->data().begin(); - const ulittle32_t *InstBuf = reinterpret_cast(Buf + Off); - uint32_t Instr1 = *InstBuf++; - uint32_t Instr2 = *InstBuf++; - uint32_t Instr3 = *InstBuf++; - if (is843419ErratumSequence(Instr1, Instr2, Instr3)) { - PatchOff = Off + 8; - } else if (OptionalAllowed && !isBranch(Instr3)) { - uint32_t Instr4 = *InstBuf++; - if (is843419ErratumSequence(Instr1, Instr2, Instr4)) - PatchOff = Off + 12; + uint64_t patchOff = 0; + const uint8_t *buf = isec->data().begin(); + const ulittle32_t *instBuf = reinterpret_cast(buf + off); + uint32_t instr1 = *instBuf++; + uint32_t instr2 = *instBuf++; + uint32_t instr3 = *instBuf++; + if (is843419ErratumSequence(instr1, instr2, instr3)) { + patchOff = off + 8; + } else if (optionalAllowed && !isBranch(instr3)) { + uint32_t instr4 = *instBuf++; + if (is843419ErratumSequence(instr1, instr2, instr4)) + patchOff = off + 12; } - if (((ISAddr + Off) & 0xfff) == 0xff8) - Off += 4; + if (((isecAddr + off) & 0xfff) == 0xff8) + off += 4; else - Off += 0xffc; - return PatchOff; + off += 0xffc; + return patchOff; } class lld::elf::Patch843419Section : public SyntheticSection { public: - Patch843419Section(InputSection *P, uint64_t Off); + Patch843419Section(InputSection *p, uint64_t off); - void writeTo(uint8_t *Buf) override; + void writeTo(uint8_t *buf) override; size_t getSize() const override { return 8; } uint64_t getLDSTAddr() const; // The Section we are patching. - const InputSection *Patchee; + const InputSection *patchee; // The offset of the instruction in the Patchee section we are patching. - uint64_t PatcheeOffset; + uint64_t patcheeOffset; // A label for the start of the Patch that we can use as a relocation target. - Symbol *PatchSym; + Symbol *patchSym; }; -lld::elf::Patch843419Section::Patch843419Section(InputSection *P, uint64_t Off) +lld::elf::Patch843419Section::Patch843419Section(InputSection *p, uint64_t off) : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4, ".text.patch"), - Patchee(P), PatcheeOffset(Off) { - this->Parent = P->getParent(); - PatchSym = addSyntheticLocal( - Saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0, + patchee(p), patcheeOffset(off) { + this->parent = p->getParent(); + patchSym = addSyntheticLocal( + saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0, getSize(), *this); - addSyntheticLocal(Saver.save("$x"), STT_NOTYPE, 0, 0, *this); + addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this); } uint64_t lld::elf::Patch843419Section::getLDSTAddr() const { - return Patchee->getVA(PatcheeOffset); + return patchee->getVA(patcheeOffset); } -void lld::elf::Patch843419Section::writeTo(uint8_t *Buf) { +void lld::elf::Patch843419Section::writeTo(uint8_t *buf) { // Copy the instruction that we will be replacing with a branch in the // Patchee Section. - write32le(Buf, read32le(Patchee->data().begin() + PatcheeOffset)); + write32le(buf, read32le(patchee->data().begin() + patcheeOffset)); // Apply any relocation transferred from the original PatcheeSection. - // For a SyntheticSection Buf already has OutSecOff added, but relocateAlloc - // also adds OutSecOff so we need to subtract to avoid double counting. - this->relocateAlloc(Buf - OutSecOff, Buf - OutSecOff + getSize()); + // For a SyntheticSection Buf already has outSecOff added, but relocateAlloc + // also adds outSecOff so we need to subtract to avoid double counting. + this->relocateAlloc(buf - outSecOff, buf - outSecOff + getSize()); // Return address is the next instruction after the one we have just copied. - uint64_t S = getLDSTAddr() + 4; - uint64_t P = PatchSym->getVA() + 4; - Target->relocateOne(Buf + 4, R_AARCH64_JUMP26, S - P); + uint64_t s = getLDSTAddr() + 4; + uint64_t p = patchSym->getVA() + 4; + target->relocateOne(buf + 4, R_AARCH64_JUMP26, s - p); } void AArch64Err843419Patcher::init() { // The AArch64 ABI permits data in executable sections. We must avoid scanning // this data as if it were instructions to avoid false matches. We use the // mapping symbols in the InputObjects to identify this data, caching the - // results in SectionMap so we don't have to recalculate it each pass. + // results in sectionMap so we don't have to recalculate it each pass. // The ABI Section 4.5.4 Mapping symbols; defines local symbols that describe // half open intervals [Symbol Value, Next Symbol Value) of code and data // within sections. If there is no next symbol then the half open interval is // [Symbol Value, End of section). The type, code or data, is determined by // the mapping symbol name, $x for code, $d for data. - auto IsCodeMapSymbol = [](const Symbol *B) { - return B->getName() == "$x" || B->getName().startswith("$x."); + auto isCodeMapSymbol = [](const Symbol *b) { + return b->getName() == "$x" || b->getName().startswith("$x."); }; - auto IsDataMapSymbol = [](const Symbol *B) { - return B->getName() == "$d" || B->getName().startswith("$d."); + auto isDataMapSymbol = [](const Symbol *b) { + return b->getName() == "$d" || b->getName().startswith("$d."); }; // Collect mapping symbols for every executable InputSection. - for (InputFile *File : ObjectFiles) { - auto *F = cast>(File); - for (Symbol *B : F->getLocalSymbols()) { - auto *Def = dyn_cast(B); - if (!Def) + for (InputFile *file : objectFiles) { + auto *f = cast>(file); + for (Symbol *b : f->getLocalSymbols()) { + auto *def = dyn_cast(b); + if (!def) continue; - if (!IsCodeMapSymbol(Def) && !IsDataMapSymbol(Def)) + if (!isCodeMapSymbol(def) && !isDataMapSymbol(def)) continue; - if (auto *Sec = dyn_cast_or_null(Def->Section)) - if (Sec->Flags & SHF_EXECINSTR) - SectionMap[Sec].push_back(Def); + if (auto *sec = dyn_cast_or_null(def->section)) + if (sec->flags & SHF_EXECINSTR) + sectionMap[sec].push_back(def); } } // For each InputSection make sure the mapping symbols are in sorted in // ascending order and free from consecutive runs of mapping symbols with // the same type. For example we must remove the redundant $d.1 from $x.0 // $d.0 $d.1 $x.1. - for (auto &KV : SectionMap) { - std::vector &MapSyms = KV.second; - if (MapSyms.size() <= 1) + for (auto &kv : sectionMap) { + std::vector &mapSyms = kv.second; + if (mapSyms.size() <= 1) continue; - std::stable_sort( - MapSyms.begin(), MapSyms.end(), - [](const Defined *A, const Defined *B) { return A->Value < B->Value; }); - MapSyms.erase( - std::unique(MapSyms.begin(), MapSyms.end(), - [=](const Defined *A, const Defined *B) { - return (IsCodeMapSymbol(A) && IsCodeMapSymbol(B)) || - (IsDataMapSymbol(A) && IsDataMapSymbol(B)); + llvm::stable_sort(mapSyms, [](const Defined *a, const Defined *b) { + return a->value < b->value; + }); + mapSyms.erase( + std::unique(mapSyms.begin(), mapSyms.end(), + [=](const Defined *a, const Defined *b) { + return (isCodeMapSymbol(a) && isCodeMapSymbol(b)) || + (isDataMapSymbol(a) && isDataMapSymbol(b)); }), - MapSyms.end()); + mapSyms.end()); } - Initialized = true; + initialized = true; } // Insert the PatchSections we have created back into the @@ -484,60 +483,60 @@ void AArch64Err843419Patcher::init() { // executable sections, although we may need to insert them earlier if the // InputSectionDescription is larger than the maximum branch range. void AArch64Err843419Patcher::insertPatches( - InputSectionDescription &ISD, std::vector &Patches) { - uint64_t ISLimit; - uint64_t PrevISLimit = ISD.Sections.front()->OutSecOff; - uint64_t PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing(); - uint64_t OutSecAddr = ISD.Sections.front()->getParent()->Addr; + InputSectionDescription &isd, std::vector &patches) { + uint64_t isecLimit; + uint64_t prevIsecLimit = isd.sections.front()->outSecOff; + uint64_t patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing(); + uint64_t outSecAddr = isd.sections.front()->getParent()->addr; - // Set the OutSecOff of patches to the place where we want to insert them. + // Set the outSecOff of patches to the place where we want to insert them. // We use a similar strategy to Thunk placement. Place patches roughly // every multiple of maximum branch range. - auto PatchIt = Patches.begin(); - auto PatchEnd = Patches.end(); - for (const InputSection *IS : ISD.Sections) { - ISLimit = IS->OutSecOff + IS->getSize(); - if (ISLimit > PatchUpperBound) { - while (PatchIt != PatchEnd) { - if ((*PatchIt)->getLDSTAddr() - OutSecAddr >= PrevISLimit) + auto patchIt = patches.begin(); + auto patchEnd = patches.end(); + for (const InputSection *isec : isd.sections) { + isecLimit = isec->outSecOff + isec->getSize(); + if (isecLimit > patchUpperBound) { + while (patchIt != patchEnd) { + if ((*patchIt)->getLDSTAddr() - outSecAddr >= prevIsecLimit) break; - (*PatchIt)->OutSecOff = PrevISLimit; - ++PatchIt; + (*patchIt)->outSecOff = prevIsecLimit; + ++patchIt; } - PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing(); + patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing(); } - PrevISLimit = ISLimit; + prevIsecLimit = isecLimit; } - for (; PatchIt != PatchEnd; ++PatchIt) { - (*PatchIt)->OutSecOff = ISLimit; + for (; patchIt != patchEnd; ++patchIt) { + (*patchIt)->outSecOff = isecLimit; } - // merge all patch sections. We use the OutSecOff assigned above to + // merge all patch sections. We use the outSecOff assigned above to // determine the insertion point. This is ok as we only merge into an // InputSectionDescription once per pass, and at the end of the pass - // assignAddresses() will recalculate all the OutSecOff values. - std::vector Tmp; - Tmp.reserve(ISD.Sections.size() + Patches.size()); - auto MergeCmp = [](const InputSection *A, const InputSection *B) { - if (A->OutSecOff < B->OutSecOff) + // assignAddresses() will recalculate all the outSecOff values. + std::vector tmp; + tmp.reserve(isd.sections.size() + patches.size()); + auto mergeCmp = [](const InputSection *a, const InputSection *b) { + if (a->outSecOff < b->outSecOff) return true; - if (A->OutSecOff == B->OutSecOff && isa(A) && - !isa(B)) + if (a->outSecOff == b->outSecOff && isa(a) && + !isa(b)) return true; return false; }; - std::merge(ISD.Sections.begin(), ISD.Sections.end(), Patches.begin(), - Patches.end(), std::back_inserter(Tmp), MergeCmp); - ISD.Sections = std::move(Tmp); + std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(), + patches.end(), std::back_inserter(tmp), mergeCmp); + isd.sections = std::move(tmp); } -// Given an erratum sequence that starts at address AdrpAddr, with an -// instruction that we need to patch at PatcheeOffset from the start of +// Given an erratum sequence that starts at address adrpAddr, with an +// instruction that we need to patch at patcheeOffset from the start of // InputSection IS, create a Patch843419 Section and add it to the // Patches that we need to insert. -static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset, - InputSection *IS, - std::vector &Patches) { +static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset, + InputSection *isec, + std::vector &patches) { // There may be a relocation at the same offset that we are patching. There // are four cases that we need to consider. // Case 1: R_AARCH64_JUMP26 branch relocation. We have already patched this @@ -552,29 +551,29 @@ static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset, // and replace the relocation with a R_AARCH_JUMP26 branch relocation. // Case 4: No relocation. We must create a new R_AARCH64_JUMP26 branch // relocation at the offset. - auto RelIt = std::find_if( - IS->Relocations.begin(), IS->Relocations.end(), - [=](const Relocation &R) { return R.Offset == PatcheeOffset; }); - if (RelIt != IS->Relocations.end() && - (RelIt->Type == R_AARCH64_JUMP26 || RelIt->Expr == R_RELAX_TLS_IE_TO_LE)) + auto relIt = llvm::find_if(isec->relocations, [=](const Relocation &r) { + return r.offset == patcheeOffset; + }); + if (relIt != isec->relocations.end() && + (relIt->type == R_AARCH64_JUMP26 || relIt->expr == R_RELAX_TLS_IE_TO_LE)) return; log("detected cortex-a53-843419 erratum sequence starting at " + - utohexstr(AdrpAddr) + " in unpatched output."); + utohexstr(adrpAddr) + " in unpatched output."); - auto *PS = make(IS, PatcheeOffset); - Patches.push_back(PS); + auto *ps = make(isec, patcheeOffset); + patches.push_back(ps); - auto MakeRelToPatch = [](uint64_t Offset, Symbol *PatchSym) { - return Relocation{R_PC, R_AARCH64_JUMP26, Offset, 0, PatchSym}; + auto makeRelToPatch = [](uint64_t offset, Symbol *patchSym) { + return Relocation{R_PC, R_AARCH64_JUMP26, offset, 0, patchSym}; }; - if (RelIt != IS->Relocations.end()) { - PS->Relocations.push_back( - {RelIt->Expr, RelIt->Type, 0, RelIt->Addend, RelIt->Sym}); - *RelIt = MakeRelToPatch(PatcheeOffset, PS->PatchSym); + if (relIt != isec->relocations.end()) { + ps->relocations.push_back( + {relIt->expr, relIt->type, 0, relIt->addend, relIt->sym}); + *relIt = makeRelToPatch(patcheeOffset, ps->patchSym); } else - IS->Relocations.push_back(MakeRelToPatch(PatcheeOffset, PS->PatchSym)); + isec->relocations.push_back(makeRelToPatch(patcheeOffset, ps->patchSym)); } // Scan all the instructions in InputSectionDescription, for each instance of @@ -582,40 +581,40 @@ static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset, // Patch843419Sections that need to be applied to ISD. std::vector AArch64Err843419Patcher::patchInputSectionDescription( - InputSectionDescription &ISD) { - std::vector Patches; - for (InputSection *IS : ISD.Sections) { + InputSectionDescription &isd) { + std::vector patches; + for (InputSection *isec : isd.sections) { // LLD doesn't use the erratum sequence in SyntheticSections. - if (isa(IS)) + if (isa(isec)) continue; - // Use SectionMap to make sure we only scan code and not inline data. + // Use sectionMap to make sure we only scan code and not inline data. // We have already sorted MapSyms in ascending order and removed consecutive // mapping symbols of the same type. Our range of executable instructions to - // scan is therefore [CodeSym->Value, DataSym->Value) or [CodeSym->Value, + // scan is therefore [codeSym->value, dataSym->value) or [codeSym->value, // section size). - std::vector &MapSyms = SectionMap[IS]; + std::vector &mapSyms = sectionMap[isec]; - auto CodeSym = llvm::find_if(MapSyms, [&](const Defined *MS) { - return MS->getName().startswith("$x"); + auto codeSym = llvm::find_if(mapSyms, [&](const Defined *ms) { + return ms->getName().startswith("$x"); }); - while (CodeSym != MapSyms.end()) { - auto DataSym = std::next(CodeSym); - uint64_t Off = (*CodeSym)->Value; - uint64_t Limit = - (DataSym == MapSyms.end()) ? IS->data().size() : (*DataSym)->Value; + while (codeSym != mapSyms.end()) { + auto dataSym = std::next(codeSym); + uint64_t off = (*codeSym)->value; + uint64_t limit = + (dataSym == mapSyms.end()) ? isec->data().size() : (*dataSym)->value; - while (Off < Limit) { - uint64_t StartAddr = IS->getVA(Off); - if (uint64_t PatcheeOffset = scanCortexA53Errata843419(IS, Off, Limit)) - implementPatch(StartAddr, PatcheeOffset, IS, Patches); + while (off < limit) { + uint64_t startAddr = isec->getVA(off); + if (uint64_t patcheeOffset = scanCortexA53Errata843419(isec, off, limit)) + implementPatch(startAddr, patcheeOffset, isec, patches); } - if (DataSym == MapSyms.end()) + if (dataSym == mapSyms.end()) break; - CodeSym = std::next(DataSym); + codeSym = std::next(dataSym); } } - return Patches; + return patches; } // For each InputSectionDescription make one pass over the executable sections @@ -631,22 +630,22 @@ AArch64Err843419Patcher::patchInputSectionDescription( // Ouptut and Input Sections may have been changed. // Returns false if no patches were required and no changes were made. bool AArch64Err843419Patcher::createFixes() { - if (Initialized == false) + if (initialized == false) init(); - bool AddressesChanged = false; - for (OutputSection *OS : OutputSections) { - if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR)) + bool addressesChanged = false; + for (OutputSection *os : outputSections) { + if (!(os->flags & SHF_ALLOC) || !(os->flags & SHF_EXECINSTR)) continue; - for (BaseCommand *BC : OS->SectionCommands) - if (auto *ISD = dyn_cast(BC)) { - std::vector Patches = - patchInputSectionDescription(*ISD); - if (!Patches.empty()) { - insertPatches(*ISD, Patches); - AddressesChanged = true; + for (BaseCommand *bc : os->sectionCommands) + if (auto *isd = dyn_cast(bc)) { + std::vector patches = + patchInputSectionDescription(*isd); + if (!patches.empty()) { + insertPatches(*isd, patches); + addressesChanged = true; } } } - return AddressesChanged; + return addressesChanged; } diff --git a/ELF/AArch64ErrataFix.h b/ELF/AArch64ErrataFix.h index edd154d..0548b58 100644 --- a/ELF/AArch64ErrataFix.h +++ b/ELF/AArch64ErrataFix.h @@ -1,9 +1,8 @@ //===- AArch64ErrataFix.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 // //===----------------------------------------------------------------------===// @@ -30,19 +29,19 @@ public: private: std::vector - patchInputSectionDescription(InputSectionDescription &ISD); + patchInputSectionDescription(InputSectionDescription &isd); - void insertPatches(InputSectionDescription &ISD, - std::vector &Patches); + void insertPatches(InputSectionDescription &isd, + std::vector &patches); void init(); - // A cache of the mapping symbols defined by the InputSecion sorted in order + // A cache of the mapping symbols defined by the InputSection sorted in order // of ascending value with redundant symbols removed. These describe // the ranges of code and data in an executable InputSection. - std::map> SectionMap; + std::map> sectionMap; - bool Initialized = false; + bool initialized = false; }; } // namespace elf diff --git a/ELF/Arch/AArch64.cpp b/ELF/Arch/AArch64.cpp index 08ffe2a..4d47897 100644 --- a/ELF/Arch/AArch64.cpp +++ b/ELF/Arch/AArch64.cpp @@ -1,9 +1,8 @@ //===- AArch64.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,60 +23,59 @@ using namespace lld::elf; // Page(Expr) is the page address of the expression Expr, defined // as (Expr & ~0xFFF). (This applies even if the machine page size // supported by the platform has a different value.) -uint64_t elf::getAArch64Page(uint64_t Expr) { - return Expr & ~static_cast(0xFFF); +uint64_t elf::getAArch64Page(uint64_t expr) { + return expr & ~static_cast(0xFFF); } namespace { -class AArch64 final : public TargetInfo { +class AArch64 : public TargetInfo { public: AArch64(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - RelType getDynRel(RelType Type) const override; - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; uint32_t getThunkSectionSpacing() const override; - bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; - bool usesOnlyLowPageBits(RelType Type) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const override; - void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + bool usesOnlyLowPageBits(RelType type) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace AArch64::AArch64() { - CopyRel = R_AARCH64_COPY; - RelativeRel = R_AARCH64_RELATIVE; - IRelativeRel = R_AARCH64_IRELATIVE; - GotRel = R_AARCH64_GLOB_DAT; - NoneRel = R_AARCH64_NONE; - PltRel = R_AARCH64_JUMP_SLOT; - TlsDescRel = R_AARCH64_TLSDESC; - TlsGotRel = R_AARCH64_TLS_TPREL64; - GotEntrySize = 8; - GotPltEntrySize = 8; - PltEntrySize = 16; - PltHeaderSize = 32; - DefaultMaxPageSize = 65536; + copyRel = R_AARCH64_COPY; + relativeRel = R_AARCH64_RELATIVE; + iRelativeRel = R_AARCH64_IRELATIVE; + gotRel = R_AARCH64_GLOB_DAT; + noneRel = R_AARCH64_NONE; + pltRel = R_AARCH64_JUMP_SLOT; + symbolicRel = R_AARCH64_ABS64; + tlsDescRel = R_AARCH64_TLSDESC; + tlsGotRel = R_AARCH64_TLS_TPREL64; + pltEntrySize = 16; + pltHeaderSize = 32; + defaultMaxPageSize = 65536; // Align to the 2 MiB page size (known as a superpage or huge page). // FreeBSD automatically promotes 2 MiB-aligned allocations. - DefaultImageBase = 0x200000; + defaultImageBase = 0x200000; - NeedsThunks = true; + needsThunks = true; } -RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_AARCH64_TLSDESC_ADR_PAGE21: return R_AARCH64_TLSDESC_PAGE; case R_AARCH64_TLSDESC_LD64_LO12: @@ -105,6 +103,7 @@ RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S, case R_AARCH64_LD_PREL_LO19: return R_PC; case R_AARCH64_ADR_PREL_PG_HI21: + case R_AARCH64_ADR_PREL_PG_HI21_NC: return R_AARCH64_PAGE_PC; case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: @@ -119,18 +118,18 @@ RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S, } } -RelExpr AArch64::adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const { - if (Expr == R_RELAX_TLS_GD_TO_IE) { - if (Type == R_AARCH64_TLSDESC_ADR_PAGE21) +RelExpr AArch64::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + if (expr == R_RELAX_TLS_GD_TO_IE) { + if (type == R_AARCH64_TLSDESC_ADR_PAGE21) return R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC; return R_RELAX_TLS_GD_TO_IE_ABS; } - return Expr; + return expr; } -bool AArch64::usesOnlyLowPageBits(RelType Type) const { - switch (Type) { +bool AArch64::usesOnlyLowPageBits(RelType type) const { + switch (type) { default: return false; case R_AARCH64_ADD_ABS_LO12_NC: @@ -147,18 +146,18 @@ bool AArch64::usesOnlyLowPageBits(RelType Type) const { } } -RelType AArch64::getDynRel(RelType Type) const { - if (Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64) - return Type; +RelType AArch64::getDynRel(RelType type) const { + if (type == R_AARCH64_ABS64) + return type; return R_AARCH64_NONE; } -void AArch64::writeGotPlt(uint8_t *Buf, const Symbol &) const { - write64le(Buf, In.Plt->getVA()); +void AArch64::writeGotPlt(uint8_t *buf, const Symbol &) const { + write64le(buf, in.plt->getVA()); } -void AArch64::writePltHeader(uint8_t *Buf) const { - const uint8_t PltData[] = { +void AArch64::writePltHeader(uint8_t *buf) const { + const uint8_t pltData[] = { 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]! 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2])) 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))] @@ -168,42 +167,42 @@ void AArch64::writePltHeader(uint8_t *Buf) const { 0x1f, 0x20, 0x03, 0xd5, // nop 0x1f, 0x20, 0x03, 0xd5 // nop }; - memcpy(Buf, PltData, sizeof(PltData)); - - uint64_t Got = In.GotPlt->getVA(); - uint64_t Plt = In.Plt->getVA(); - relocateOne(Buf + 4, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(Got + 16) - getAArch64Page(Plt + 4)); - relocateOne(Buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, Got + 16); - relocateOne(Buf + 12, R_AARCH64_ADD_ABS_LO12_NC, Got + 16); + memcpy(buf, pltData, sizeof(pltData)); + + uint64_t got = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(got + 16) - getAArch64Page(plt + 4)); + relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16); + relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16); } -void AArch64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Inst[] = { +void AArch64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t inst[] = { 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n])) 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))] 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[n])) 0x20, 0x02, 0x1f, 0xd6 // br x17 }; - memcpy(Buf, Inst, sizeof(Inst)); + memcpy(buf, inst, sizeof(inst)); - relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(GotPltEntryAddr) - getAArch64Page(PltEntryAddr)); - relocateOne(Buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, GotPltEntryAddr); - relocateOne(Buf + 8, R_AARCH64_ADD_ABS_LO12_NC, GotPltEntryAddr); + relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr)); + relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr); + relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr); } -bool AArch64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const { +bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { // ELF for the ARM 64-bit architecture, section Call and Jump relocations // only permits range extension thunks for R_AARCH64_CALL26 and // R_AARCH64_JUMP26 relocation types. - if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26) + if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26) return false; - uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA(); - return !inBranchRange(Type, BranchAddr, Dst); + uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); + return !inBranchRange(type, branchAddr, dst); } uint32_t AArch64::getThunkSectionSpacing() const { @@ -213,71 +212,72 @@ uint32_t AArch64::getThunkSectionSpacing() const { return (128 * 1024 * 1024) - 0x30000; } -bool AArch64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { - if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26) +bool AArch64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26) return true; // The AArch64 call and unconditional branch instructions have a range of // +/- 128 MiB. - uint64_t Range = 128 * 1024 * 1024; - if (Dst > Src) { + uint64_t range = 128 * 1024 * 1024; + if (dst > src) { // Immediate of branch is signed. - Range -= 4; - return Dst - Src <= Range; + range -= 4; + return dst - src <= range; } - return Src - Dst <= Range; + return src - dst <= range; } -static void write32AArch64Addr(uint8_t *L, uint64_t Imm) { - uint32_t ImmLo = (Imm & 0x3) << 29; - uint32_t ImmHi = (Imm & 0x1FFFFC) << 3; - uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3); - write32le(L, (read32le(L) & ~Mask) | ImmLo | ImmHi); +static void write32AArch64Addr(uint8_t *l, uint64_t imm) { + uint32_t immLo = (imm & 0x3) << 29; + uint32_t immHi = (imm & 0x1FFFFC) << 3; + uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3); + write32le(l, (read32le(l) & ~mask) | immLo | immHi); } // Return the bits [Start, End] from Val shifted Start bits. // For instance, getBits(0xF0, 4, 8) returns 0xF. -static uint64_t getBits(uint64_t Val, int Start, int End) { - uint64_t Mask = ((uint64_t)1 << (End + 1 - Start)) - 1; - return (Val >> Start) & Mask; +static uint64_t getBits(uint64_t val, int start, int end) { + uint64_t mask = ((uint64_t)1 << (end + 1 - start)) - 1; + return (val >> start) & mask; } -static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); } +static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); } // Update the immediate field in a AARCH64 ldr, str, and add instruction. -static void or32AArch64Imm(uint8_t *L, uint64_t Imm) { - or32le(L, (Imm & 0xFFF) << 10); +static void or32AArch64Imm(uint8_t *l, uint64_t imm) { + or32le(l, (imm & 0xFFF) << 10); } -void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_AARCH64_ABS16: case R_AARCH64_PREL16: - checkIntUInt(Loc, Val, 16, Type); - write16le(Loc, Val); + checkIntUInt(loc, val, 16, type); + write16le(loc, val); break; case R_AARCH64_ABS32: case R_AARCH64_PREL32: - checkIntUInt(Loc, Val, 32, Type); - write32le(Loc, Val); + checkIntUInt(loc, val, 32, type); + write32le(loc, val); break; case R_AARCH64_ABS64: - case R_AARCH64_GLOB_DAT: case R_AARCH64_PREL64: - write64le(Loc, Val); + write64le(loc, val); break; case R_AARCH64_ADD_ABS_LO12_NC: - or32AArch64Imm(Loc, Val); + or32AArch64Imm(loc, val); break; case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_ADR_PREL_PG_HI21: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case R_AARCH64_TLSDESC_ADR_PAGE21: - checkInt(Loc, Val, 33, Type); - write32AArch64Addr(Loc, Val >> 12); + checkInt(loc, val, 33, type); + LLVM_FALLTHROUGH; + case R_AARCH64_ADR_PREL_PG_HI21_NC: + write32AArch64Addr(loc, val >> 12); break; case R_AARCH64_ADR_PREL_LO21: - checkInt(Loc, Val, 21, Type); - write32AArch64Addr(Loc, Val); + checkInt(loc, val, 21, type); + write32AArch64Addr(loc, val); break; case R_AARCH64_JUMP26: // Normally we would just write the bits of the immediate field, however @@ -287,75 +287,75 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { // opcode and the immediate (0 001 | 01 imm26) we can do this // transformation by placing a R_AARCH64_JUMP26 relocation at the offset of // the instruction we want to patch. - write32le(Loc, 0x14000000); + write32le(loc, 0x14000000); LLVM_FALLTHROUGH; case R_AARCH64_CALL26: - checkInt(Loc, Val, 28, Type); - or32le(Loc, (Val & 0x0FFFFFFC) >> 2); + checkInt(loc, val, 28, type); + or32le(loc, (val & 0x0FFFFFFC) >> 2); break; case R_AARCH64_CONDBR19: case R_AARCH64_LD_PREL_LO19: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 21, Type); - or32le(Loc, (Val & 0x1FFFFC) << 3); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 21, type); + or32le(loc, (val & 0x1FFFFC) << 3); break; case R_AARCH64_LDST8_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC: - or32AArch64Imm(Loc, getBits(Val, 0, 11)); + or32AArch64Imm(loc, getBits(val, 0, 11)); break; case R_AARCH64_LDST16_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: - checkAlignment(Loc, Val, 2, Type); - or32AArch64Imm(Loc, getBits(Val, 1, 11)); + checkAlignment(loc, val, 2, type); + or32AArch64Imm(loc, getBits(val, 1, 11)); break; case R_AARCH64_LDST32_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: - checkAlignment(Loc, Val, 4, Type); - or32AArch64Imm(Loc, getBits(Val, 2, 11)); + checkAlignment(loc, val, 4, type); + or32AArch64Imm(loc, getBits(val, 2, 11)); break; case R_AARCH64_LDST64_ABS_LO12_NC: case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: case R_AARCH64_TLSDESC_LD64_LO12: - checkAlignment(Loc, Val, 8, Type); - or32AArch64Imm(Loc, getBits(Val, 3, 11)); + checkAlignment(loc, val, 8, type); + or32AArch64Imm(loc, getBits(val, 3, 11)); break; case R_AARCH64_LDST128_ABS_LO12_NC: case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: - checkAlignment(Loc, Val, 16, Type); - or32AArch64Imm(Loc, getBits(Val, 4, 11)); + checkAlignment(loc, val, 16, type); + or32AArch64Imm(loc, getBits(val, 4, 11)); break; case R_AARCH64_MOVW_UABS_G0_NC: - or32le(Loc, (Val & 0xFFFF) << 5); + or32le(loc, (val & 0xFFFF) << 5); break; case R_AARCH64_MOVW_UABS_G1_NC: - or32le(Loc, (Val & 0xFFFF0000) >> 11); + or32le(loc, (val & 0xFFFF0000) >> 11); break; case R_AARCH64_MOVW_UABS_G2_NC: - or32le(Loc, (Val & 0xFFFF00000000) >> 27); + or32le(loc, (val & 0xFFFF00000000) >> 27); break; case R_AARCH64_MOVW_UABS_G3: - or32le(Loc, (Val & 0xFFFF000000000000) >> 43); + or32le(loc, (val & 0xFFFF000000000000) >> 43); break; case R_AARCH64_TSTBR14: - checkInt(Loc, Val, 16, Type); - or32le(Loc, (Val & 0xFFFC) << 3); + checkInt(loc, val, 16, type); + or32le(loc, (val & 0xFFFC) << 3); break; case R_AARCH64_TLSLE_ADD_TPREL_HI12: - checkUInt(Loc, Val, 24, Type); - or32AArch64Imm(Loc, Val >> 12); + checkUInt(loc, val, 24, type); + or32AArch64Imm(loc, val >> 12); break; case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: case R_AARCH64_TLSDESC_ADD_LO12: - or32AArch64Imm(Loc, Val); + or32AArch64Imm(loc, val); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } -void AArch64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void AArch64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // TLSDESC Global-Dynamic relocation are in the form: // adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21] // ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12] @@ -367,25 +367,25 @@ void AArch64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // movk x0, #0x10 // nop // nop - checkUInt(Loc, Val, 32, Type); + checkUInt(loc, val, 32, type); - switch (Type) { + switch (type) { case R_AARCH64_TLSDESC_ADD_LO12: case R_AARCH64_TLSDESC_CALL: - write32le(Loc, 0xd503201f); // nop + write32le(loc, 0xd503201f); // nop return; case R_AARCH64_TLSDESC_ADR_PAGE21: - write32le(Loc, 0xd2a00000 | (((Val >> 16) & 0xffff) << 5)); // movz + write32le(loc, 0xd2a00000 | (((val >> 16) & 0xffff) << 5)); // movz return; case R_AARCH64_TLSDESC_LD64_LO12: - write32le(Loc, 0xf2800000 | ((Val & 0xffff) << 5)); // movk + write32le(loc, 0xf2800000 | ((val & 0xffff) << 5)); // movk return; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } -void AArch64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void AArch64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { // TLSDESC Global-Dynamic relocation are in the form: // adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21] // ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12] @@ -398,43 +398,193 @@ void AArch64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { // nop // nop - switch (Type) { + switch (type) { case R_AARCH64_TLSDESC_ADD_LO12: case R_AARCH64_TLSDESC_CALL: - write32le(Loc, 0xd503201f); // nop + write32le(loc, 0xd503201f); // nop break; case R_AARCH64_TLSDESC_ADR_PAGE21: - write32le(Loc, 0x90000000); // adrp - relocateOne(Loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, Val); + write32le(loc, 0x90000000); // adrp + relocateOne(loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, val); break; case R_AARCH64_TLSDESC_LD64_LO12: - write32le(Loc, 0xf9400000); // ldr - relocateOne(Loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, Val); + write32le(loc, 0xf9400000); // ldr + relocateOne(loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, val); break; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } -void AArch64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { - checkUInt(Loc, Val, 32, Type); +void AArch64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { + checkUInt(loc, val, 32, type); - if (Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) { + if (type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) { // Generate MOVZ. - uint32_t RegNo = read32le(Loc) & 0x1f; - write32le(Loc, (0xd2a00000 | RegNo) | (((Val >> 16) & 0xffff) << 5)); + uint32_t regNo = read32le(loc) & 0x1f; + write32le(loc, (0xd2a00000 | regNo) | (((val >> 16) & 0xffff) << 5)); return; } - if (Type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) { + if (type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) { // Generate MOVK. - uint32_t RegNo = read32le(Loc) & 0x1f; - write32le(Loc, (0xf2800000 | RegNo) | ((Val & 0xffff) << 5)); + uint32_t regNo = read32le(loc) & 0x1f; + write32le(loc, (0xf2800000 | regNo) | ((val & 0xffff) << 5)); return; } llvm_unreachable("invalid relocation for TLS IE to LE relaxation"); } -TargetInfo *elf::getAArch64TargetInfo() { - static AArch64 Target; - return &Target; +// AArch64 may use security features in variant PLT sequences. These are: +// Pointer Authentication (PAC), introduced in armv8.3-a and Branch Target +// Indicator (BTI) introduced in armv8.5-a. The additional instructions used +// in the variant Plt sequences are encoded in the Hint space so they can be +// deployed on older architectures, which treat the instructions as a nop. +// PAC and BTI can be combined leading to the following combinations: +// writePltHeader +// writePltHeaderBti (no PAC Header needed) +// writePlt +// writePltBti (BTI only) +// writePltPac (PAC only) +// writePltBtiPac (BTI and PAC) +// +// When PAC is enabled the dynamic loader encrypts the address that it places +// in the .got.plt using the pacia1716 instruction which encrypts the value in +// x17 using the modifier in x16. The static linker places autia1716 before the +// indirect branch to x17 to authenticate the address in x17 with the modifier +// in x16. This makes it more difficult for an attacker to modify the value in +// the .got.plt. +// +// When BTI is enabled all indirect branches must land on a bti instruction. +// The static linker must place a bti instruction at the start of any PLT entry +// that may be the target of an indirect branch. As the PLT entries call the +// lazy resolver indirectly this must have a bti instruction at start. In +// general a bti instruction is not needed for a PLT entry as indirect calls +// are resolved to the function address and not the PLT entry for the function. +// There are a small number of cases where the PLT address can escape, such as +// taking the address of a function or ifunc via a non got-generating +// relocation, and a shared library refers to that symbol. +// +// We use the bti c variant of the instruction which permits indirect branches +// (br) via x16/x17 and indirect function calls (blr) via any register. The ABI +// guarantees that all indirect branches from code requiring BTI protection +// will go via x16/x17 + +namespace { +class AArch64BtiPac final : public AArch64 { +public: + AArch64BtiPac(); + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + +private: + bool btiHeader; // bti instruction needed in PLT Header + bool btiEntry; // bti instruction needed in PLT Entry + bool pacEntry; // autia1716 instruction needed in PLT Entry +}; +} // namespace + +AArch64BtiPac::AArch64BtiPac() { + btiHeader = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI); + // A BTI (Branch Target Indicator) Plt Entry is only required if the + // address of the PLT entry can be taken by the program, which permits an + // indirect jump to the PLT entry. This can happen when the address + // of the PLT entry for a function is canonicalised due to the address of + // the function in an executable being taken by a shared library. + // FIXME: There is a potential optimization to omit the BTI if we detect + // that the address of the PLT entry isn't taken. + btiEntry = btiHeader && !config->shared; + pacEntry = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC); + + if (btiEntry || pacEntry) + pltEntrySize = 24; } + +void AArch64BtiPac::writePltHeader(uint8_t *buf) const { + const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c + const uint8_t pltData[] = { + 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]! + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2])) + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5 // nop + }; + const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop + + uint64_t got = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + + if (btiHeader) { + // PltHeader is called indirectly by plt[N]. Prefix pltData with a BTI C + // instruction. + memcpy(buf, btiData, sizeof(btiData)); + buf += sizeof(btiData); + plt += sizeof(btiData); + } + memcpy(buf, pltData, sizeof(pltData)); + + relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(got + 16) - getAArch64Page(plt + 8)); + relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16); + relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16); + if (!btiHeader) + // We didn't add the BTI c instruction so round out size with NOP. + memcpy(buf + sizeof(pltData), nopData, sizeof(nopData)); +} + +void AArch64BtiPac::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + // The PLT entry is of the form: + // [btiData] addrInst (pacBr | stdBr) [nopData] + const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c + const uint8_t addrInst[] = { + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))] + 0x10, 0x02, 0x00, 0x91 // add x16, x16, Offset(&(.plt.got[n])) + }; + const uint8_t pacBr[] = { + 0x9f, 0x21, 0x03, 0xd5, // autia1716 + 0x20, 0x02, 0x1f, 0xd6 // br x17 + }; + const uint8_t stdBr[] = { + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5 // nop + }; + const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop + + if (btiEntry) { + memcpy(buf, btiData, sizeof(btiData)); + buf += sizeof(btiData); + pltEntryAddr += sizeof(btiData); + } + + memcpy(buf, addrInst, sizeof(addrInst)); + relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(gotPltEntryAddr) - + getAArch64Page(pltEntryAddr)); + relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr); + relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr); + + if (pacEntry) + memcpy(buf + sizeof(addrInst), pacBr, sizeof(pacBr)); + else + memcpy(buf + sizeof(addrInst), stdBr, sizeof(stdBr)); + if (!btiEntry) + // We didn't add the BTI c instruction so round out size with NOP. + memcpy(buf + sizeof(addrInst) + sizeof(stdBr), nopData, sizeof(nopData)); +} + +static TargetInfo *getTargetInfo() { + if (config->andFeatures & (GNU_PROPERTY_AARCH64_FEATURE_1_BTI | + GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) { + static AArch64BtiPac t; + return &t; + } + static AArch64 t; + return &t; +} + +TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); } diff --git a/ELF/Arch/AMDGPU.cpp b/ELF/Arch/AMDGPU.cpp index a7c6c84..f2e32ca 100644 --- a/ELF/Arch/AMDGPU.cpp +++ b/ELF/Arch/AMDGPU.cpp @@ -1,9 +1,8 @@ //===- AMDGPU.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 // //===----------------------------------------------------------------------===// @@ -26,62 +25,63 @@ class AMDGPU final : public TargetInfo { public: AMDGPU(); uint32_t calcEFlags() const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; }; } // namespace AMDGPU::AMDGPU() { - RelativeRel = R_AMDGPU_RELATIVE64; - GotRel = R_AMDGPU_ABS64; - NoneRel = R_AMDGPU_NONE; - GotEntrySize = 8; + relativeRel = R_AMDGPU_RELATIVE64; + gotRel = R_AMDGPU_ABS64; + noneRel = R_AMDGPU_NONE; + symbolicRel = R_AMDGPU_ABS64; } -static uint32_t getEFlags(InputFile *File) { - return cast>(File)->getObj().getHeader()->e_flags; +static uint32_t getEFlags(InputFile *file) { + return cast>(file)->getObj().getHeader()->e_flags; } uint32_t AMDGPU::calcEFlags() const { - assert(!ObjectFiles.empty()); - uint32_t Ret = getEFlags(ObjectFiles[0]); + assert(!objectFiles.empty()); + uint32_t ret = getEFlags(objectFiles[0]); // Verify that all input files have the same e_flags. - for (InputFile *F : makeArrayRef(ObjectFiles).slice(1)) { - if (Ret == getEFlags(F)) + for (InputFile *f : makeArrayRef(objectFiles).slice(1)) { + if (ret == getEFlags(f)) continue; - error("incompatible e_flags: " + toString(F)); + error("incompatible e_flags: " + toString(f)); return 0; } - return Ret; + return ret; } -void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void AMDGPU::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_AMDGPU_ABS32: case R_AMDGPU_GOTPCREL: case R_AMDGPU_GOTPCREL32_LO: case R_AMDGPU_REL32: case R_AMDGPU_REL32_LO: - write32le(Loc, Val); + write32le(loc, val); break; case R_AMDGPU_ABS64: case R_AMDGPU_REL64: - write64le(Loc, Val); + write64le(loc, val); break; case R_AMDGPU_GOTPCREL32_HI: case R_AMDGPU_REL32_HI: - write32le(Loc, Val >> 32); + write32le(loc, val >> 32); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -RelExpr AMDGPU::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_AMDGPU_ABS32: case R_AMDGPU_ABS64: return R_ABS; @@ -95,11 +95,19 @@ RelExpr AMDGPU::getRelExpr(RelType Type, const Symbol &S, case R_AMDGPU_GOTPCREL32_HI: return R_GOT_PC; default: - return R_INVALID; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } +RelType AMDGPU::getDynRel(RelType type) const { + if (type == R_AMDGPU_ABS64) + return type; + return R_AMDGPU_NONE; +} + TargetInfo *elf::getAMDGPUTargetInfo() { - static AMDGPU Target; - return &Target; + static AMDGPU target; + return ⌖ } diff --git a/ELF/Arch/ARM.cpp b/ELF/Arch/ARM.cpp index 120caca..64adc33 100644 --- a/ELF/Arch/ARM.cpp +++ b/ELF/Arch/ARM.cpp @@ -1,9 +1,8 @@ //===- ARM.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 // //===----------------------------------------------------------------------===// @@ -27,63 +26,62 @@ class ARM final : public TargetInfo { public: ARM(); uint32_t calcEFlags() const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - RelType getDynRel(RelType Type) const override; - int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override; - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writeIgotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - void addPltSymbols(InputSection &IS, uint64_t Off) const override; - void addPltHeaderSymbols(InputSection &ISD) const override; - bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writeIgotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void addPltSymbols(InputSection &isec, uint64_t off) const override; + void addPltHeaderSymbols(InputSection &isd) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; uint32_t getThunkSectionSpacing() const override; - bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace ARM::ARM() { - CopyRel = R_ARM_COPY; - RelativeRel = R_ARM_RELATIVE; - IRelativeRel = R_ARM_IRELATIVE; - GotRel = R_ARM_GLOB_DAT; - NoneRel = R_ARM_NONE; - PltRel = R_ARM_JUMP_SLOT; - TlsGotRel = R_ARM_TLS_TPOFF32; - TlsModuleIndexRel = R_ARM_TLS_DTPMOD32; - TlsOffsetRel = R_ARM_TLS_DTPOFF32; - GotBaseSymInGotPlt = false; - GotEntrySize = 4; - GotPltEntrySize = 4; - PltEntrySize = 16; - PltHeaderSize = 32; - TrapInstr = {0xd4, 0xd4, 0xd4, 0xd4}; - NeedsThunks = true; + copyRel = R_ARM_COPY; + relativeRel = R_ARM_RELATIVE; + iRelativeRel = R_ARM_IRELATIVE; + gotRel = R_ARM_GLOB_DAT; + noneRel = R_ARM_NONE; + pltRel = R_ARM_JUMP_SLOT; + symbolicRel = R_ARM_ABS32; + tlsGotRel = R_ARM_TLS_TPOFF32; + tlsModuleIndexRel = R_ARM_TLS_DTPMOD32; + tlsOffsetRel = R_ARM_TLS_DTPOFF32; + gotBaseSymInGotPlt = false; + pltEntrySize = 16; + pltHeaderSize = 32; + trapInstr = {0xd4, 0xd4, 0xd4, 0xd4}; + needsThunks = true; } uint32_t ARM::calcEFlags() const { // The ABIFloatType is used by loaders to detect the floating point calling // convention. - uint32_t ABIFloatType = 0; - if (Config->ARMVFPArgs == ARMVFPArgKind::Base || - Config->ARMVFPArgs == ARMVFPArgKind::Default) - ABIFloatType = EF_ARM_ABI_FLOAT_SOFT; - else if (Config->ARMVFPArgs == ARMVFPArgKind::VFP) - ABIFloatType = EF_ARM_ABI_FLOAT_HARD; + uint32_t abiFloatType = 0; + if (config->armVFPArgs == ARMVFPArgKind::Base || + config->armVFPArgs == ARMVFPArgKind::Default) + abiFloatType = EF_ARM_ABI_FLOAT_SOFT; + else if (config->armVFPArgs == ARMVFPArgKind::VFP) + abiFloatType = EF_ARM_ABI_FLOAT_HARD; // We don't currently use any features incompatible with EF_ARM_EABI_VER5, // but we don't have any firm guarantees of conformance. Linux AArch64 // kernels (as of 2016) require an EABI version to be set. - return EF_ARM_EABI_VER5 | ABIFloatType; + return EF_ARM_EABI_VER5 | abiFloatType; } -RelExpr ARM::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr ARM::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_ARM_THM_JUMP11: return R_PC; case R_ARM_CALL: @@ -108,11 +106,11 @@ RelExpr ARM::getRelExpr(RelType Type, const Symbol &S, case R_ARM_SBREL32: return R_ARM_SBREL; case R_ARM_TARGET1: - return Config->Target1Rel ? R_PC : R_ABS; + return config->target1Rel ? R_PC : R_ABS; case R_ARM_TARGET2: - if (Config->Target2 == Target2Policy::Rel) + if (config->target2 == Target2Policy::Rel) return R_PC; - if (Config->Target2 == Target2Policy::Abs) + if (config->target2 == Target2Policy::Abs) return R_ABS; return R_GOT_PC; case R_ARM_TLS_GD32: @@ -145,25 +143,25 @@ RelExpr ARM::getRelExpr(RelType Type, const Symbol &S, } } -RelType ARM::getDynRel(RelType Type) const { - if ((Type == R_ARM_ABS32) || (Type == R_ARM_TARGET1 && !Config->Target1Rel)) +RelType ARM::getDynRel(RelType type) const { + if ((type == R_ARM_ABS32) || (type == R_ARM_TARGET1 && !config->target1Rel)) return R_ARM_ABS32; return R_ARM_NONE; } -void ARM::writeGotPlt(uint8_t *Buf, const Symbol &) const { - write32le(Buf, In.Plt->getVA()); +void ARM::writeGotPlt(uint8_t *buf, const Symbol &) const { + write32le(buf, in.plt->getVA()); } -void ARM::writeIgotPlt(uint8_t *Buf, const Symbol &S) const { +void ARM::writeIgotPlt(uint8_t *buf, const Symbol &s) const { // An ARM entry is the address of the ifunc resolver function. - write32le(Buf, S.getVA()); + write32le(buf, s.getVA()); } // Long form PLT Header that does not have any restrictions on the displacement // of the .plt from the .plt.got. -static void writePltHeaderLong(uint8_t *Buf) { - const uint8_t PltData[] = { +static void writePltHeaderLong(uint8_t *buf) { + const uint8_t pltData[] = { 0x04, 0xe0, 0x2d, 0xe5, // str lr, [sp,#-4]! 0x04, 0xe0, 0x9f, 0xe5, // ldr lr, L2 0x0e, 0xe0, 0x8f, 0xe0, // L1: add lr, pc, lr @@ -172,128 +170,128 @@ static void writePltHeaderLong(uint8_t *Buf) { 0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary 0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary 0xd4, 0xd4, 0xd4, 0xd4}; - memcpy(Buf, PltData, sizeof(PltData)); - uint64_t GotPlt = In.GotPlt->getVA(); - uint64_t L1 = In.Plt->getVA() + 8; - write32le(Buf + 16, GotPlt - L1 - 8); + memcpy(buf, pltData, sizeof(pltData)); + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t l1 = in.plt->getVA() + 8; + write32le(buf + 16, gotPlt - l1 - 8); } // The default PLT header requires the .plt.got to be within 128 Mb of the // .plt in the positive direction. -void ARM::writePltHeader(uint8_t *Buf) const { +void ARM::writePltHeader(uint8_t *buf) const { // Use a similar sequence to that in writePlt(), the difference is the calling // conventions mean we use lr instead of ip. The PLT entry is responsible for // saving lr on the stack, the dynamic loader is responsible for reloading // it. - const uint32_t PltData[] = { + const uint32_t pltData[] = { 0xe52de004, // L1: str lr, [sp,#-4]! 0xe28fe600, // add lr, pc, #0x0NN00000 &(.got.plt - L1 - 4) 0xe28eea00, // add lr, lr, #0x000NN000 &(.got.plt - L1 - 4) 0xe5bef000, // ldr pc, [lr, #0x00000NNN] &(.got.plt -L1 - 4) }; - uint64_t Offset = In.GotPlt->getVA() - In.Plt->getVA() - 4; - if (!llvm::isUInt<27>(Offset)) { + uint64_t offset = in.gotPlt->getVA() - in.plt->getVA() - 4; + if (!llvm::isUInt<27>(offset)) { // We cannot encode the Offset, use the long form. - writePltHeaderLong(Buf); + writePltHeaderLong(buf); return; } - write32le(Buf + 0, PltData[0]); - write32le(Buf + 4, PltData[1] | ((Offset >> 20) & 0xff)); - write32le(Buf + 8, PltData[2] | ((Offset >> 12) & 0xff)); - write32le(Buf + 12, PltData[3] | (Offset & 0xfff)); - memcpy(Buf + 16, TrapInstr.data(), 4); // Pad to 32-byte boundary - memcpy(Buf + 20, TrapInstr.data(), 4); - memcpy(Buf + 24, TrapInstr.data(), 4); - memcpy(Buf + 28, TrapInstr.data(), 4); + write32le(buf + 0, pltData[0]); + write32le(buf + 4, pltData[1] | ((offset >> 20) & 0xff)); + write32le(buf + 8, pltData[2] | ((offset >> 12) & 0xff)); + write32le(buf + 12, pltData[3] | (offset & 0xfff)); + memcpy(buf + 16, trapInstr.data(), 4); // Pad to 32-byte boundary + memcpy(buf + 20, trapInstr.data(), 4); + memcpy(buf + 24, trapInstr.data(), 4); + memcpy(buf + 28, trapInstr.data(), 4); } -void ARM::addPltHeaderSymbols(InputSection &IS) const { - addSyntheticLocal("$a", STT_NOTYPE, 0, 0, IS); - addSyntheticLocal("$d", STT_NOTYPE, 16, 0, IS); +void ARM::addPltHeaderSymbols(InputSection &isec) const { + addSyntheticLocal("$a", STT_NOTYPE, 0, 0, isec); + addSyntheticLocal("$d", STT_NOTYPE, 16, 0, isec); } // Long form PLT entries that do not have any restrictions on the displacement // of the .plt from the .plt.got. -static void writePltLong(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) { - const uint8_t PltData[] = { +static void writePltLong(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) { + const uint8_t pltData[] = { 0x04, 0xc0, 0x9f, 0xe5, // ldr ip, L2 0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc 0x00, 0xf0, 0x9c, 0xe5, // ldr pc, [ip] 0x00, 0x00, 0x00, 0x00, // L2: .word Offset(&(.plt.got) - L1 - 8 }; - memcpy(Buf, PltData, sizeof(PltData)); - uint64_t L1 = PltEntryAddr + 4; - write32le(Buf + 12, GotPltEntryAddr - L1 - 8); + memcpy(buf, pltData, sizeof(pltData)); + uint64_t l1 = pltEntryAddr + 4; + write32le(buf + 12, gotPltEntryAddr - l1 - 8); } // The default PLT entries require the .plt.got to be within 128 Mb of the // .plt in the positive direction. -void ARM::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { +void ARM::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { // The PLT entry is similar to the example given in Appendix A of ELF for // the Arm Architecture. Instead of using the Group Relocations to find the // optimal rotation for the 8-bit immediate used in the add instructions we // hard code the most compact rotations for simplicity. This saves a load // instruction over the long plt sequences. - const uint32_t PltData[] = { + const uint32_t pltData[] = { 0xe28fc600, // L1: add ip, pc, #0x0NN00000 Offset(&(.plt.got) - L1 - 8 0xe28cca00, // add ip, ip, #0x000NN000 Offset(&(.plt.got) - L1 - 8 0xe5bcf000, // ldr pc, [ip, #0x00000NNN] Offset(&(.plt.got) - L1 - 8 }; - uint64_t Offset = GotPltEntryAddr - PltEntryAddr - 8; - if (!llvm::isUInt<27>(Offset)) { + uint64_t offset = gotPltEntryAddr - pltEntryAddr - 8; + if (!llvm::isUInt<27>(offset)) { // We cannot encode the Offset, use the long form. - writePltLong(Buf, GotPltEntryAddr, PltEntryAddr, Index, RelOff); + writePltLong(buf, gotPltEntryAddr, pltEntryAddr, index, relOff); return; } - write32le(Buf + 0, PltData[0] | ((Offset >> 20) & 0xff)); - write32le(Buf + 4, PltData[1] | ((Offset >> 12) & 0xff)); - write32le(Buf + 8, PltData[2] | (Offset & 0xfff)); - memcpy(Buf + 12, TrapInstr.data(), 4); // Pad to 16-byte boundary + write32le(buf + 0, pltData[0] | ((offset >> 20) & 0xff)); + write32le(buf + 4, pltData[1] | ((offset >> 12) & 0xff)); + write32le(buf + 8, pltData[2] | (offset & 0xfff)); + memcpy(buf + 12, trapInstr.data(), 4); // Pad to 16-byte boundary } -void ARM::addPltSymbols(InputSection &IS, uint64_t Off) const { - addSyntheticLocal("$a", STT_NOTYPE, Off, 0, IS); - addSyntheticLocal("$d", STT_NOTYPE, Off + 12, 0, IS); +void ARM::addPltSymbols(InputSection &isec, uint64_t off) const { + addSyntheticLocal("$a", STT_NOTYPE, off, 0, isec); + addSyntheticLocal("$d", STT_NOTYPE, off + 12, 0, isec); } -bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const { +bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { // If S is an undefined weak symbol and does not have a PLT entry then it // will be resolved as a branch to the next instruction. - if (S.isUndefWeak() && !S.isInPlt()) + if (s.isUndefWeak() && !s.isInPlt()) return false; // A state change from ARM to Thumb and vice versa must go through an // interworking thunk if the relocation type is not R_ARM_CALL or // R_ARM_THM_CALL. - switch (Type) { + switch (type) { case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_JUMP24: // Source is ARM, all PLT entries are ARM so no interworking required. // Otherwise we need to interwork if Symbol has bit 0 set (Thumb). - if (Expr == R_PC && ((S.getVA() & 1) == 1)) + if (expr == R_PC && ((s.getVA() & 1) == 1)) return true; LLVM_FALLTHROUGH; case R_ARM_CALL: { - uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA(); - return !inBranchRange(Type, BranchAddr, Dst); + uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); + return !inBranchRange(type, branchAddr, dst); } case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: // Source is Thumb, all PLT entries are ARM so interworking is required. // Otherwise we need to interwork if Symbol has bit 0 clear (ARM). - if (Expr == R_PLT_PC || ((S.getVA() & 1) == 0)) + if (expr == R_PLT_PC || ((s.getVA() & 1) == 0)) return true; LLVM_FALLTHROUGH; case R_ARM_THM_CALL: { - uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA(); - return !inBranchRange(Type, BranchAddr, Dst); + uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA(); + return !inBranchRange(type, branchAddr, dst); } } return false; @@ -301,13 +299,13 @@ bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, uint32_t ARM::getThunkSectionSpacing() const { // The placing of pre-created ThunkSections is controlled by the value - // ThunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to + // thunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to // place the ThunkSection such that all branches from the InputSections // prior to the ThunkSection can reach a Thunk placed at the end of the // ThunkSection. Graphically: - // | up to ThunkSectionSpacing .text input sections | + // | up to thunkSectionSpacing .text input sections | // | ThunkSection | - // | up to ThunkSectionSpacing .text input sections | + // | up to thunkSectionSpacing .text input sections | // | ThunkSection | // Pre-created ThunkSections are spaced roughly 16MiB apart on ARMv7. This @@ -318,69 +316,68 @@ uint32_t ARM::getThunkSectionSpacing() const { // Thumb B.W range +/- 1MiB // If a branch cannot reach a pre-created ThunkSection a new one will be // created so we can handle the rare cases of a Thumb 2 conditional branch. - // We intentionally use a lower size for ThunkSectionSpacing than the maximum + // We intentionally use a lower size for thunkSectionSpacing than the maximum // branch range so the end of the ThunkSection is more likely to be within // range of the branch instruction that is furthest away. The value we shorten - // ThunkSectionSpacing by is set conservatively to allow us to create 16,384 + // thunkSectionSpacing by is set conservatively to allow us to create 16,384 // 12 byte Thunks at any offset in a ThunkSection without risk of a branch to // one of the Thunks going out of range. - // On Arm the ThunkSectionSpacing depends on the range of the Thumb Branch + // On Arm the thunkSectionSpacing depends on the range of the Thumb Branch // range. On earlier Architectures such as ARMv4, ARMv5 and ARMv6 (except // ARMv6T2) the range is +/- 4MiB. - return (Config->ARMJ1J2BranchEncoding) ? 0x1000000 - 0x30000 + return (config->armJ1J2BranchEncoding) ? 0x1000000 - 0x30000 : 0x400000 - 0x7500; } -bool ARM::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { - uint64_t Range; - uint64_t InstrSize; +bool ARM::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + uint64_t range; + uint64_t instrSize; - switch (Type) { + switch (type) { case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_JUMP24: case R_ARM_CALL: - Range = 0x2000000; - InstrSize = 4; + range = 0x2000000; + instrSize = 4; break; case R_ARM_THM_JUMP19: - Range = 0x100000; - InstrSize = 2; + range = 0x100000; + instrSize = 2; break; case R_ARM_THM_JUMP24: case R_ARM_THM_CALL: - Range = Config->ARMJ1J2BranchEncoding ? 0x1000000 : 0x400000; - InstrSize = 2; + range = config->armJ1J2BranchEncoding ? 0x1000000 : 0x400000; + instrSize = 2; break; default: return true; } // PC at Src is 2 instructions ahead, immediate of branch is signed - if (Src > Dst) - Range -= 2 * InstrSize; + if (src > dst) + range -= 2 * instrSize; else - Range += InstrSize; + range += instrSize; - if ((Dst & 0x1) == 0) + if ((dst & 0x1) == 0) // Destination is ARM, if ARM caller then Src is already 4-byte aligned. // If Thumb Caller (BLX) the Src address has bottom 2 bits cleared to ensure // destination will be 4 byte aligned. - Src &= ~0x3; + src &= ~0x3; else // Bit 0 == 1 denotes Thumb state, it is not part of the range - Dst &= ~0x1; + dst &= ~0x1; - uint64_t Distance = (Src > Dst) ? Src - Dst : Dst - Src; - return Distance <= Range; + uint64_t distance = (src > dst) ? src - dst : dst - src; + return distance <= range; } -void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_ARM_ABS32: case R_ARM_BASE_PREL: - case R_ARM_GLOB_DAT: case R_ARM_GOTOFF32: case R_ARM_GOT_BREL: case R_ARM_GOT_PREL: @@ -396,135 +393,132 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_ARM_TLS_LE32: case R_ARM_TLS_TPOFF32: case R_ARM_TLS_DTPOFF32: - write32le(Loc, Val); - break; - case R_ARM_TLS_DTPMOD32: - write32le(Loc, 1); + write32le(loc, val); break; case R_ARM_PREL31: - checkInt(Loc, Val, 31, Type); - write32le(Loc, (read32le(Loc) & 0x80000000) | (Val & ~0x80000000)); + checkInt(loc, val, 31, type); + write32le(loc, (read32le(loc) & 0x80000000) | (val & ~0x80000000)); break; case R_ARM_CALL: // R_ARM_CALL is used for BL and BLX instructions, depending on the // value of bit 0 of Val, we must select a BL or BLX instruction - if (Val & 1) { + if (val & 1) { // If bit 0 of Val is 1 the target is Thumb, we must select a BLX. // The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1' - checkInt(Loc, Val, 26, Type); - write32le(Loc, 0xfa000000 | // opcode - ((Val & 2) << 23) | // H - ((Val >> 2) & 0x00ffffff)); // imm24 + checkInt(loc, val, 26, type); + write32le(loc, 0xfa000000 | // opcode + ((val & 2) << 23) | // H + ((val >> 2) & 0x00ffffff)); // imm24 break; } - if ((read32le(Loc) & 0xfe000000) == 0xfa000000) + if ((read32le(loc) & 0xfe000000) == 0xfa000000) // BLX (always unconditional) instruction to an ARM Target, select an // unconditional BL. - write32le(Loc, 0xeb000000 | (read32le(Loc) & 0x00ffffff)); + write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff)); // fall through as BL encoding is shared with B LLVM_FALLTHROUGH; case R_ARM_JUMP24: case R_ARM_PC24: case R_ARM_PLT32: - checkInt(Loc, Val, 26, Type); - write32le(Loc, (read32le(Loc) & ~0x00ffffff) | ((Val >> 2) & 0x00ffffff)); + checkInt(loc, val, 26, type); + write32le(loc, (read32le(loc) & ~0x00ffffff) | ((val >> 2) & 0x00ffffff)); break; case R_ARM_THM_JUMP11: - checkInt(Loc, Val, 12, Type); - write16le(Loc, (read32le(Loc) & 0xf800) | ((Val >> 1) & 0x07ff)); + checkInt(loc, val, 12, type); + write16le(loc, (read32le(loc) & 0xf800) | ((val >> 1) & 0x07ff)); break; case R_ARM_THM_JUMP19: // Encoding T3: Val = S:J2:J1:imm6:imm11:0 - checkInt(Loc, Val, 21, Type); - write16le(Loc, - (read16le(Loc) & 0xfbc0) | // opcode cond - ((Val >> 10) & 0x0400) | // S - ((Val >> 12) & 0x003f)); // imm6 - write16le(Loc + 2, + checkInt(loc, val, 21, type); + write16le(loc, + (read16le(loc) & 0xfbc0) | // opcode cond + ((val >> 10) & 0x0400) | // S + ((val >> 12) & 0x003f)); // imm6 + write16le(loc + 2, 0x8000 | // opcode - ((Val >> 8) & 0x0800) | // J2 - ((Val >> 5) & 0x2000) | // J1 - ((Val >> 1) & 0x07ff)); // imm11 + ((val >> 8) & 0x0800) | // J2 + ((val >> 5) & 0x2000) | // J1 + ((val >> 1) & 0x07ff)); // imm11 break; case R_ARM_THM_CALL: // R_ARM_THM_CALL is used for BL and BLX instructions, depending on the // value of bit 0 of Val, we must select a BL or BLX instruction - if ((Val & 1) == 0) { + if ((val & 1) == 0) { // Ensure BLX destination is 4-byte aligned. As BLX instruction may // only be two byte aligned. This must be done before overflow check - Val = alignTo(Val, 4); + val = alignTo(val, 4); } // Bit 12 is 0 for BLX, 1 for BL - write16le(Loc + 2, (read16le(Loc + 2) & ~0x1000) | (Val & 1) << 12); - if (!Config->ARMJ1J2BranchEncoding) { + write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | (val & 1) << 12); + if (!config->armJ1J2BranchEncoding) { // Older Arm architectures do not support R_ARM_THM_JUMP24 and have // different encoding rules and range due to J1 and J2 always being 1. - checkInt(Loc, Val, 23, Type); - write16le(Loc, + checkInt(loc, val, 23, type); + write16le(loc, 0xf000 | // opcode - ((Val >> 12) & 0x07ff)); // imm11 - write16le(Loc + 2, - (read16le(Loc + 2) & 0xd000) | // opcode + ((val >> 12) & 0x07ff)); // imm11 + write16le(loc + 2, + (read16le(loc + 2) & 0xd000) | // opcode 0x2800 | // J1 == J2 == 1 - ((Val >> 1) & 0x07ff)); // imm11 + ((val >> 1) & 0x07ff)); // imm11 break; } // Fall through as rest of encoding is the same as B.W LLVM_FALLTHROUGH; case R_ARM_THM_JUMP24: // Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0 - checkInt(Loc, Val, 25, Type); - write16le(Loc, + checkInt(loc, val, 25, type); + write16le(loc, 0xf000 | // opcode - ((Val >> 14) & 0x0400) | // S - ((Val >> 12) & 0x03ff)); // imm10 - write16le(Loc + 2, - (read16le(Loc + 2) & 0xd000) | // opcode - (((~(Val >> 10)) ^ (Val >> 11)) & 0x2000) | // J1 - (((~(Val >> 11)) ^ (Val >> 13)) & 0x0800) | // J2 - ((Val >> 1) & 0x07ff)); // imm11 + ((val >> 14) & 0x0400) | // S + ((val >> 12) & 0x03ff)); // imm10 + write16le(loc + 2, + (read16le(loc + 2) & 0xd000) | // opcode + (((~(val >> 10)) ^ (val >> 11)) & 0x2000) | // J1 + (((~(val >> 11)) ^ (val >> 13)) & 0x0800) | // J2 + ((val >> 1) & 0x07ff)); // imm11 break; case R_ARM_MOVW_ABS_NC: case R_ARM_MOVW_PREL_NC: - write32le(Loc, (read32le(Loc) & ~0x000f0fff) | ((Val & 0xf000) << 4) | - (Val & 0x0fff)); + write32le(loc, (read32le(loc) & ~0x000f0fff) | ((val & 0xf000) << 4) | + (val & 0x0fff)); break; case R_ARM_MOVT_ABS: case R_ARM_MOVT_PREL: - write32le(Loc, (read32le(Loc) & ~0x000f0fff) | - (((Val >> 16) & 0xf000) << 4) | ((Val >> 16) & 0xfff)); + write32le(loc, (read32le(loc) & ~0x000f0fff) | + (((val >> 16) & 0xf000) << 4) | ((val >> 16) & 0xfff)); break; case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVT_PREL: // Encoding T1: A = imm4:i:imm3:imm8 - write16le(Loc, + write16le(loc, 0xf2c0 | // opcode - ((Val >> 17) & 0x0400) | // i - ((Val >> 28) & 0x000f)); // imm4 - write16le(Loc + 2, - (read16le(Loc + 2) & 0x8f00) | // opcode - ((Val >> 12) & 0x7000) | // imm3 - ((Val >> 16) & 0x00ff)); // imm8 + ((val >> 17) & 0x0400) | // i + ((val >> 28) & 0x000f)); // imm4 + write16le(loc + 2, + (read16le(loc + 2) & 0x8f00) | // opcode + ((val >> 12) & 0x7000) | // imm3 + ((val >> 16) & 0x00ff)); // imm8 break; case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVW_PREL_NC: // Encoding T3: A = imm4:i:imm3:imm8 - write16le(Loc, + write16le(loc, 0xf240 | // opcode - ((Val >> 1) & 0x0400) | // i - ((Val >> 12) & 0x000f)); // imm4 - write16le(Loc + 2, - (read16le(Loc + 2) & 0x8f00) | // opcode - ((Val << 4) & 0x7000) | // imm3 - (Val & 0x00ff)); // imm8 + ((val >> 1) & 0x0400) | // i + ((val >> 12) & 0x000f)); // imm4 + write16le(loc + 2, + (read16le(loc + 2) & 0x8f00) | // opcode + ((val << 4) & 0x7000) | // imm3 + (val & 0x00ff)); // imm8 break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } -int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const { - switch (Type) { +int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { default: return 0; case R_ARM_ABS32: @@ -540,47 +534,47 @@ int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const { case R_ARM_TLS_LDO32: case R_ARM_TLS_IE32: case R_ARM_TLS_LE32: - return SignExtend64<32>(read32le(Buf)); + return SignExtend64<32>(read32le(buf)); case R_ARM_PREL31: - return SignExtend64<31>(read32le(Buf)); + return SignExtend64<31>(read32le(buf)); case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_PC24: case R_ARM_PLT32: - return SignExtend64<26>(read32le(Buf) << 2); + return SignExtend64<26>(read32le(buf) << 2); case R_ARM_THM_JUMP11: - return SignExtend64<12>(read16le(Buf) << 1); + return SignExtend64<12>(read16le(buf) << 1); case R_ARM_THM_JUMP19: { // Encoding T3: A = S:J2:J1:imm10:imm6:0 - uint16_t Hi = read16le(Buf); - uint16_t Lo = read16le(Buf + 2); - return SignExtend64<20>(((Hi & 0x0400) << 10) | // S - ((Lo & 0x0800) << 8) | // J2 - ((Lo & 0x2000) << 5) | // J1 - ((Hi & 0x003f) << 12) | // imm6 - ((Lo & 0x07ff) << 1)); // imm11:0 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<20>(((hi & 0x0400) << 10) | // S + ((lo & 0x0800) << 8) | // J2 + ((lo & 0x2000) << 5) | // J1 + ((hi & 0x003f) << 12) | // imm6 + ((lo & 0x07ff) << 1)); // imm11:0 } case R_ARM_THM_CALL: - if (!Config->ARMJ1J2BranchEncoding) { + if (!config->armJ1J2BranchEncoding) { // Older Arm architectures do not support R_ARM_THM_JUMP24 and have // different encoding rules and range due to J1 and J2 always being 1. - uint16_t Hi = read16le(Buf); - uint16_t Lo = read16le(Buf + 2); - return SignExtend64<22>(((Hi & 0x7ff) << 12) | // imm11 - ((Lo & 0x7ff) << 1)); // imm11:0 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<22>(((hi & 0x7ff) << 12) | // imm11 + ((lo & 0x7ff) << 1)); // imm11:0 break; } LLVM_FALLTHROUGH; case R_ARM_THM_JUMP24: { // Encoding B T4, BL T1, BLX T2: A = S:I1:I2:imm10:imm11:0 // I1 = NOT(J1 EOR S), I2 = NOT(J2 EOR S) - uint16_t Hi = read16le(Buf); - uint16_t Lo = read16le(Buf + 2); - return SignExtend64<24>(((Hi & 0x0400) << 14) | // S - (~((Lo ^ (Hi << 3)) << 10) & 0x00800000) | // I1 - (~((Lo ^ (Hi << 1)) << 11) & 0x00400000) | // I2 - ((Hi & 0x003ff) << 12) | // imm0 - ((Lo & 0x007ff) << 1)); // imm11:0 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<24>(((hi & 0x0400) << 14) | // S + (~((lo ^ (hi << 3)) << 10) & 0x00800000) | // I1 + (~((lo ^ (hi << 1)) << 11) & 0x00400000) | // I2 + ((hi & 0x003ff) << 12) | // imm0 + ((lo & 0x007ff) << 1)); // imm11:0 } // ELF for the ARM Architecture 4.6.1.1 the implicit addend for MOVW and // MOVT is in the range -32768 <= A < 32768 @@ -588,25 +582,25 @@ int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const { case R_ARM_MOVT_ABS: case R_ARM_MOVW_PREL_NC: case R_ARM_MOVT_PREL: { - uint64_t Val = read32le(Buf) & 0x000f0fff; - return SignExtend64<16>(((Val & 0x000f0000) >> 4) | (Val & 0x00fff)); + uint64_t val = read32le(buf) & 0x000f0fff; + return SignExtend64<16>(((val & 0x000f0000) >> 4) | (val & 0x00fff)); } case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: { // Encoding T3: A = imm4:i:imm3:imm8 - uint16_t Hi = read16le(Buf); - uint16_t Lo = read16le(Buf + 2); - return SignExtend64<16>(((Hi & 0x000f) << 12) | // imm4 - ((Hi & 0x0400) << 1) | // i - ((Lo & 0x7000) >> 4) | // imm3 - (Lo & 0x00ff)); // imm8 + uint16_t hi = read16le(buf); + uint16_t lo = read16le(buf + 2); + return SignExtend64<16>(((hi & 0x000f) << 12) | // imm4 + ((hi & 0x0400) << 1) | // i + ((lo & 0x7000) >> 4) | // imm3 + (lo & 0x00ff)); // imm8 } } } TargetInfo *elf::getARMTargetInfo() { - static ARM Target; - return &Target; + static ARM target; + return ⌖ } diff --git a/ELF/Arch/AVR.cpp b/ELF/Arch/AVR.cpp index 637da37..869f0fe 100644 --- a/ELF/Arch/AVR.cpp +++ b/ELF/Arch/AVR.cpp @@ -1,9 +1,8 @@ //===- AVR.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 // //===----------------------------------------------------------------------===// // @@ -44,34 +43,34 @@ namespace { class AVR final : public TargetInfo { public: AVR(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace -AVR::AVR() { NoneRel = R_AVR_NONE; } +AVR::AVR() { noneRel = R_AVR_NONE; } -RelExpr AVR::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { +RelExpr AVR::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { return R_ABS; } -void AVR::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_AVR_CALL: { - uint16_t Hi = Val >> 17; - uint16_t Lo = Val >> 1; - write16le(Loc, read16le(Loc) | ((Hi >> 1) << 4) | (Hi & 1)); - write16le(Loc + 2, Lo); + uint16_t hi = val >> 17; + uint16_t lo = val >> 1; + write16le(loc, read16le(loc) | ((hi >> 1) << 4) | (hi & 1)); + write16le(loc + 2, lo); break; } default: - error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } TargetInfo *elf::getAVRTargetInfo() { - static AVR Target; - return &Target; + static AVR target; + return ⌖ } diff --git a/ELF/Arch/Hexagon.cpp b/ELF/Arch/Hexagon.cpp index b4d33be..c497a6d 100644 --- a/ELF/Arch/Hexagon.cpp +++ b/ELF/Arch/Hexagon.cpp @@ -1,9 +1,8 @@ //===-- Hexagon.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 // //===----------------------------------------------------------------------===// @@ -28,65 +27,65 @@ class Hexagon final : public TargetInfo { public: Hexagon(); uint32_t calcEFlags() const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; } // namespace Hexagon::Hexagon() { - PltRel = R_HEX_JMP_SLOT; - RelativeRel = R_HEX_RELATIVE; - GotRel = R_HEX_GLOB_DAT; - GotEntrySize = 4; + pltRel = R_HEX_JMP_SLOT; + relativeRel = R_HEX_RELATIVE; + gotRel = R_HEX_GLOB_DAT; + symbolicRel = R_HEX_32; + // The zero'th GOT entry is reserved for the address of _DYNAMIC. The // next 3 are reserved for the dynamic loader. - GotPltHeaderEntriesNum = 4; - GotPltEntrySize = 4; + gotPltHeaderEntriesNum = 4; - PltEntrySize = 16; - PltHeaderSize = 32; + pltEntrySize = 16; + pltHeaderSize = 32; // Hexagon Linux uses 64K pages by default. - DefaultMaxPageSize = 0x10000; - NoneRel = R_HEX_NONE; + defaultMaxPageSize = 0x10000; + noneRel = R_HEX_NONE; } uint32_t Hexagon::calcEFlags() const { - assert(!ObjectFiles.empty()); + assert(!objectFiles.empty()); // The architecture revision must always be equal to or greater than // greatest revision in the list of inputs. - uint32_t Ret = 0; - for (InputFile *F : ObjectFiles) { - uint32_t EFlags = cast>(F)->getObj().getHeader()->e_flags; - if (EFlags > Ret) - Ret = EFlags; + uint32_t ret = 0; + for (InputFile *f : objectFiles) { + uint32_t eflags = cast>(f)->getObj().getHeader()->e_flags; + if (eflags > ret) + ret = eflags; } - return Ret; + return ret; } -static uint32_t applyMask(uint32_t Mask, uint32_t Data) { - uint32_t Result = 0; - size_t Off = 0; +static uint32_t applyMask(uint32_t mask, uint32_t data) { + uint32_t result = 0; + size_t off = 0; - for (size_t Bit = 0; Bit != 32; ++Bit) { - uint32_t ValBit = (Data >> Off) & 1; - uint32_t MaskBit = (Mask >> Bit) & 1; - if (MaskBit) { - Result |= (ValBit << Bit); - ++Off; + for (size_t bit = 0; bit != 32; ++bit) { + uint32_t valBit = (data >> off) & 1; + uint32_t maskBit = (mask >> bit) & 1; + if (maskBit) { + result |= (valBit << bit); + ++off; } } - return Result; + return result; } -RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_HEX_B9_PCREL: case R_HEX_B9_PCREL_X: case R_HEX_B13_PCREL: @@ -109,16 +108,16 @@ RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S, } } -static uint32_t findMaskR6(uint32_t Insn) { +static uint32_t findMaskR6(uint32_t insn) { // There are (arguably too) many relocation masks for the DSP's // R_HEX_6_X type. The table below is used to select the correct mask // for the given instruction. struct InstructionMask { - uint32_t CmpMask; - uint32_t RelocMask; + uint32_t cmpMask; + uint32_t relocMask; }; - static const InstructionMask R6[] = { + static const InstructionMask r6[] = { {0x38000000, 0x0000201f}, {0x39000000, 0x0000201f}, {0x3e000000, 0x00001f80}, {0x3f000000, 0x00001f80}, {0x40000000, 0x000020f8}, {0x41000000, 0x000007e0}, @@ -136,124 +135,124 @@ static uint32_t findMaskR6(uint32_t Insn) { // Duplex forms have a fixed mask and parse bits 15:14 are always // zero. Non-duplex insns will always have at least one bit set in the // parse field. - if ((0xC000 & Insn) == 0x0) + if ((0xC000 & insn) == 0x0) return 0x03f00000; - for (InstructionMask I : R6) - if ((0xff000000 & Insn) == I.CmpMask) - return I.RelocMask; + for (InstructionMask i : r6) + if ((0xff000000 & insn) == i.cmpMask) + return i.relocMask; error("unrecognized instruction for R_HEX_6 relocation: 0x" + - utohexstr(Insn)); + utohexstr(insn)); return 0; } -static uint32_t findMaskR8(uint32_t Insn) { - if ((0xff000000 & Insn) == 0xde000000) +static uint32_t findMaskR8(uint32_t insn) { + if ((0xff000000 & insn) == 0xde000000) return 0x00e020e8; - if ((0xff000000 & Insn) == 0x3c000000) + if ((0xff000000 & insn) == 0x3c000000) return 0x0000207f; return 0x00001fe0; } -static uint32_t findMaskR11(uint32_t Insn) { - if ((0xff000000 & Insn) == 0xa1000000) +static uint32_t findMaskR11(uint32_t insn) { + if ((0xff000000 & insn) == 0xa1000000) return 0x060020ff; return 0x06003fe0; } -static uint32_t findMaskR16(uint32_t Insn) { - if ((0xff000000 & Insn) == 0x48000000) +static uint32_t findMaskR16(uint32_t insn) { + if ((0xff000000 & insn) == 0x48000000) return 0x061f20ff; - if ((0xff000000 & Insn) == 0x49000000) + if ((0xff000000 & insn) == 0x49000000) return 0x061f3fe0; - if ((0xff000000 & Insn) == 0x78000000) + if ((0xff000000 & insn) == 0x78000000) return 0x00df3fe0; - if ((0xff000000 & Insn) == 0xb0000000) + if ((0xff000000 & insn) == 0xb0000000) return 0x0fe03fe0; error("unrecognized instruction for R_HEX_16_X relocation: 0x" + - utohexstr(Insn)); + utohexstr(insn)); return 0; } -static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); } +static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); } -void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_HEX_NONE: break; case R_HEX_6_PCREL_X: case R_HEX_6_X: - or32le(Loc, applyMask(findMaskR6(read32le(Loc)), Val)); + or32le(loc, applyMask(findMaskR6(read32le(loc)), val)); break; case R_HEX_8_X: - or32le(Loc, applyMask(findMaskR8(read32le(Loc)), Val)); + or32le(loc, applyMask(findMaskR8(read32le(loc)), val)); break; case R_HEX_9_X: - or32le(Loc, applyMask(0x00003fe0, Val & 0x3f)); + or32le(loc, applyMask(0x00003fe0, val & 0x3f)); break; case R_HEX_10_X: - or32le(Loc, applyMask(0x00203fe0, Val & 0x3f)); + or32le(loc, applyMask(0x00203fe0, val & 0x3f)); break; case R_HEX_11_X: case R_HEX_GOT_11_X: - or32le(Loc, applyMask(findMaskR11(read32le(Loc)), Val & 0x3f)); + or32le(loc, applyMask(findMaskR11(read32le(loc)), val & 0x3f)); break; case R_HEX_12_X: - or32le(Loc, applyMask(0x000007e0, Val)); + or32le(loc, applyMask(0x000007e0, val)); break; case R_HEX_16_X: // These relocs only have 6 effective bits. case R_HEX_GOT_16_X: - or32le(Loc, applyMask(findMaskR16(read32le(Loc)), Val & 0x3f)); + or32le(loc, applyMask(findMaskR16(read32le(loc)), val & 0x3f)); break; case R_HEX_32: case R_HEX_32_PCREL: - or32le(Loc, Val); + or32le(loc, val); break; case R_HEX_32_6_X: case R_HEX_GOT_32_6_X: - or32le(Loc, applyMask(0x0fff3fff, Val >> 6)); + or32le(loc, applyMask(0x0fff3fff, val >> 6)); break; case R_HEX_B9_PCREL: - or32le(Loc, applyMask(0x003000fe, Val >> 2)); + or32le(loc, applyMask(0x003000fe, val >> 2)); break; case R_HEX_B9_PCREL_X: - or32le(Loc, applyMask(0x003000fe, Val & 0x3f)); + or32le(loc, applyMask(0x003000fe, val & 0x3f)); break; case R_HEX_B13_PCREL: - or32le(Loc, applyMask(0x00202ffe, Val >> 2)); + or32le(loc, applyMask(0x00202ffe, val >> 2)); break; case R_HEX_B15_PCREL: - or32le(Loc, applyMask(0x00df20fe, Val >> 2)); + or32le(loc, applyMask(0x00df20fe, val >> 2)); break; case R_HEX_B15_PCREL_X: - or32le(Loc, applyMask(0x00df20fe, Val & 0x3f)); + or32le(loc, applyMask(0x00df20fe, val & 0x3f)); break; case R_HEX_B22_PCREL: case R_HEX_PLT_B22_PCREL: - or32le(Loc, applyMask(0x1ff3ffe, Val >> 2)); + or32le(loc, applyMask(0x1ff3ffe, val >> 2)); break; case R_HEX_B22_PCREL_X: - or32le(Loc, applyMask(0x1ff3ffe, Val & 0x3f)); + or32le(loc, applyMask(0x1ff3ffe, val & 0x3f)); break; case R_HEX_B32_PCREL_X: - or32le(Loc, applyMask(0x0fff3fff, Val >> 6)); + or32le(loc, applyMask(0x0fff3fff, val >> 6)); break; case R_HEX_HI16: - or32le(Loc, applyMask(0x00c03fff, Val >> 16)); + or32le(loc, applyMask(0x00c03fff, val >> 16)); break; case R_HEX_LO16: - or32le(Loc, applyMask(0x00c03fff, Val)); + or32le(loc, applyMask(0x00c03fff, val)); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); break; } } -void Hexagon::writePltHeader(uint8_t *Buf) const { - const uint8_t PltData[] = { +void Hexagon::writePltHeader(uint8_t *buf) const { + const uint8_t pltData[] = { 0x00, 0x40, 0x00, 0x00, // { immext (#0) 0x1c, 0xc0, 0x49, 0x6a, // r28 = add (pc, ##GOT0@PCREL) } # @GOT0 0x0e, 0x42, 0x9c, 0xe2, // { r14 -= add (r28, #16) # offset of GOTn @@ -263,30 +262,30 @@ void Hexagon::writePltHeader(uint8_t *Buf) const { 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 } # call dynamic linker 0x0c, 0xdb, 0x00, 0x54, // trap0(#0xdb) # bring plt0 into 16byte alignment }; - memcpy(Buf, PltData, sizeof(PltData)); + memcpy(buf, pltData, sizeof(pltData)); // Offset from PLT0 to the GOT. - uint64_t Off = In.GotPlt->getVA() - In.Plt->getVA(); - relocateOne(Buf, R_HEX_B32_PCREL_X, Off); - relocateOne(Buf + 4, R_HEX_6_PCREL_X, Off); + uint64_t off = in.gotPlt->getVA() - in.plt->getVA(); + relocateOne(buf, R_HEX_B32_PCREL_X, off); + relocateOne(buf + 4, R_HEX_6_PCREL_X, off); } -void Hexagon::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Inst[] = { +void Hexagon::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t inst[] = { 0x00, 0x40, 0x00, 0x00, // { immext (#0) 0x0e, 0xc0, 0x49, 0x6a, // r14 = add (pc, ##GOTn@PCREL) } 0x1c, 0xc0, 0x8e, 0x91, // r28 = memw (r14) 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 }; - memcpy(Buf, Inst, sizeof(Inst)); + memcpy(buf, inst, sizeof(inst)); - relocateOne(Buf, R_HEX_B32_PCREL_X, GotPltEntryAddr - PltEntryAddr); - relocateOne(Buf + 4, R_HEX_6_PCREL_X, GotPltEntryAddr - PltEntryAddr); + relocateOne(buf, R_HEX_B32_PCREL_X, gotPltEntryAddr - pltEntryAddr); + relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr); } TargetInfo *elf::getHexagonTargetInfo() { - static Hexagon Target; - return &Target; + static Hexagon target; + return ⌖ } diff --git a/ELF/Arch/MSP430.cpp b/ELF/Arch/MSP430.cpp index fe0c0fe..9066439 100644 --- a/ELF/Arch/MSP430.cpp +++ b/ELF/Arch/MSP430.cpp @@ -1,9 +1,8 @@ //===- MSP430.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 // //===----------------------------------------------------------------------===// // @@ -34,20 +33,20 @@ namespace { class MSP430 final : public TargetInfo { public: MSP430(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace MSP430::MSP430() { // mov.b #0, r3 - TrapInstr = {0x43, 0x43, 0x43, 0x43}; + trapInstr = {0x43, 0x43, 0x43, 0x43}; } -RelExpr MSP430::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr MSP430::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_MSP430_10_PCREL: case R_MSP430_16_PCREL: case R_MSP430_16_PCREL_BYTE: @@ -60,35 +59,35 @@ RelExpr MSP430::getRelExpr(RelType Type, const Symbol &S, } } -void MSP430::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void MSP430::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_MSP430_8: - checkIntUInt(Loc, Val, 8, Type); - *Loc = Val; + checkIntUInt(loc, val, 8, type); + *loc = val; break; case R_MSP430_16: case R_MSP430_16_PCREL: case R_MSP430_16_BYTE: case R_MSP430_16_PCREL_BYTE: - checkIntUInt(Loc, Val, 16, Type); - write16le(Loc, Val); + checkIntUInt(loc, val, 16, type); + write16le(loc, val); break; case R_MSP430_32: - checkIntUInt(Loc, Val, 32, Type); - write32le(Loc, Val); + checkIntUInt(loc, val, 32, type); + write32le(loc, val); break; case R_MSP430_10_PCREL: { - int16_t Offset = ((int16_t)Val >> 1) - 1; - checkInt(Loc, Offset, 10, Type); - write16le(Loc, (read16le(Loc) & 0xFC00) | (Offset & 0x3FF)); + int16_t offset = ((int16_t)val >> 1) - 1; + checkInt(loc, offset, 10, type); + write16le(loc, (read16le(loc) & 0xFC00) | (offset & 0x3FF)); break; } default: - error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } TargetInfo *elf::getMSP430TargetInfo() { - static MSP430 Target; - return &Target; + static MSP430 target; + return ⌖ } diff --git a/ELF/Arch/Mips.cpp b/ELF/Arch/Mips.cpp index 23b0c1d..24b3957 100644 --- a/ELF/Arch/Mips.cpp +++ b/ELF/Arch/Mips.cpp @@ -1,9 +1,8 @@ //===- MIPS.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 // //===----------------------------------------------------------------------===// @@ -29,47 +28,47 @@ template class MIPS final : public TargetInfo { public: MIPS(); uint32_t calcEFlags() const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override; - RelType getDynRel(RelType Type) const override; - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - bool usesOnlyLowPageBits(RelType Type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; + RelType getDynRel(RelType type) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + bool usesOnlyLowPageBits(RelType type) const override; }; } // namespace template MIPS::MIPS() { - GotPltHeaderEntriesNum = 2; - DefaultMaxPageSize = 65536; - GotEntrySize = sizeof(typename ELFT::uint); - GotPltEntrySize = sizeof(typename ELFT::uint); - GotBaseSymInGotPlt = false; - PltEntrySize = 16; - PltHeaderSize = 32; - CopyRel = R_MIPS_COPY; - NoneRel = R_MIPS_NONE; - PltRel = R_MIPS_JUMP_SLOT; - NeedsThunks = true; + gotPltHeaderEntriesNum = 2; + defaultMaxPageSize = 65536; + gotBaseSymInGotPlt = false; + pltEntrySize = 16; + pltHeaderSize = 32; + copyRel = R_MIPS_COPY; + noneRel = R_MIPS_NONE; + pltRel = R_MIPS_JUMP_SLOT; + needsThunks = true; // Set `sigrie 1` as a trap instruction. - write32(TrapInstr.data(), 0x04170001); + write32(trapInstr.data(), 0x04170001); if (ELFT::Is64Bits) { - RelativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32; - TlsGotRel = R_MIPS_TLS_TPREL64; - TlsModuleIndexRel = R_MIPS_TLS_DTPMOD64; - TlsOffsetRel = R_MIPS_TLS_DTPREL64; + relativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32; + symbolicRel = R_MIPS_64; + tlsGotRel = R_MIPS_TLS_TPREL64; + tlsModuleIndexRel = R_MIPS_TLS_DTPMOD64; + tlsOffsetRel = R_MIPS_TLS_DTPREL64; } else { - RelativeRel = R_MIPS_REL32; - TlsGotRel = R_MIPS_TLS_TPREL32; - TlsModuleIndexRel = R_MIPS_TLS_DTPMOD32; - TlsOffsetRel = R_MIPS_TLS_DTPREL32; + relativeRel = R_MIPS_REL32; + symbolicRel = R_MIPS_32; + tlsGotRel = R_MIPS_TLS_TPREL32; + tlsModuleIndexRel = R_MIPS_TLS_DTPMOD32; + tlsOffsetRel = R_MIPS_TLS_DTPREL32; } } @@ -78,13 +77,13 @@ template uint32_t MIPS::calcEFlags() const { } template -RelExpr MIPS::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { +RelExpr MIPS::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { // See comment in the calculateMipsRelChain. - if (ELFT::Is64Bits || Config->MipsN32Abi) - Type &= 0xff; + if (ELFT::Is64Bits || config->mipsN32Abi) + type &= 0xff; - switch (Type) { + switch (type) { case R_MIPS_JALR: case R_MICROMIPS_JALR: return R_HINT; @@ -108,9 +107,9 @@ RelExpr MIPS::getRelExpr(RelType Type, const Symbol &S, // offset between start of function and 'gp' value which by default // equal to the start of .got section. In that case we consider these // relocations as relative. - if (&S == ElfSym::MipsGpDisp) + if (&s == ElfSym::mipsGpDisp) return R_MIPS_GOT_GP_PC; - if (&S == ElfSym::MipsLocalGp) + if (&s == ElfSym::mipsLocalGp) return R_MIPS_GOT_GP; LLVM_FALLTHROUGH; case R_MIPS_32: @@ -147,7 +146,7 @@ RelExpr MIPS::getRelExpr(RelType Type, const Symbol &S, return R_PC; case R_MIPS_GOT16: case R_MICROMIPS_GOT16: - if (S.isLocal()) + if (s.isLocal()) return R_MIPS_GOT_LOCAL_PAGE; LLVM_FALLTHROUGH; case R_MIPS_CALL16: @@ -176,209 +175,213 @@ RelExpr MIPS::getRelExpr(RelType Type, const Symbol &S, case R_MIPS_NONE: return R_NONE; default: - return R_INVALID; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } -template RelType MIPS::getDynRel(RelType Type) const { - if (Type == R_MIPS_32 || Type == R_MIPS_64) - return RelativeRel; +template RelType MIPS::getDynRel(RelType type) const { + if (type == symbolicRel) + return type; return R_MIPS_NONE; } template -void MIPS::writeGotPlt(uint8_t *Buf, const Symbol &) const { - uint64_t VA = In.Plt->getVA(); +void MIPS::writeGotPlt(uint8_t *buf, const Symbol &) const { + uint64_t va = in.plt->getVA(); if (isMicroMips()) - VA |= 1; - write32(Buf, VA); + va |= 1; + write32(buf, va); } -template static uint32_t readShuffle(const uint8_t *Loc) { +template static uint32_t readShuffle(const uint8_t *loc) { // The major opcode of a microMIPS instruction needs to appear // in the first 16-bit word (lowest address) for efficient hardware // decode so that it knows if the instruction is 16-bit or 32-bit // as early as possible. To do so, little-endian binaries keep 16-bit // words in a big-endian order. That is why we have to swap these // words to get a correct value. - uint32_t V = read32(Loc); + uint32_t v = read32(loc); if (E == support::little) - return (V << 16) | (V >> 16); - return V; + return (v << 16) | (v >> 16); + return v; } template -static void writeValue(uint8_t *Loc, uint64_t V, uint8_t BitsSize, - uint8_t Shift) { - uint32_t Instr = read32(Loc); - uint32_t Mask = 0xffffffff >> (32 - BitsSize); - uint32_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask); - write32(Loc, Data); +static void writeValue(uint8_t *loc, uint64_t v, uint8_t bitsSize, + uint8_t shift) { + uint32_t instr = read32(loc); + uint32_t mask = 0xffffffff >> (32 - bitsSize); + uint32_t data = (instr & ~mask) | ((v >> shift) & mask); + write32(loc, data); } template -static void writeShuffleValue(uint8_t *Loc, uint64_t V, uint8_t BitsSize, - uint8_t Shift) { +static void writeShuffleValue(uint8_t *loc, uint64_t v, uint8_t bitsSize, + uint8_t shift) { // See comments in readShuffle for purpose of this code. - uint16_t *Words = (uint16_t *)Loc; + uint16_t *words = (uint16_t *)loc; if (E == support::little) - std::swap(Words[0], Words[1]); + std::swap(words[0], words[1]); - writeValue(Loc, V, BitsSize, Shift); + writeValue(loc, v, bitsSize, shift); if (E == support::little) - std::swap(Words[0], Words[1]); + std::swap(words[0], words[1]); } template -static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize, - uint8_t Shift) { - uint16_t Instr = read16(Loc); - uint16_t Mask = 0xffff >> (16 - BitsSize); - uint16_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask); - write16(Loc, Data); +static void writeMicroRelocation16(uint8_t *loc, uint64_t v, uint8_t bitsSize, + uint8_t shift) { + uint16_t instr = read16(loc); + uint16_t mask = 0xffff >> (16 - bitsSize); + uint16_t data = (instr & ~mask) | ((v >> shift) & mask); + write16(loc, data); } -template void MIPS::writePltHeader(uint8_t *Buf) const { - const endianness E = ELFT::TargetEndianness; +template void MIPS::writePltHeader(uint8_t *buf) const { + const endianness e = ELFT::TargetEndianness; if (isMicroMips()) { - uint64_t GotPlt = In.GotPlt->getVA(); - uint64_t Plt = In.Plt->getVA(); + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); // Overwrite trap instructions written by Writer::writeTrapInstr. - memset(Buf, 0, PltHeaderSize); - - write16(Buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - . - write16(Buf + 4, 0xff23); // lw $25, 0($3) - write16(Buf + 8, 0x0535); // subu16 $2, $2, $3 - write16(Buf + 10, 0x2525); // srl16 $2, $2, 2 - write16(Buf + 12, 0x3302); // addiu $24, $2, -2 - write16(Buf + 14, 0xfffe); - write16(Buf + 16, 0x0dff); // move $15, $31 + memset(buf, 0, pltHeaderSize); + + write16(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - . + write16(buf + 4, 0xff23); // lw $25, 0($3) + write16(buf + 8, 0x0535); // subu16 $2, $2, $3 + write16(buf + 10, 0x2525); // srl16 $2, $2, 2 + write16(buf + 12, 0x3302); // addiu $24, $2, -2 + write16(buf + 14, 0xfffe); + write16(buf + 16, 0x0dff); // move $15, $31 if (isMipsR6()) { - write16(Buf + 18, 0x0f83); // move $28, $3 - write16(Buf + 20, 0x472b); // jalrc $25 - write16(Buf + 22, 0x0c00); // nop - relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPlt - Plt); + write16(buf + 18, 0x0f83); // move $28, $3 + write16(buf + 20, 0x472b); // jalrc $25 + write16(buf + 22, 0x0c00); // nop + relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt); } else { - write16(Buf + 18, 0x45f9); // jalrc $25 - write16(Buf + 20, 0x0f83); // move $28, $3 - write16(Buf + 22, 0x0c00); // nop - relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPlt - Plt); + write16(buf + 18, 0x45f9); // jalrc $25 + write16(buf + 20, 0x0f83); // move $28, $3 + write16(buf + 22, 0x0c00); // nop + relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt); } return; } - if (Config->MipsN32Abi) { - write32(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) - write32(Buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14) - write32(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) - write32(Buf + 12, 0x030ec023); // subu $24, $24, $14 - write32(Buf + 16, 0x03e07825); // move $15, $31 - write32(Buf + 20, 0x0018c082); // srl $24, $24, 2 + if (config->mipsN32Abi) { + write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14) + write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32(buf + 12, 0x030ec023); // subu $24, $24, $14 + write32(buf + 16, 0x03e07825); // move $15, $31 + write32(buf + 20, 0x0018c082); // srl $24, $24, 2 } else if (ELFT::Is64Bits) { - write32(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) - write32(Buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14) - write32(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) - write32(Buf + 12, 0x030ec023); // subu $24, $24, $14 - write32(Buf + 16, 0x03e07825); // move $15, $31 - write32(Buf + 20, 0x0018c0c2); // srl $24, $24, 3 + write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14) + write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32(buf + 12, 0x030ec023); // subu $24, $24, $14 + write32(buf + 16, 0x03e07825); // move $15, $31 + write32(buf + 20, 0x0018c0c2); // srl $24, $24, 3 } else { - write32(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) - write32(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) - write32(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) - write32(Buf + 12, 0x031cc023); // subu $24, $24, $28 - write32(Buf + 16, 0x03e07825); // move $15, $31 - write32(Buf + 20, 0x0018c082); // srl $24, $24, 2 + write32(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) + write32(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) + write32(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) + write32(buf + 12, 0x031cc023); // subu $24, $24, $28 + write32(buf + 16, 0x03e07825); // move $15, $31 + write32(buf + 20, 0x0018c082); // srl $24, $24, 2 } - uint32_t JalrInst = Config->ZHazardplt ? 0x0320fc09 : 0x0320f809; - write32(Buf + 24, JalrInst); // jalr.hb $25 or jalr $25 - write32(Buf + 28, 0x2718fffe); // subu $24, $24, 2 + uint32_t jalrInst = config->zHazardplt ? 0x0320fc09 : 0x0320f809; + write32(buf + 24, jalrInst); // jalr.hb $25 or jalr $25 + write32(buf + 28, 0x2718fffe); // subu $24, $24, 2 - uint64_t GotPlt = In.GotPlt->getVA(); - writeValue(Buf, GotPlt + 0x8000, 16, 16); - writeValue(Buf + 4, GotPlt, 16, 0); - writeValue(Buf + 8, GotPlt, 16, 0); + uint64_t gotPlt = in.gotPlt->getVA(); + writeValue(buf, gotPlt + 0x8000, 16, 16); + writeValue(buf + 4, gotPlt, 16, 0); + writeValue(buf + 8, gotPlt, 16, 0); } template -void MIPS::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const endianness E = ELFT::TargetEndianness; +void MIPS::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const endianness e = ELFT::TargetEndianness; if (isMicroMips()) { // Overwrite trap instructions written by Writer::writeTrapInstr. - memset(Buf, 0, PltEntrySize); + memset(buf, 0, pltEntrySize); if (isMipsR6()) { - write16(Buf, 0x7840); // addiupc $2, (GOTPLT) - . - write16(Buf + 4, 0xff22); // lw $25, 0($2) - write16(Buf + 8, 0x0f02); // move $24, $2 - write16(Buf + 10, 0x4723); // jrc $25 / jr16 $25 - relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPltEntryAddr - PltEntryAddr); + write16(buf, 0x7840); // addiupc $2, (GOTPLT) - . + write16(buf + 4, 0xff22); // lw $25, 0($2) + write16(buf + 8, 0x0f02); // move $24, $2 + write16(buf + 10, 0x4723); // jrc $25 / jr16 $25 + relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr); } else { - write16(Buf, 0x7900); // addiupc $2, (GOTPLT) - . - write16(Buf + 4, 0xff22); // lw $25, 0($2) - write16(Buf + 8, 0x4599); // jrc $25 / jr16 $25 - write16(Buf + 10, 0x0f02); // move $24, $2 - relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPltEntryAddr - PltEntryAddr); + write16(buf, 0x7900); // addiupc $2, (GOTPLT) - . + write16(buf + 4, 0xff22); // lw $25, 0($2) + write16(buf + 8, 0x4599); // jrc $25 / jr16 $25 + write16(buf + 10, 0x0f02); // move $24, $2 + relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr); } return; } - uint32_t JrInst = isMipsR6() ? (Config->ZHazardplt ? 0x03200409 : 0x03200009) - : (Config->ZHazardplt ? 0x03200408 : 0x03200008); - - write32(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry) - write32(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15) - write32(Buf + 8, JrInst); // jr $25 / jr.hb $25 - write32(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry) - writeValue(Buf, GotPltEntryAddr + 0x8000, 16, 16); - writeValue(Buf + 4, GotPltEntryAddr, 16, 0); - writeValue(Buf + 12, GotPltEntryAddr, 16, 0); + uint32_t loadInst = ELFT::Is64Bits ? 0xddf90000 : 0x8df90000; + uint32_t jrInst = isMipsR6() ? (config->zHazardplt ? 0x03200409 : 0x03200009) + : (config->zHazardplt ? 0x03200408 : 0x03200008); + uint32_t addInst = ELFT::Is64Bits ? 0x65f80000 : 0x25f80000; + + write32(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry) + write32(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15) + write32(buf + 8, jrInst); // jr $25 / jr.hb $25 + write32(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry) + writeValue(buf, gotPltEntryAddr + 0x8000, 16, 16); + writeValue(buf + 4, gotPltEntryAddr, 16, 0); + writeValue(buf + 12, gotPltEntryAddr, 16, 0); } template -bool MIPS::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const { +bool MIPS::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { // Any MIPS PIC code function is invoked with its address in register $t9. // So if we have a branch instruction from non-PIC code to the PIC one // we cannot make the jump directly and need to create a small stubs // to save the target function address. // See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf - if (Type != R_MIPS_26 && Type != R_MICROMIPS_26_S1 && - Type != R_MICROMIPS_PC26_S1) + if (type != R_MIPS_26 && type != R_MIPS_PC26_S2 && + type != R_MICROMIPS_26_S1 && type != R_MICROMIPS_PC26_S1) return false; - auto *F = dyn_cast_or_null>(File); - if (!F) + auto *f = dyn_cast_or_null>(file); + if (!f) return false; // If current file has PIC code, LA25 stub is not required. - if (F->getObj().getHeader()->e_flags & EF_MIPS_PIC) + if (f->getObj().getHeader()->e_flags & EF_MIPS_PIC) return false; - auto *D = dyn_cast(&S); + auto *d = dyn_cast(&s); // LA25 is required if target file has PIC code // or target symbol is a PIC symbol. - return D && isMipsPIC(D); + return d && isMipsPIC(d); } template -int64_t MIPS::getImplicitAddend(const uint8_t *Buf, RelType Type) const { - const endianness E = ELFT::TargetEndianness; - switch (Type) { +int64_t MIPS::getImplicitAddend(const uint8_t *buf, RelType type) const { + const endianness e = ELFT::TargetEndianness; + switch (type) { case R_MIPS_32: case R_MIPS_GPREL32: case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_TPREL32: - return SignExtend64<32>(read32(Buf)); + return SignExtend64<32>(read32(buf)); case R_MIPS_26: // FIXME (simon): If the relocation target symbol is not a PLT entry // we should use another expression for calculation: // ((A << 2) | (P & 0xf0000000)) >> 2 - return SignExtend64<28>(read32(Buf) << 2); + return SignExtend64<28>(read32(buf) << 2); case R_MIPS_GOT16: case R_MIPS_HI16: case R_MIPS_PCHI16: - return SignExtend64<16>(read32(Buf)) << 16; + return SignExtend64<16>(read32(buf)) << 16; case R_MIPS_GPREL16: case R_MIPS_LO16: case R_MIPS_PCLO16: @@ -386,54 +389,54 @@ int64_t MIPS::getImplicitAddend(const uint8_t *Buf, RelType Type) const { case R_MIPS_TLS_DTPREL_LO16: case R_MIPS_TLS_TPREL_HI16: case R_MIPS_TLS_TPREL_LO16: - return SignExtend64<16>(read32(Buf)); + return SignExtend64<16>(read32(buf)); case R_MICROMIPS_GOT16: case R_MICROMIPS_HI16: - return SignExtend64<16>(readShuffle(Buf)) << 16; + return SignExtend64<16>(readShuffle(buf)) << 16; case R_MICROMIPS_GPREL16: case R_MICROMIPS_LO16: case R_MICROMIPS_TLS_DTPREL_HI16: case R_MICROMIPS_TLS_DTPREL_LO16: case R_MICROMIPS_TLS_TPREL_HI16: case R_MICROMIPS_TLS_TPREL_LO16: - return SignExtend64<16>(readShuffle(Buf)); + return SignExtend64<16>(readShuffle(buf)); case R_MICROMIPS_GPREL7_S2: - return SignExtend64<9>(readShuffle(Buf) << 2); + return SignExtend64<9>(readShuffle(buf) << 2); case R_MIPS_PC16: - return SignExtend64<18>(read32(Buf) << 2); + return SignExtend64<18>(read32(buf) << 2); case R_MIPS_PC19_S2: - return SignExtend64<21>(read32(Buf) << 2); + return SignExtend64<21>(read32(buf) << 2); case R_MIPS_PC21_S2: - return SignExtend64<23>(read32(Buf) << 2); + return SignExtend64<23>(read32(buf) << 2); case R_MIPS_PC26_S2: - return SignExtend64<28>(read32(Buf) << 2); + return SignExtend64<28>(read32(buf) << 2); case R_MIPS_PC32: - return SignExtend64<32>(read32(Buf)); + return SignExtend64<32>(read32(buf)); case R_MICROMIPS_26_S1: - return SignExtend64<27>(readShuffle(Buf) << 1); + return SignExtend64<27>(readShuffle(buf) << 1); case R_MICROMIPS_PC7_S1: - return SignExtend64<8>(read16(Buf) << 1); + return SignExtend64<8>(read16(buf) << 1); case R_MICROMIPS_PC10_S1: - return SignExtend64<11>(read16(Buf) << 1); + return SignExtend64<11>(read16(buf) << 1); case R_MICROMIPS_PC16_S1: - return SignExtend64<17>(readShuffle(Buf) << 1); + return SignExtend64<17>(readShuffle(buf) << 1); case R_MICROMIPS_PC18_S3: - return SignExtend64<21>(readShuffle(Buf) << 3); + return SignExtend64<21>(readShuffle(buf) << 3); case R_MICROMIPS_PC19_S2: - return SignExtend64<21>(readShuffle(Buf) << 2); + return SignExtend64<21>(readShuffle(buf) << 2); case R_MICROMIPS_PC21_S1: - return SignExtend64<22>(readShuffle(Buf) << 1); + return SignExtend64<22>(readShuffle(buf) << 1); case R_MICROMIPS_PC23_S2: - return SignExtend64<25>(readShuffle(Buf) << 2); + return SignExtend64<25>(readShuffle(buf) << 2); case R_MICROMIPS_PC26_S1: - return SignExtend64<27>(readShuffle(Buf) << 1); + return SignExtend64<27>(readShuffle(buf) << 1); default: return 0; } } static std::pair -calculateMipsRelChain(uint8_t *Loc, RelType Type, uint64_t Val) { +calculateMipsRelChain(uint8_t *loc, RelType type, uint64_t val) { // MIPS N64 ABI packs multiple relocations into the single relocation // record. In general, all up to three relocations can have arbitrary // types. In fact, Clang and GCC uses only a few combinations. For now, @@ -446,72 +449,134 @@ calculateMipsRelChain(uint8_t *Loc, RelType Type, uint64_t Val) { // relocations used to modify result of the first one: extend it to // 64-bit, extract high or low part etc. For details, see part 2.9 Relocation // at the https://dmz-portal.mips.com/mw/images/8/82/007-4658-001.pdf - RelType Type2 = (Type >> 8) & 0xff; - RelType Type3 = (Type >> 16) & 0xff; - if (Type2 == R_MIPS_NONE && Type3 == R_MIPS_NONE) - return std::make_pair(Type, Val); - if (Type2 == R_MIPS_64 && Type3 == R_MIPS_NONE) - return std::make_pair(Type2, Val); - if (Type2 == R_MIPS_SUB && (Type3 == R_MIPS_HI16 || Type3 == R_MIPS_LO16)) - return std::make_pair(Type3, -Val); - error(getErrorLocation(Loc) + "unsupported relocations combination " + - Twine(Type)); - return std::make_pair(Type & 0xff, Val); + RelType type2 = (type >> 8) & 0xff; + RelType type3 = (type >> 16) & 0xff; + if (type2 == R_MIPS_NONE && type3 == R_MIPS_NONE) + return std::make_pair(type, val); + if (type2 == R_MIPS_64 && type3 == R_MIPS_NONE) + return std::make_pair(type2, val); + if (type2 == R_MIPS_SUB && (type3 == R_MIPS_HI16 || type3 == R_MIPS_LO16)) + return std::make_pair(type3, -val); + error(getErrorLocation(loc) + "unsupported relocations combination " + + Twine(type)); + return std::make_pair(type & 0xff, val); +} + +static bool isBranchReloc(RelType type) { + return type == R_MIPS_26 || type == R_MIPS_PC26_S2 || + type == R_MIPS_PC21_S2 || type == R_MIPS_PC16; +} + +static bool isMicroBranchReloc(RelType type) { + return type == R_MICROMIPS_26_S1 || type == R_MICROMIPS_PC16_S1 || + type == R_MICROMIPS_PC10_S1 || type == R_MICROMIPS_PC7_S1; } template -void MIPS::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - const endianness E = ELFT::TargetEndianness; +static uint64_t fixupCrossModeJump(uint8_t *loc, RelType type, uint64_t val) { + // Here we need to detect jump/branch from regular MIPS code + // to a microMIPS target and vice versa. In that cases jump + // instructions need to be replaced by their "cross-mode" + // equivalents. + const endianness e = ELFT::TargetEndianness; + bool isMicroTgt = val & 0x1; + bool isCrossJump = (isMicroTgt && isBranchReloc(type)) || + (!isMicroTgt && isMicroBranchReloc(type)); + if (!isCrossJump) + return val; + + switch (type) { + case R_MIPS_26: { + uint32_t inst = read32(loc) >> 26; + if (inst == 0x3 || inst == 0x1d) { // JAL or JALX + writeValue(loc, 0x1d << 26, 32, 0); + return val; + } + break; + } + case R_MICROMIPS_26_S1: { + uint32_t inst = readShuffle(loc) >> 26; + if (inst == 0x3d || inst == 0x3c) { // JAL32 or JALX32 + val >>= 1; + writeShuffleValue(loc, 0x3c << 26, 32, 0); + return val; + } + break; + } + case R_MIPS_PC26_S2: + case R_MIPS_PC21_S2: + case R_MIPS_PC16: + case R_MICROMIPS_PC16_S1: + case R_MICROMIPS_PC10_S1: + case R_MICROMIPS_PC7_S1: + // FIXME (simon): Support valid branch relocations. + break; + default: + llvm_unreachable("unexpected jump/branch relocation"); + } + + error(getErrorLocation(loc) + + "unsupported jump/branch instruction between ISA modes referenced by " + + toString(type) + " relocation"); + return val; +} + +template +void MIPS::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + const endianness e = ELFT::TargetEndianness; + + if (ELFT::Is64Bits || config->mipsN32Abi) + std::tie(type, val) = calculateMipsRelChain(loc, type, val); - if (ELFT::Is64Bits || Config->MipsN32Abi) - std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val); + // Detect cross-mode jump/branch and fix instruction. + val = fixupCrossModeJump(loc, type, val); // Thread pointer and DRP offsets from the start of TLS data area. // https://www.linux-mips.org/wiki/NPTL - if (Type == R_MIPS_TLS_DTPREL_HI16 || Type == R_MIPS_TLS_DTPREL_LO16 || - Type == R_MIPS_TLS_DTPREL32 || Type == R_MIPS_TLS_DTPREL64 || - Type == R_MICROMIPS_TLS_DTPREL_HI16 || - Type == R_MICROMIPS_TLS_DTPREL_LO16) { - Val -= 0x8000; - } else if (Type == R_MIPS_TLS_TPREL_HI16 || Type == R_MIPS_TLS_TPREL_LO16 || - Type == R_MIPS_TLS_TPREL32 || Type == R_MIPS_TLS_TPREL64 || - Type == R_MICROMIPS_TLS_TPREL_HI16 || - Type == R_MICROMIPS_TLS_TPREL_LO16) { - Val -= 0x7000; + if (type == R_MIPS_TLS_DTPREL_HI16 || type == R_MIPS_TLS_DTPREL_LO16 || + type == R_MIPS_TLS_DTPREL32 || type == R_MIPS_TLS_DTPREL64 || + type == R_MICROMIPS_TLS_DTPREL_HI16 || + type == R_MICROMIPS_TLS_DTPREL_LO16) { + val -= 0x8000; + } else if (type == R_MIPS_TLS_TPREL_HI16 || type == R_MIPS_TLS_TPREL_LO16 || + type == R_MIPS_TLS_TPREL32 || type == R_MIPS_TLS_TPREL64 || + type == R_MICROMIPS_TLS_TPREL_HI16 || + type == R_MICROMIPS_TLS_TPREL_LO16) { + val -= 0x7000; } - switch (Type) { + switch (type) { case R_MIPS_32: case R_MIPS_GPREL32: case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_TPREL32: - write32(Loc, Val); + write32(loc, val); break; case R_MIPS_64: case R_MIPS_TLS_DTPREL64: case R_MIPS_TLS_TPREL64: - write64(Loc, Val); + write64(loc, val); break; case R_MIPS_26: - writeValue(Loc, Val, 26, 2); + writeValue(loc, val, 26, 2); break; case R_MIPS_GOT16: // The R_MIPS_GOT16 relocation's value in "relocatable" linking mode // is updated addend (not a GOT index). In that case write high 16 bits // to store a correct addend value. - if (Config->Relocatable) { - writeValue(Loc, Val + 0x8000, 16, 16); + if (config->relocatable) { + writeValue(loc, val + 0x8000, 16, 16); } else { - checkInt(Loc, Val, 16, Type); - writeValue(Loc, Val, 16, 0); + checkInt(loc, val, 16, type); + writeValue(loc, val, 16, 0); } break; case R_MICROMIPS_GOT16: - if (Config->Relocatable) { - writeShuffleValue(Loc, Val + 0x8000, 16, 16); + if (config->relocatable) { + writeShuffleValue(loc, val + 0x8000, 16, 16); } else { - checkInt(Loc, Val, 16, Type); - writeShuffleValue(Loc, Val, 16, 0); + checkInt(loc, val, 16, type); + writeShuffleValue(loc, val, 16, 0); } break; case R_MIPS_CALL16: @@ -521,7 +586,7 @@ void MIPS::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_MIPS_TLS_GD: case R_MIPS_TLS_GOTTPREL: case R_MIPS_TLS_LDM: - checkInt(Loc, Val, 16, Type); + checkInt(loc, val, 16, type); LLVM_FALLTHROUGH; case R_MIPS_CALL_LO16: case R_MIPS_GOT_LO16: @@ -530,13 +595,13 @@ void MIPS::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_MIPS_PCLO16: case R_MIPS_TLS_DTPREL_LO16: case R_MIPS_TLS_TPREL_LO16: - writeValue(Loc, Val, 16, 0); + writeValue(loc, val, 16, 0); break; case R_MICROMIPS_GPREL16: case R_MICROMIPS_TLS_GD: case R_MICROMIPS_TLS_LDM: - checkInt(Loc, Val, 16, Type); - writeShuffleValue(Loc, Val, 16, 0); + checkInt(loc, val, 16, type); + writeShuffleValue(loc, val, 16, 0); break; case R_MICROMIPS_CALL16: case R_MICROMIPS_CALL_LO16: @@ -544,11 +609,11 @@ void MIPS::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_MICROMIPS_TLS_DTPREL_LO16: case R_MICROMIPS_TLS_GOTTPREL: case R_MICROMIPS_TLS_TPREL_LO16: - writeShuffleValue(Loc, Val, 16, 0); + writeShuffleValue(loc, val, 16, 0); break; case R_MICROMIPS_GPREL7_S2: - checkInt(Loc, Val, 7, Type); - writeShuffleValue(Loc, Val, 7, 2); + checkInt(loc, val, 7, type); + writeShuffleValue(loc, val, 7, 2); break; case R_MIPS_CALL_HI16: case R_MIPS_GOT_HI16: @@ -556,113 +621,113 @@ void MIPS::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_MIPS_PCHI16: case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_TPREL_HI16: - writeValue(Loc, Val + 0x8000, 16, 16); + writeValue(loc, val + 0x8000, 16, 16); break; case R_MICROMIPS_CALL_HI16: case R_MICROMIPS_GOT_HI16: case R_MICROMIPS_HI16: case R_MICROMIPS_TLS_DTPREL_HI16: case R_MICROMIPS_TLS_TPREL_HI16: - writeShuffleValue(Loc, Val + 0x8000, 16, 16); + writeShuffleValue(loc, val + 0x8000, 16, 16); break; case R_MIPS_HIGHER: - writeValue(Loc, Val + 0x80008000, 16, 32); + writeValue(loc, val + 0x80008000, 16, 32); break; case R_MIPS_HIGHEST: - writeValue(Loc, Val + 0x800080008000, 16, 48); + writeValue(loc, val + 0x800080008000, 16, 48); break; case R_MIPS_JALR: case R_MICROMIPS_JALR: // Ignore this optimization relocation for now break; case R_MIPS_PC16: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 18, Type); - writeValue(Loc, Val, 16, 2); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 18, type); + writeValue(loc, val, 16, 2); break; case R_MIPS_PC19_S2: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 21, Type); - writeValue(Loc, Val, 19, 2); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 21, type); + writeValue(loc, val, 19, 2); break; case R_MIPS_PC21_S2: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 23, Type); - writeValue(Loc, Val, 21, 2); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 23, type); + writeValue(loc, val, 21, 2); break; case R_MIPS_PC26_S2: - checkAlignment(Loc, Val, 4, Type); - checkInt(Loc, Val, 28, Type); - writeValue(Loc, Val, 26, 2); + checkAlignment(loc, val, 4, type); + checkInt(loc, val, 28, type); + writeValue(loc, val, 26, 2); break; case R_MIPS_PC32: - writeValue(Loc, Val, 32, 0); + writeValue(loc, val, 32, 0); break; case R_MICROMIPS_26_S1: case R_MICROMIPS_PC26_S1: - checkInt(Loc, Val, 27, Type); - writeShuffleValue(Loc, Val, 26, 1); + checkInt(loc, val, 27, type); + writeShuffleValue(loc, val, 26, 1); break; case R_MICROMIPS_PC7_S1: - checkInt(Loc, Val, 8, Type); - writeMicroRelocation16(Loc, Val, 7, 1); + checkInt(loc, val, 8, type); + writeMicroRelocation16(loc, val, 7, 1); break; case R_MICROMIPS_PC10_S1: - checkInt(Loc, Val, 11, Type); - writeMicroRelocation16(Loc, Val, 10, 1); + checkInt(loc, val, 11, type); + writeMicroRelocation16(loc, val, 10, 1); break; case R_MICROMIPS_PC16_S1: - checkInt(Loc, Val, 17, Type); - writeShuffleValue(Loc, Val, 16, 1); + checkInt(loc, val, 17, type); + writeShuffleValue(loc, val, 16, 1); break; case R_MICROMIPS_PC18_S3: - checkInt(Loc, Val, 21, Type); - writeShuffleValue(Loc, Val, 18, 3); + checkInt(loc, val, 21, type); + writeShuffleValue(loc, val, 18, 3); break; case R_MICROMIPS_PC19_S2: - checkInt(Loc, Val, 21, Type); - writeShuffleValue(Loc, Val, 19, 2); + checkInt(loc, val, 21, type); + writeShuffleValue(loc, val, 19, 2); break; case R_MICROMIPS_PC21_S1: - checkInt(Loc, Val, 22, Type); - writeShuffleValue(Loc, Val, 21, 1); + checkInt(loc, val, 22, type); + writeShuffleValue(loc, val, 21, 1); break; case R_MICROMIPS_PC23_S2: - checkInt(Loc, Val, 25, Type); - writeShuffleValue(Loc, Val, 23, 2); + checkInt(loc, val, 25, type); + writeShuffleValue(loc, val, 23, 2); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -template bool MIPS::usesOnlyLowPageBits(RelType Type) const { - return Type == R_MIPS_LO16 || Type == R_MIPS_GOT_OFST || - Type == R_MICROMIPS_LO16; +template bool MIPS::usesOnlyLowPageBits(RelType type) const { + return type == R_MIPS_LO16 || type == R_MIPS_GOT_OFST || + type == R_MICROMIPS_LO16; } // Return true if the symbol is a PIC function. -template bool elf::isMipsPIC(const Defined *Sym) { - if (!Sym->isFunc()) +template bool elf::isMipsPIC(const Defined *sym) { + if (!sym->isFunc()) return false; - if (Sym->StOther & STO_MIPS_PIC) + if (sym->stOther & STO_MIPS_PIC) return true; - if (!Sym->Section) + if (!sym->section) return false; - ObjFile *File = - cast(Sym->Section)->template getFile(); - if (!File) + ObjFile *file = + cast(sym->section)->template getFile(); + if (!file) return false; - return File->getObj().getHeader()->e_flags & EF_MIPS_PIC; + return file->getObj().getHeader()->e_flags & EF_MIPS_PIC; } template TargetInfo *elf::getMipsTargetInfo() { - static MIPS Target; - return &Target; + static MIPS target; + return ⌖ } template TargetInfo *elf::getMipsTargetInfo(); diff --git a/ELF/Arch/MipsArchTree.cpp b/ELF/Arch/MipsArchTree.cpp index 98ceac3..f64d037 100644 --- a/ELF/Arch/MipsArchTree.cpp +++ b/ELF/Arch/MipsArchTree.cpp @@ -1,9 +1,8 @@ //===- MipsArchTree.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 // //===---------------------------------------------------------------------===// // @@ -29,18 +28,18 @@ using namespace lld::elf; namespace { struct ArchTreeEdge { - uint32_t Child; - uint32_t Parent; + uint32_t child; + uint32_t parent; }; struct FileFlags { - InputFile *File; - uint32_t Flags; + InputFile *file; + uint32_t flags; }; } // namespace -static StringRef getAbiName(uint32_t Flags) { - switch (Flags) { +static StringRef getAbiName(uint32_t flags) { + switch (flags) { case 0: return "n64"; case EF_MIPS_ABI2: @@ -58,76 +57,76 @@ static StringRef getAbiName(uint32_t Flags) { } } -static StringRef getNanName(bool IsNan2008) { - return IsNan2008 ? "2008" : "legacy"; +static StringRef getNanName(bool isNan2008) { + return isNan2008 ? "2008" : "legacy"; } -static StringRef getFpName(bool IsFp64) { return IsFp64 ? "64" : "32"; } +static StringRef getFpName(bool isFp64) { return isFp64 ? "64" : "32"; } -static void checkFlags(ArrayRef Files) { - assert(!Files.empty() && "expected non-empty file list"); +static void checkFlags(ArrayRef files) { + assert(!files.empty() && "expected non-empty file list"); - uint32_t ABI = Files[0].Flags & (EF_MIPS_ABI | EF_MIPS_ABI2); - bool Nan = Files[0].Flags & EF_MIPS_NAN2008; - bool Fp = Files[0].Flags & EF_MIPS_FP64; + uint32_t abi = files[0].flags & (EF_MIPS_ABI | EF_MIPS_ABI2); + bool nan = files[0].flags & EF_MIPS_NAN2008; + bool fp = files[0].flags & EF_MIPS_FP64; - for (const FileFlags &F : Files) { - if (Config->Is64 && F.Flags & EF_MIPS_MICROMIPS) - error(toString(F.File) + ": microMIPS 64-bit is not supported"); + for (const FileFlags &f : files) { + if (config->is64 && f.flags & EF_MIPS_MICROMIPS) + error(toString(f.file) + ": microMIPS 64-bit is not supported"); - uint32_t ABI2 = F.Flags & (EF_MIPS_ABI | EF_MIPS_ABI2); - if (ABI != ABI2) - error(toString(F.File) + ": ABI '" + getAbiName(ABI2) + - "' is incompatible with target ABI '" + getAbiName(ABI) + "'"); + uint32_t abi2 = f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2); + if (abi != abi2) + error(toString(f.file) + ": ABI '" + getAbiName(abi2) + + "' is incompatible with target ABI '" + getAbiName(abi) + "'"); - bool Nan2 = F.Flags & EF_MIPS_NAN2008; - if (Nan != Nan2) - error(toString(F.File) + ": -mnan=" + getNanName(Nan2) + - " is incompatible with target -mnan=" + getNanName(Nan)); + bool nan2 = f.flags & EF_MIPS_NAN2008; + if (nan != nan2) + error(toString(f.file) + ": -mnan=" + getNanName(nan2) + + " is incompatible with target -mnan=" + getNanName(nan)); - bool Fp2 = F.Flags & EF_MIPS_FP64; - if (Fp != Fp2) - error(toString(F.File) + ": -mfp" + getFpName(Fp2) + - " is incompatible with target -mfp" + getFpName(Fp)); + bool fp2 = f.flags & EF_MIPS_FP64; + if (fp != fp2) + error(toString(f.file) + ": -mfp" + getFpName(fp2) + + " is incompatible with target -mfp" + getFpName(fp)); } } -static uint32_t getMiscFlags(ArrayRef Files) { - uint32_t Ret = 0; - for (const FileFlags &F : Files) - Ret |= F.Flags & +static uint32_t getMiscFlags(ArrayRef files) { + uint32_t ret = 0; + for (const FileFlags &f : files) + ret |= f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2 | EF_MIPS_ARCH_ASE | EF_MIPS_NOREORDER | EF_MIPS_MICROMIPS | EF_MIPS_NAN2008 | EF_MIPS_32BITMODE); - return Ret; + return ret; } -static uint32_t getPicFlags(ArrayRef Files) { +static uint32_t getPicFlags(ArrayRef files) { // Check PIC/non-PIC compatibility. - bool IsPic = Files[0].Flags & (EF_MIPS_PIC | EF_MIPS_CPIC); - for (const FileFlags &F : Files.slice(1)) { - bool IsPic2 = F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC); - if (IsPic && !IsPic2) - warn(toString(F.File) + + bool isPic = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + for (const FileFlags &f : files.slice(1)) { + bool isPic2 = f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + if (isPic && !isPic2) + warn(toString(f.file) + ": linking non-abicalls code with abicalls code " + - toString(Files[0].File)); - if (!IsPic && IsPic2) - warn(toString(F.File) + + toString(files[0].file)); + if (!isPic && isPic2) + warn(toString(f.file) + ": linking abicalls code with non-abicalls code " + - toString(Files[0].File)); + toString(files[0].file)); } // Compute the result PIC/non-PIC flag. - uint32_t Ret = Files[0].Flags & (EF_MIPS_PIC | EF_MIPS_CPIC); - for (const FileFlags &F : Files.slice(1)) - Ret &= F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + uint32_t ret = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + for (const FileFlags &f : files.slice(1)) + ret &= f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC); // PIC code is inherently CPIC and may not set CPIC flag explicitly. - if (Ret & EF_MIPS_PIC) - Ret |= EF_MIPS_CPIC; - return Ret; + if (ret & EF_MIPS_PIC) + ret |= EF_MIPS_CPIC; + return ret; } -static ArchTreeEdge ArchTree[] = { +static ArchTreeEdge archTree[] = { // MIPS32R6 and MIPS64R6 are not compatible with other extensions // MIPS64R2 extensions. {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, EF_MIPS_ARCH_64R2}, @@ -167,25 +166,25 @@ static ArchTreeEdge ArchTree[] = { {EF_MIPS_ARCH_2, EF_MIPS_ARCH_1}, }; -static bool isArchMatched(uint32_t New, uint32_t Res) { - if (New == Res) +static bool isArchMatched(uint32_t New, uint32_t res) { + if (New == res) return true; - if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, Res)) + if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res)) return true; - if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, Res)) + if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res)) return true; - for (const auto &Edge : ArchTree) { - if (Res == Edge.Child) { - Res = Edge.Parent; - if (Res == New) + for (const auto &edge : archTree) { + if (res == edge.child) { + res = edge.parent; + if (res == New) return true; } } return false; } -static StringRef getMachName(uint32_t Flags) { - switch (Flags & EF_MIPS_MACH) { +static StringRef getMachName(uint32_t flags) { + switch (flags & EF_MIPS_MACH) { case EF_MIPS_MACH_NONE: return ""; case EF_MIPS_MACH_3900: @@ -229,8 +228,8 @@ static StringRef getMachName(uint32_t Flags) { } } -static StringRef getArchName(uint32_t Flags) { - switch (Flags & EF_MIPS_ARCH) { +static StringRef getArchName(uint32_t flags) { + switch (flags & EF_MIPS_ARCH) { case EF_MIPS_ARCH_1: return "mips1"; case EF_MIPS_ARCH_2: @@ -258,12 +257,12 @@ static StringRef getArchName(uint32_t Flags) { } } -static std::string getFullArchName(uint32_t Flags) { - StringRef Arch = getArchName(Flags); - StringRef Mach = getMachName(Flags); - if (Mach.empty()) - return Arch.str(); - return (Arch + " (" + Mach + ")").str(); +static std::string getFullArchName(uint32_t flags) { + StringRef arch = getArchName(flags); + StringRef mach = getMachName(flags); + if (mach.empty()) + return arch.str(); + return (arch + " (" + mach + ")").str(); } // There are (arguably too) many MIPS ISAs out there. Their relationships @@ -275,55 +274,55 @@ static std::string getFullArchName(uint32_t Flags) { // Output file gets EF_MIPS_ARCH_2 flag. From the other side mips3 and mips32 // are incompatible because nor mips3 is a parent for misp32, nor mips32 // is a parent for mips3. -static uint32_t getArchFlags(ArrayRef Files) { - uint32_t Ret = Files[0].Flags & (EF_MIPS_ARCH | EF_MIPS_MACH); +static uint32_t getArchFlags(ArrayRef files) { + uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH); - for (const FileFlags &F : Files.slice(1)) { - uint32_t New = F.Flags & (EF_MIPS_ARCH | EF_MIPS_MACH); + for (const FileFlags &f : files.slice(1)) { + uint32_t New = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH); // Check ISA compatibility. - if (isArchMatched(New, Ret)) + if (isArchMatched(New, ret)) continue; - if (!isArchMatched(Ret, New)) { - error("incompatible target ISA:\n>>> " + toString(Files[0].File) + ": " + - getFullArchName(Ret) + "\n>>> " + toString(F.File) + ": " + + if (!isArchMatched(ret, New)) { + error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " + + getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " + getFullArchName(New)); return 0; } - Ret = New; + ret = New; } - return Ret; + return ret; } template uint32_t elf::calcMipsEFlags() { - std::vector V; - for (InputFile *F : ObjectFiles) - V.push_back({F, cast>(F)->getObj().getHeader()->e_flags}); - if (V.empty()) + std::vector v; + for (InputFile *f : objectFiles) + v.push_back({f, cast>(f)->getObj().getHeader()->e_flags}); + if (v.empty()) return 0; - checkFlags(V); - return getMiscFlags(V) | getPicFlags(V) | getArchFlags(V); + checkFlags(v); + return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v); } -static int compareMipsFpAbi(uint8_t FpA, uint8_t FpB) { - if (FpA == FpB) +static int compareMipsFpAbi(uint8_t fpA, uint8_t fpB) { + if (fpA == fpB) return 0; - if (FpB == Mips::Val_GNU_MIPS_ABI_FP_ANY) + if (fpB == Mips::Val_GNU_MIPS_ABI_FP_ANY) return 1; - if (FpB == Mips::Val_GNU_MIPS_ABI_FP_64A && - FpA == Mips::Val_GNU_MIPS_ABI_FP_64) + if (fpB == Mips::Val_GNU_MIPS_ABI_FP_64A && + fpA == Mips::Val_GNU_MIPS_ABI_FP_64) return 1; - if (FpB != Mips::Val_GNU_MIPS_ABI_FP_XX) + if (fpB != Mips::Val_GNU_MIPS_ABI_FP_XX) return -1; - if (FpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE || - FpA == Mips::Val_GNU_MIPS_ABI_FP_64 || - FpA == Mips::Val_GNU_MIPS_ABI_FP_64A) + if (fpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE || + fpA == Mips::Val_GNU_MIPS_ABI_FP_64 || + fpA == Mips::Val_GNU_MIPS_ABI_FP_64A) return 1; return -1; } -static StringRef getMipsFpAbiName(uint8_t FpAbi) { - switch (FpAbi) { +static StringRef getMipsFpAbiName(uint8_t fpAbi) { + switch (fpAbi) { case Mips::Val_GNU_MIPS_ABI_FP_ANY: return "any"; case Mips::Val_GNU_MIPS_ABI_FP_DOUBLE: @@ -345,43 +344,43 @@ static StringRef getMipsFpAbiName(uint8_t FpAbi) { } } -uint8_t elf::getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag, - StringRef FileName) { - if (compareMipsFpAbi(NewFlag, OldFlag) >= 0) - return NewFlag; - if (compareMipsFpAbi(OldFlag, NewFlag) < 0) - error(FileName + ": floating point ABI '" + getMipsFpAbiName(NewFlag) + +uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, + StringRef fileName) { + if (compareMipsFpAbi(newFlag, oldFlag) >= 0) + return newFlag; + if (compareMipsFpAbi(oldFlag, newFlag) < 0) + error(fileName + ": floating point ABI '" + getMipsFpAbiName(newFlag) + "' is incompatible with target floating point ABI '" + - getMipsFpAbiName(OldFlag) + "'"); - return OldFlag; + getMipsFpAbiName(oldFlag) + "'"); + return oldFlag; } -template static bool isN32Abi(const InputFile *F) { - if (auto *EF = dyn_cast>(F)) - return EF->getObj().getHeader()->e_flags & EF_MIPS_ABI2; +template static bool isN32Abi(const InputFile *f) { + if (auto *ef = dyn_cast(f)) + return ef->template getObj().getHeader()->e_flags & EF_MIPS_ABI2; return false; } -bool elf::isMipsN32Abi(const InputFile *F) { - switch (Config->EKind) { +bool elf::isMipsN32Abi(const InputFile *f) { + switch (config->ekind) { case ELF32LEKind: - return isN32Abi(F); + return isN32Abi(f); case ELF32BEKind: - return isN32Abi(F); + return isN32Abi(f); case ELF64LEKind: - return isN32Abi(F); + return isN32Abi(f); case ELF64BEKind: - return isN32Abi(F); + return isN32Abi(f); default: llvm_unreachable("unknown Config->EKind"); } } -bool elf::isMicroMips() { return Config->EFlags & EF_MIPS_MICROMIPS; } +bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; } bool elf::isMipsR6() { - uint32_t Arch = Config->EFlags & EF_MIPS_ARCH; - return Arch == EF_MIPS_ARCH_32R6 || Arch == EF_MIPS_ARCH_64R6; + uint32_t arch = config->eflags & EF_MIPS_ARCH; + return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6; } template uint32_t elf::calcMipsEFlags(); diff --git a/ELF/Arch/PPC.cpp b/ELF/Arch/PPC.cpp index 7673780..46c5891 100644 --- a/ELF/Arch/PPC.cpp +++ b/ELF/Arch/PPC.cpp @@ -1,13 +1,14 @@ //===- PPC.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 "OutputSections.h" #include "Symbols.h" +#include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Support/Endian.h" @@ -22,60 +23,410 @@ namespace { class PPC final : public TargetInfo { public: PPC(); - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writeGotHeader(uint8_t *buf) const override; + void writePltHeader(uint8_t *buf) const override { + llvm_unreachable("should call writePPC32GlinkSection() instead"); + } + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override { + llvm_unreachable("should call writePPC32GlinkSection() instead"); + } + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + bool needsThunk(RelExpr expr, RelType relocType, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; + uint32_t getThunkSectionSpacing() const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + int getTlsGdRelaxSkip(RelType type) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace +static uint16_t lo(uint32_t v) { return v; } +static uint16_t ha(uint32_t v) { return (v + 0x8000) >> 16; } + +static uint32_t readFromHalf16(const uint8_t *loc) { + return read32(config->isLE ? loc : loc - 2); +} + +static void writeFromHalf16(uint8_t *loc, uint32_t insn) { + write32(config->isLE ? loc : loc - 2, insn); +} + +void elf::writePPC32GlinkSection(uint8_t *buf, size_t numEntries) { + // On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an + // absolute address from a specific .plt slot (usually called .got.plt on + // other targets) and jumps there. + // + // a) With immediate binding (BIND_NOW), the .plt entry is resolved at load + // time. The .glink section is not used. + // b) With lazy binding, the .plt entry points to a `b PLTresolve` + // instruction in .glink, filled in by PPC::writeGotPlt(). + + // Write N `b PLTresolve` first. + for (size_t i = 0; i != numEntries; ++i) + write32(buf + 4 * i, 0x48000000 | 4 * (numEntries - i)); + buf += 4 * numEntries; + + // Then write PLTresolve(), which has two forms: PIC and non-PIC. PLTresolve() + // computes the PLT index (by computing the distance from the landing b to + // itself) and calls _dl_runtime_resolve() (in glibc). + uint32_t got = in.got->getVA(); + uint32_t glink = in.plt->getVA(); // VA of .glink + const uint8_t *end = buf + 64; + if (config->isPic) { + uint32_t afterBcl = in.plt->getSize() - target->pltHeaderSize + 12; + uint32_t gotBcl = got + 4 - (glink + afterBcl); + write32(buf + 0, 0x3d6b0000 | ha(afterBcl)); // addis r11,r11,1f-glink@ha + write32(buf + 4, 0x7c0802a6); // mflr r0 + write32(buf + 8, 0x429f0005); // bcl 20,30,.+4 + write32(buf + 12, 0x396b0000 | lo(afterBcl)); // 1: addi r11,r11,1b-.glink@l + write32(buf + 16, 0x7d8802a6); // mflr r12 + write32(buf + 20, 0x7c0803a6); // mtlr r0 + write32(buf + 24, 0x7d6c5850); // sub r11,r11,r12 + write32(buf + 28, 0x3d8c0000 | ha(gotBcl)); // addis 12,12,GOT+4-1b@ha + if (ha(gotBcl) == ha(gotBcl + 4)) { + write32(buf + 32, 0x800c0000 | lo(gotBcl)); // lwz r0,r12,GOT+4-1b@l(r12) + write32(buf + 36, + 0x818c0000 | lo(gotBcl + 4)); // lwz r12,r12,GOT+8-1b@l(r12) + } else { + write32(buf + 32, 0x840c0000 | lo(gotBcl)); // lwzu r0,r12,GOT+4-1b@l(r12) + write32(buf + 36, 0x818c0000 | 4); // lwz r12,r12,4(r12) + } + write32(buf + 40, 0x7c0903a6); // mtctr 0 + write32(buf + 44, 0x7c0b5a14); // add r0,11,11 + write32(buf + 48, 0x7d605a14); // add r11,0,11 + write32(buf + 52, 0x4e800420); // bctr + buf += 56; + } else { + write32(buf + 0, 0x3d800000 | ha(got + 4)); // lis r12,GOT+4@ha + write32(buf + 4, 0x3d6b0000 | ha(-glink)); // addis r11,r11,-Glink@ha + if (ha(got + 4) == ha(got + 8)) + write32(buf + 8, 0x800c0000 | lo(got + 4)); // lwz r0,GOT+4@l(r12) + else + write32(buf + 8, 0x840c0000 | lo(got + 4)); // lwzu r0,GOT+4@l(r12) + write32(buf + 12, 0x396b0000 | lo(-glink)); // addi r11,r11,-Glink@l + write32(buf + 16, 0x7c0903a6); // mtctr r0 + write32(buf + 20, 0x7c0b5a14); // add r0,r11,r11 + if (ha(got + 4) == ha(got + 8)) + write32(buf + 24, 0x818c0000 | lo(got + 8)); // lwz r12,GOT+8@ha(r12) + else + write32(buf + 24, 0x818c0000 | 4); // lwz r12,4(r12) + write32(buf + 28, 0x7d605a14); // add r11,r0,r11 + write32(buf + 32, 0x4e800420); // bctr + buf += 36; + } + + // Pad with nop. They should not be executed. + for (; buf < end; buf += 4) + write32(buf, 0x60000000); +} + PPC::PPC() { - NoneRel = R_PPC_NONE; - GotBaseSymOff = 0x8000; - GotBaseSymInGotPlt = false; + gotRel = R_PPC_GLOB_DAT; + noneRel = R_PPC_NONE; + pltRel = R_PPC_JMP_SLOT; + relativeRel = R_PPC_RELATIVE; + iRelativeRel = R_PPC_IRELATIVE; + symbolicRel = R_PPC_ADDR32; + gotBaseSymInGotPlt = false; + gotHeaderEntriesNum = 3; + gotPltHeaderEntriesNum = 0; + pltHeaderSize = 64; // size of PLTresolve in .glink + pltEntrySize = 4; + + needsThunks = true; + + tlsModuleIndexRel = R_PPC_DTPMOD32; + tlsOffsetRel = R_PPC_DTPREL32; + tlsGotRel = R_PPC_TPREL32; + + defaultMaxPageSize = 65536; + defaultImageBase = 0x10000000; + + write32(trapInstr.data(), 0x7fe00008); +} + +void PPC::writeGotHeader(uint8_t *buf) const { + // _GLOBAL_OFFSET_TABLE_[0] = _DYNAMIC + // glibc stores _dl_runtime_resolve in _GLOBAL_OFFSET_TABLE_[1], + // link_map in _GLOBAL_OFFSET_TABLE_[2]. + write32(buf, mainPart->dynamic->getVA()); +} + +void PPC::writeGotPlt(uint8_t *buf, const Symbol &s) const { + // Address of the symbol resolver stub in .glink . + write32(buf, in.plt->getVA() + 4 * s.pltIndex); +} + +bool PPC::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { + if (type != R_PPC_REL24 && type != R_PPC_PLTREL24) + return false; + if (s.isInPlt()) + return true; + if (s.isUndefWeak()) + return false; + return !(expr == R_PC && PPC::inBranchRange(type, branchAddr, s.getVA())); } -RelExpr PPC::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +uint32_t PPC::getThunkSectionSpacing() const { return 0x2000000; } + +bool PPC::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + uint64_t offset = dst - src; + if (type == R_PPC_REL24 || type == R_PPC_PLTREL24) + return isInt<26>(offset); + llvm_unreachable("unsupported relocation type used in branch"); +} + +RelExpr PPC::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_HA: + case R_PPC_DTPREL16_HI: + case R_PPC_DTPREL16_LO: + case R_PPC_DTPREL32: + return R_DTPREL; case R_PPC_REL14: - case R_PPC_REL24: case R_PPC_REL32: + case R_PPC_LOCAL24PC: + case R_PPC_REL16_LO: + case R_PPC_REL16_HI: + case R_PPC_REL16_HA: return R_PC; - case R_PPC_PLTREL24: + case R_PPC_GOT16: + return R_GOT_OFF; + case R_PPC_REL24: return R_PLT_PC; + case R_PPC_PLTREL24: + return R_PPC32_PLTREL; + case R_PPC_GOT_TLSGD16: + return R_TLSGD_GOT; + case R_PPC_GOT_TLSLD16: + return R_TLSLD_GOT; + case R_PPC_GOT_TPREL16: + return R_GOT_OFF; + case R_PPC_TLS: + return R_TLSIE_HINT; + case R_PPC_TLSGD: + return R_TLSDESC_CALL; + case R_PPC_TLSLD: + return R_TLSLD_HINT; + case R_PPC_TPREL16: + case R_PPC_TPREL16_HA: + case R_PPC_TPREL16_LO: + case R_PPC_TPREL16_HI: + return R_TLS; default: return R_ABS; } } -void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +RelType PPC::getDynRel(RelType type) const { + if (type == R_PPC_ADDR32) + return type; + return R_PPC_NONE; +} + +static std::pair fromDTPREL(RelType type, uint64_t val) { + uint64_t dtpBiasedVal = val - 0x8000; + switch (type) { + case R_PPC_DTPREL16: + return {R_PPC64_ADDR16, dtpBiasedVal}; + case R_PPC_DTPREL16_HA: + return {R_PPC_ADDR16_HA, dtpBiasedVal}; + case R_PPC_DTPREL16_HI: + return {R_PPC_ADDR16_HI, dtpBiasedVal}; + case R_PPC_DTPREL16_LO: + return {R_PPC_ADDR16_LO, dtpBiasedVal}; + case R_PPC_DTPREL32: + return {R_PPC_ADDR32, dtpBiasedVal}; + default: + return {type, val}; + } +} + +void PPC::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + RelType newType; + std::tie(newType, val) = fromDTPREL(type, val); + switch (newType) { + case R_PPC_ADDR16: + checkIntUInt(loc, val, 16, type); + write16(loc, val); + break; + case R_PPC_GOT16: + case R_PPC_GOT_TLSGD16: + case R_PPC_GOT_TLSLD16: + case R_PPC_GOT_TPREL16: + case R_PPC_TPREL16: + checkInt(loc, val, 16, type); + write16(loc, val); + break; case R_PPC_ADDR16_HA: - write16be(Loc, (Val + 0x8000) >> 16); + case R_PPC_DTPREL16_HA: + case R_PPC_GOT_TLSGD16_HA: + case R_PPC_GOT_TLSLD16_HA: + case R_PPC_GOT_TPREL16_HA: + case R_PPC_REL16_HA: + case R_PPC_TPREL16_HA: + write16(loc, ha(val)); break; case R_PPC_ADDR16_HI: - write16be(Loc, Val >> 16); + case R_PPC_DTPREL16_HI: + case R_PPC_GOT_TLSGD16_HI: + case R_PPC_GOT_TLSLD16_HI: + case R_PPC_GOT_TPREL16_HI: + case R_PPC_REL16_HI: + case R_PPC_TPREL16_HI: + write16(loc, val >> 16); break; case R_PPC_ADDR16_LO: - write16be(Loc, Val); + case R_PPC_DTPREL16_LO: + case R_PPC_GOT_TLSGD16_LO: + case R_PPC_GOT_TLSLD16_LO: + case R_PPC_GOT_TPREL16_LO: + case R_PPC_REL16_LO: + case R_PPC_TPREL16_LO: + write16(loc, val); break; case R_PPC_ADDR32: case R_PPC_REL32: - write32be(Loc, Val); + write32(loc, val); break; - case R_PPC_REL14: - write32be(Loc, read32be(Loc) | (Val & 0xFFFC)); + case R_PPC_REL14: { + uint32_t mask = 0x0000FFFC; + checkInt(loc, val, 16, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); break; - case R_PPC_PLTREL24: + } case R_PPC_REL24: - write32be(Loc, read32be(Loc) | (Val & 0x3FFFFFC)); + case R_PPC_LOCAL24PC: + case R_PPC_PLTREL24: { + uint32_t mask = 0x03FFFFFC; + checkInt(loc, val, 26, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); break; + } + default: + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); + } +} + +RelExpr PPC::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + if (expr == R_RELAX_TLS_GD_TO_IE) + return R_RELAX_TLS_GD_TO_IE_GOT_OFF; + if (expr == R_RELAX_TLS_LD_TO_LE) + return R_RELAX_TLS_LD_TO_LE_ABS; + return expr; +} + +int PPC::getTlsGdRelaxSkip(RelType type) const { + // A __tls_get_addr call instruction is marked with 2 relocations: + // + // R_PPC_TLSGD / R_PPC_TLSLD: marker relocation + // R_PPC_REL24: __tls_get_addr + // + // After the relaxation we no longer call __tls_get_addr and should skip both + // relocations to not create a false dependence on __tls_get_addr being + // defined. + if (type == R_PPC_TLSGD || type == R_PPC_TLSLD) + return 2; + return 1; +} + +void PPC::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TLSGD16: { + // addi rT, rA, x@got@tlsgd --> lwz rT, x@got@tprel(rA) + uint32_t insn = readFromHalf16(loc); + writeFromHalf16(loc, 0x80000000 | (insn & 0x03ff0000)); + relocateOne(loc, R_PPC_GOT_TPREL16, val); + break; + } + case R_PPC_TLSGD: + // bl __tls_get_addr(x@tldgd) --> add r3, r3, r2 + write32(loc, 0x7c631214); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); + } +} + +void PPC::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TLSGD16: + // addi r3, r31, x@got@tlsgd --> addis r3, r2, x@tprel@ha + writeFromHalf16(loc, 0x3c620000 | ha(val)); + break; + case R_PPC_TLSGD: + // bl __tls_get_addr(x@tldgd) --> add r3, r3, x@tprel@l + write32(loc, 0x38630000 | lo(val)); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); + } +} + +void PPC::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TLSLD16: + // addi r3, rA, x@got@tlsgd --> addis r3, r2, 0 + writeFromHalf16(loc, 0x3c620000); + break; + case R_PPC_TLSLD: + // r3+x@dtprel computes r3+x-0x8000, while we want it to compute r3+x@tprel + // = r3+x-0x7000, so add 4096 to r3. + // bl __tls_get_addr(x@tlsld) --> addi r3, r3, 4096 + write32(loc, 0x38631000); + break; + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_HA: + case R_PPC_DTPREL16_HI: + case R_PPC_DTPREL16_LO: + relocateOne(loc, type, val); + break; + default: + llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); + } +} + +void PPC::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC_GOT_TPREL16: { + // lwz rT, x@got@tprel(rA) --> addis rT, r2, x@tprel@ha + uint32_t rt = readFromHalf16(loc) & 0x03e00000; + writeFromHalf16(loc, 0x3c020000 | rt | ha(val)); + break; + } + case R_PPC_TLS: { + uint32_t insn = read32(loc); + if (insn >> 26 != 31) + error("unrecognized instruction for IE to LE R_PPC_TLS"); + // addi rT, rT, x@tls --> addi rT, rT, x@tprel@l + uint32_t dFormOp = getPPCDFormOp((read32(loc) & 0x000007fe) >> 1); + if (dFormOp == 0) + error("unrecognized instruction for IE to LE R_PPC_TLS"); + write32(loc, (dFormOp << 26) | (insn & 0x03ff0000) | lo(val)); + break; + } default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unsupported relocation for TLS IE to LE relaxation"); } } TargetInfo *elf::getPPCTargetInfo() { - static PPC Target; - return &Target; + static PPC target; + return ⌖ } diff --git a/ELF/Arch/PPC64.cpp b/ELF/Arch/PPC64.cpp index 8a320c9..70d284c 100644 --- a/ELF/Arch/PPC64.cpp +++ b/ELF/Arch/PPC64.cpp @@ -1,9 +1,8 @@ //===- PPC64.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 // //===----------------------------------------------------------------------===// @@ -20,8 +19,8 @@ using namespace llvm::ELF; using namespace lld; using namespace lld::elf; -static uint64_t PPC64TocOffset = 0x8000; -static uint64_t DynamicThreadPointerOffset = 0x8000; +static uint64_t ppc64TocOffset = 0x8000; +static uint64_t dynamicThreadPointerOffset = 0x8000; // The instruction encoding of bits 21-30 from the ISA for the Xform and Dform // instructions that can be used as part of the initial exec TLS sequence. @@ -65,16 +64,16 @@ uint64_t elf::getPPC64TocBase() { // TOC starts where the first of these sections starts. We always create a // .got when we see a relocation that uses it, so for us the start is always // the .got. - uint64_t TocVA = In.Got->getVA(); + uint64_t tocVA = in.got->getVA(); // Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000 // thus permitting a full 64 Kbytes segment. Note that the glibc startup // code (crt1.o) assumes that you can get from the TOC base to the // start of the .toc section with only a single (signed) 16-bit relocation. - return TocVA + PPC64TocOffset; + return tocVA + ppc64TocOffset; } -unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther) { +unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) { // The offset is encoded into the 3 most significant bits of the st_other // field, with some special values described in section 3.4.1 of the ABI: // 0 --> Zero offset between the GEP and LEP, and the function does NOT use @@ -86,43 +85,134 @@ unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther) { // 2 --> 2^2 = 4 bytes --> 1 instruction. // 6 --> 2^6 = 64 bytes --> 16 instructions. // 7 --> Reserved. - uint8_t GepToLep = (StOther >> 5) & 7; - if (GepToLep < 2) + uint8_t gepToLep = (stOther >> 5) & 7; + if (gepToLep < 2) return 0; // The value encoded in the st_other bits is the // log-base-2(offset). - if (GepToLep < 7) - return 1 << GepToLep; + if (gepToLep < 7) + return 1 << gepToLep; error("reserved value of 7 in the 3 most-significant-bits of st_other"); return 0; } +bool elf::isPPC64SmallCodeModelTocReloc(RelType type) { + // The only small code model relocations that access the .toc section. + return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS; +} + +// Find the R_PPC64_ADDR64 in .rela.toc with matching offset. +template +static std::pair +getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) { + if (tocSec->numRelocations == 0) + return {}; + + // .rela.toc contains exclusively R_PPC64_ADDR64 relocations sorted by + // r_offset: 0, 8, 16, etc. For a given Offset, Offset / 8 gives us the + // relocation index in most cases. + // + // In rare cases a TOC entry may store a constant that doesn't need an + // R_PPC64_ADDR64, the corresponding r_offset is therefore missing. Offset / 8 + // points to a relocation with larger r_offset. Do a linear probe then. + // Constants are extremely uncommon in .toc and the extra number of array + // accesses can be seen as a small constant. + ArrayRef relas = tocSec->template relas(); + uint64_t index = std::min(offset / 8, relas.size() - 1); + for (;;) { + if (relas[index].r_offset == offset) { + Symbol &sym = tocSec->getFile()->getRelocTargetSym(relas[index]); + return {dyn_cast(&sym), getAddend(relas[index])}; + } + if (relas[index].r_offset < offset || index == 0) + break; + --index; + } + return {}; +} + +// When accessing a symbol defined in another translation unit, compilers +// reserve a .toc entry, allocate a local label and generate toc-indirect +// instuctions: +// +// addis 3, 2, .LC0@toc@ha # R_PPC64_TOC16_HA +// ld 3, .LC0@toc@l(3) # R_PPC64_TOC16_LO_DS, load the address from a .toc entry +// ld/lwa 3, 0(3) # load the value from the address +// +// .section .toc,"aw",@progbits +// .LC0: .tc var[TC],var +// +// If var is defined, non-preemptable and addressable with a 32-bit signed +// offset from the toc base, the address of var can be computed by adding an +// offset to the toc base, saving a load. +// +// addis 3,2,var@toc@ha # this may be relaxed to a nop, +// addi 3,3,var@toc@l # then this becomes addi 3,2,var@toc +// ld/lwa 3, 0(3) # load the value from the address +// +// Returns true if the relaxation is performed. +bool elf::tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel, + uint8_t *bufLoc) { + assert(config->tocOptimize); + if (rel.addend < 0) + return false; + + // If the symbol is not the .toc section, this isn't a toc-indirection. + Defined *defSym = dyn_cast(rel.sym); + if (!defSym || !defSym->isSection() || defSym->section->name != ".toc") + return false; + + Defined *d; + int64_t addend; + auto *tocISB = cast(defSym->section); + std::tie(d, addend) = + config->isLE ? getRelaTocSymAndAddend(tocISB, rel.addend) + : getRelaTocSymAndAddend(tocISB, rel.addend); + + // Only non-preemptable defined symbols can be relaxed. + if (!d || d->isPreemptible) + return false; + + // Two instructions can materialize a 32-bit signed offset from the toc base. + uint64_t tocRelative = d->getVA(addend) - getPPC64TocBase(); + if (!isInt<32>(tocRelative)) + return false; + + // Add PPC64TocOffset that will be subtracted by relocateOne(). + target->relaxGot(bufLoc, type, tocRelative + ppc64TocOffset); + return true; +} + namespace { class PPC64 final : public TargetInfo { public: PPC64(); + int getTlsGdRelaxSkip(RelType type) const override; uint32_t calcEFlags() const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void writeGotHeader(uint8_t *Buf) const override; - bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const override; - bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; - RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const override; - void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - - bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End, - uint8_t StOther) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void writeGotHeader(uint8_t *buf) const override; + bool needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const override; + uint32_t getThunkSectionSpacing() const override; + bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; + + bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const override; }; } // namespace @@ -130,19 +220,19 @@ public: // #higher(value), #highera(value), #highest(value), and #highesta(value) // macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi // document. -static uint16_t lo(uint64_t V) { return V; } -static uint16_t hi(uint64_t V) { return V >> 16; } -static uint16_t ha(uint64_t V) { return (V + 0x8000) >> 16; } -static uint16_t higher(uint64_t V) { return V >> 32; } -static uint16_t highera(uint64_t V) { return (V + 0x8000) >> 32; } -static uint16_t highest(uint64_t V) { return V >> 48; } -static uint16_t highesta(uint64_t V) { return (V + 0x8000) >> 48; } +static uint16_t lo(uint64_t v) { return v; } +static uint16_t hi(uint64_t v) { return v >> 16; } +static uint16_t ha(uint64_t v) { return (v + 0x8000) >> 16; } +static uint16_t higher(uint64_t v) { return v >> 32; } +static uint16_t highera(uint64_t v) { return (v + 0x8000) >> 32; } +static uint16_t highest(uint64_t v) { return v >> 48; } +static uint16_t highesta(uint64_t v) { return (v + 0x8000) >> 48; } // Extracts the 'PO' field of an instruction encoding. -static uint8_t getPrimaryOpCode(uint32_t Encoding) { return (Encoding >> 26); } +static uint8_t getPrimaryOpCode(uint32_t encoding) { return (encoding >> 26); } -static bool isDQFormInstruction(uint32_t Encoding) { - switch (getPrimaryOpCode(Encoding)) { +static bool isDQFormInstruction(uint32_t encoding) { + switch (getPrimaryOpCode(encoding)) { default: return false; case 56: @@ -152,12 +242,12 @@ static bool isDQFormInstruction(uint32_t Encoding) { // There are both DS and DQ instruction forms with this primary opcode. // Namely `lxv` and `stxv` are the DQ-forms that use it. // The DS 'XO' bits being set to 01 is restricted to DQ form. - return (Encoding & 3) == 0x1; + return (encoding & 3) == 0x1; } } -static bool isInstructionUpdateForm(uint32_t Encoding) { - switch (getPrimaryOpCode(Encoding)) { +static bool isInstructionUpdateForm(uint32_t encoding) { + switch (getPrimaryOpCode(encoding)) { default: return false; case LBZU: @@ -176,7 +266,7 @@ static bool isInstructionUpdateForm(uint32_t Encoding) { // between LD/LDU/LWA case LD: case STD: - return (Encoding & 3) == 1; + return (encoding & 3) == 1; } } @@ -185,40 +275,38 @@ static bool isInstructionUpdateForm(uint32_t Encoding) { // pointer is pointing into the middle of the word we want to extract, and on // little-endian it is pointing to the start of the word. These 2 helpers are to // simplify reading and writing in that context. -static void writeInstrFromHalf16(uint8_t *Loc, uint32_t Instr) { - write32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0), Instr); +static void writeFromHalf16(uint8_t *loc, uint32_t insn) { + write32(config->isLE ? loc : loc - 2, insn); } -static uint32_t readInstrFromHalf16(const uint8_t *Loc) { - return read32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0)); +static uint32_t readFromHalf16(const uint8_t *loc) { + return read32(config->isLE ? loc : loc - 2); } PPC64::PPC64() { - GotRel = R_PPC64_GLOB_DAT; - NoneRel = R_PPC64_NONE; - PltRel = R_PPC64_JMP_SLOT; - RelativeRel = R_PPC64_RELATIVE; - IRelativeRel = R_PPC64_IRELATIVE; - GotEntrySize = 8; - PltEntrySize = 4; - GotPltEntrySize = 8; - GotBaseSymInGotPlt = false; - GotBaseSymOff = 0x8000; - GotHeaderEntriesNum = 1; - GotPltHeaderEntriesNum = 2; - PltHeaderSize = 60; - NeedsThunks = true; - - TlsModuleIndexRel = R_PPC64_DTPMOD64; - TlsOffsetRel = R_PPC64_DTPREL64; - - TlsGotRel = R_PPC64_TPREL64; - - NeedsMoreStackNonSplit = false; + gotRel = R_PPC64_GLOB_DAT; + noneRel = R_PPC64_NONE; + pltRel = R_PPC64_JMP_SLOT; + relativeRel = R_PPC64_RELATIVE; + iRelativeRel = R_PPC64_IRELATIVE; + symbolicRel = R_PPC64_ADDR64; + pltEntrySize = 4; + gotBaseSymInGotPlt = false; + gotHeaderEntriesNum = 1; + gotPltHeaderEntriesNum = 2; + pltHeaderSize = 60; + needsThunks = true; + + tlsModuleIndexRel = R_PPC64_DTPMOD64; + tlsOffsetRel = R_PPC64_DTPREL64; + + tlsGotRel = R_PPC64_TPREL64; + + needsMoreStackNonSplit = false; // We need 64K pages (at least under glibc/Linux, the loader won't // set different permissions on a finer granularity than that). - DefaultMaxPageSize = 65536; + defaultMaxPageSize = 65536; // The PPC64 ELF ABI v1 spec, says: // @@ -228,31 +316,66 @@ PPC64::PPC64() { // // And because the lowest non-zero 256M boundary is 0x10000000, PPC64 linkers // use 0x10000000 as the starting address. - DefaultImageBase = 0x10000000; + defaultImageBase = 0x10000000; - write32(TrapInstr.data(), 0x7fe00008); + write32(trapInstr.data(), 0x7fe00008); +} + +int PPC64::getTlsGdRelaxSkip(RelType type) const { + // A __tls_get_addr call instruction is marked with 2 relocations: + // + // R_PPC64_TLSGD / R_PPC64_TLSLD: marker relocation + // R_PPC64_REL24: __tls_get_addr + // + // After the relaxation we no longer call __tls_get_addr and should skip both + // relocations to not create a false dependence on __tls_get_addr being + // defined. + if (type == R_PPC64_TLSGD || type == R_PPC64_TLSLD) + return 2; + return 1; } -static uint32_t getEFlags(InputFile *File) { - if (Config->EKind == ELF64BEKind) - return cast>(File)->getObj().getHeader()->e_flags; - return cast>(File)->getObj().getHeader()->e_flags; +static uint32_t getEFlags(InputFile *file) { + if (config->ekind == ELF64BEKind) + return cast>(file)->getObj().getHeader()->e_flags; + return cast>(file)->getObj().getHeader()->e_flags; } // This file implements v2 ABI. This function makes sure that all // object files have v2 or an unspecified version as an ABI version. uint32_t PPC64::calcEFlags() const { - for (InputFile *F : ObjectFiles) { - uint32_t Flag = getEFlags(F); - if (Flag == 1) - error(toString(F) + ": ABI version 1 is not supported"); - else if (Flag > 2) - error(toString(F) + ": unrecognized e_flags: " + Twine(Flag)); + for (InputFile *f : objectFiles) { + uint32_t flag = getEFlags(f); + if (flag == 1) + error(toString(f) + ": ABI version 1 is not supported"); + else if (flag > 2) + error(toString(f) + ": unrecognized e_flags: " + Twine(flag)); } return 2; } -void PPC64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { + case R_PPC64_TOC16_HA: + // Convert "addis reg, 2, .LC0@toc@h" to "addis reg, 2, var@toc@h" or "nop". + relocateOne(loc, type, val); + break; + case R_PPC64_TOC16_LO_DS: { + // Convert "ld reg, .LC0@toc@l(reg)" to "addi reg, reg, var@toc@l" or + // "addi reg, 2, var@toc". + uint32_t insn = readFromHalf16(loc); + if (getPrimaryOpCode(insn) != LD) + error("expected a 'ld' for got-indirect to toc-relative relaxing"); + writeFromHalf16(loc, (insn & 0x03ffffff) | 0x38000000); + relocateOne(loc, R_PPC64_TOC16_LO, val); + break; + } + default: + llvm_unreachable("unexpected relocation type"); + } +} + +void PPC64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // Reference: 3.7.4.2 of the 64-bit ELF V2 abi supplement. // The general dynamic code sequence for a global `x` will look like: // Instruction Relocation Symbol @@ -268,30 +391,30 @@ void PPC64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // bl __tls_get_addr(x@tlsgd) into nop // nop into addi r3, r3, x@tprel@l - switch (Type) { + switch (type) { case R_PPC64_GOT_TLSGD16_HA: - writeInstrFromHalf16(Loc, 0x60000000); // nop + writeFromHalf16(loc, 0x60000000); // nop break; case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: - writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13 - relocateOne(Loc, R_PPC64_TPREL16_HA, Val); + writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13 + relocateOne(loc, R_PPC64_TPREL16_HA, val); break; case R_PPC64_TLSGD: - write32(Loc, 0x60000000); // nop - write32(Loc + 4, 0x38630000); // addi r3, r3 + write32(loc, 0x60000000); // nop + write32(loc + 4, 0x38630000); // addi r3, r3 // Since we are relocating a half16 type relocation and Loc + 4 points to // the start of an instruction we need to advance the buffer by an extra // 2 bytes on BE. - relocateOne(Loc + 4 + (Config->EKind == ELF64BEKind ? 2 : 0), - R_PPC64_TPREL16_LO, Val); + relocateOne(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0), + R_PPC64_TPREL16_LO, val); break; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } } -void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { // Reference: 3.7.4.3 of the 64-bit ELF V2 abi supplement. // The local dynamic code sequence for a global `x` will look like: // Instruction Relocation Symbol @@ -307,16 +430,16 @@ void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // bl __tls_get_addr(x@tlsgd) into nop // nop into addi r3, r3, 4096 - switch (Type) { + switch (type) { case R_PPC64_GOT_TLSLD16_HA: - writeInstrFromHalf16(Loc, 0x60000000); // nop + writeFromHalf16(loc, 0x60000000); // nop break; case R_PPC64_GOT_TLSLD16_LO: - writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13, 0 + writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13, 0 break; case R_PPC64_TLSLD: - write32(Loc, 0x60000000); // nop - write32(Loc + 4, 0x38631000); // addi r3, r3, 4096 + write32(loc, 0x60000000); // nop + write32(loc + 4, 0x38631000); // addi r3, r3, 4096 break; case R_PPC64_DTPREL16: case R_PPC64_DTPREL16_HA: @@ -324,19 +447,15 @@ void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_PPC64_DTPREL16_DS: case R_PPC64_DTPREL16_LO: case R_PPC64_DTPREL16_LO_DS: - case R_PPC64_GOT_DTPREL16_HA: - case R_PPC64_GOT_DTPREL16_LO_DS: - case R_PPC64_GOT_DTPREL16_DS: - case R_PPC64_GOT_DTPREL16_HI: - relocateOne(Loc, Type, Val); + relocateOne(loc, type, val); break; default: llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); } } -static unsigned getDFormOp(unsigned SecondaryOp) { - switch (SecondaryOp) { +unsigned elf::getPPCDFormOp(unsigned secondaryOp) { + switch (secondaryOp) { case LBZX: return LBZ; case LHZX: @@ -356,12 +475,11 @@ static unsigned getDFormOp(unsigned SecondaryOp) { case ADD: return ADDI; default: - error("unrecognized instruction for IE to LE R_PPC64_TLS"); return 0; } } -void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { // The initial exec code sequence for a global `x` will look like: // Instruction Relocation Symbol // addis r9, r2, x@got@tprel@ha R_PPC64_GOT_TPREL16_HA x @@ -381,26 +499,28 @@ void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // instruction, if we are accessing memory it will use any of the X-form // indexed load or store instructions. - unsigned Offset = (Config->EKind == ELF64BEKind) ? 2 : 0; - switch (Type) { + unsigned offset = (config->ekind == ELF64BEKind) ? 2 : 0; + switch (type) { case R_PPC64_GOT_TPREL16_HA: - write32(Loc - Offset, 0x60000000); // nop + write32(loc - offset, 0x60000000); // nop break; case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_TPREL16_DS: { - uint32_t RegNo = read32(Loc - Offset) & 0x03E00000; // bits 6-10 - write32(Loc - Offset, 0x3C0D0000 | RegNo); // addis RegNo, r13 - relocateOne(Loc, R_PPC64_TPREL16_HA, Val); + uint32_t regNo = read32(loc - offset) & 0x03E00000; // bits 6-10 + write32(loc - offset, 0x3C0D0000 | regNo); // addis RegNo, r13 + relocateOne(loc, R_PPC64_TPREL16_HA, val); break; } case R_PPC64_TLS: { - uint32_t PrimaryOp = getPrimaryOpCode(read32(Loc)); - if (PrimaryOp != 31) + uint32_t primaryOp = getPrimaryOpCode(read32(loc)); + if (primaryOp != 31) error("unrecognized instruction for IE to LE R_PPC64_TLS"); - uint32_t SecondaryOp = (read32(Loc) & 0x000007FE) >> 1; // bits 21-30 - uint32_t DFormOp = getDFormOp(SecondaryOp); - write32(Loc, ((DFormOp << 26) | (read32(Loc) & 0x03FFFFFF))); - relocateOne(Loc + Offset, R_PPC64_TPREL16_LO, Val); + uint32_t secondaryOp = (read32(loc) & 0x000007FE) >> 1; // bits 21-30 + uint32_t dFormOp = getPPCDFormOp(secondaryOp); + if (dFormOp == 0) + error("unrecognized instruction for IE to LE R_PPC64_TLS"); + write32(loc, ((dFormOp << 26) | (read32(loc) & 0x03FFFFFF))); + relocateOne(loc + offset, R_PPC64_TPREL16_LO, val); break; } default: @@ -409,9 +529,9 @@ void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { } } -RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr PPC64::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_PPC64_GOT16: case R_PPC64_GOT16_DS: case R_PPC64_GOT16_HA: @@ -421,16 +541,17 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, return R_GOT_OFF; case R_PPC64_TOC16: case R_PPC64_TOC16_DS: - case R_PPC64_TOC16_HA: case R_PPC64_TOC16_HI: case R_PPC64_TOC16_LO: - case R_PPC64_TOC16_LO_DS: return R_GOTREL; + case R_PPC64_TOC16_HA: + case R_PPC64_TOC16_LO_DS: + return config->tocOptimize ? R_PPC64_RELAX_TOC : R_GOTREL; case R_PPC64_TOC: - return R_PPC_TOC; + return R_PPC64_TOCBASE; case R_PPC64_REL14: case R_PPC64_REL24: - return R_PPC_CALL_PLT; + return R_PPC64_CALL_PLT; case R_PPC64_REL16_LO: case R_PPC64_REL16_HA: case R_PPC64_REL32: @@ -478,7 +599,7 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, case R_PPC64_DTPREL16_LO: case R_PPC64_DTPREL16_LO_DS: case R_PPC64_DTPREL64: - return R_ABS; + return R_DTPREL; case R_PPC64_TLSGD: return R_TLSDESC_CALL; case R_PPC64_TLSLD: @@ -490,115 +611,121 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, } } -void PPC64::writeGotHeader(uint8_t *Buf) const { - write64(Buf, getPPC64TocBase()); +RelType PPC64::getDynRel(RelType type) const { + if (type == R_PPC64_ADDR64 || type == R_PPC64_TOC) + return R_PPC64_ADDR64; + return R_PPC64_NONE; } -void PPC64::writePltHeader(uint8_t *Buf) const { +void PPC64::writeGotHeader(uint8_t *buf) const { + write64(buf, getPPC64TocBase()); +} + +void PPC64::writePltHeader(uint8_t *buf) const { // The generic resolver stub goes first. - write32(Buf + 0, 0x7c0802a6); // mflr r0 - write32(Buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8> - write32(Buf + 8, 0x7d6802a6); // mflr r11 - write32(Buf + 12, 0x7c0803a6); // mtlr r0 - write32(Buf + 16, 0x7d8b6050); // subf r12, r11, r12 - write32(Buf + 20, 0x380cffcc); // subi r0,r12,52 - write32(Buf + 24, 0x7800f082); // srdi r0,r0,62,2 - write32(Buf + 28, 0xe98b002c); // ld r12,44(r11) - write32(Buf + 32, 0x7d6c5a14); // add r11,r12,r11 - write32(Buf + 36, 0xe98b0000); // ld r12,0(r11) - write32(Buf + 40, 0xe96b0008); // ld r11,8(r11) - write32(Buf + 44, 0x7d8903a6); // mtctr r12 - write32(Buf + 48, 0x4e800420); // bctr + write32(buf + 0, 0x7c0802a6); // mflr r0 + write32(buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8> + write32(buf + 8, 0x7d6802a6); // mflr r11 + write32(buf + 12, 0x7c0803a6); // mtlr r0 + write32(buf + 16, 0x7d8b6050); // subf r12, r11, r12 + write32(buf + 20, 0x380cffcc); // subi r0,r12,52 + write32(buf + 24, 0x7800f082); // srdi r0,r0,62,2 + write32(buf + 28, 0xe98b002c); // ld r12,44(r11) + write32(buf + 32, 0x7d6c5a14); // add r11,r12,r11 + write32(buf + 36, 0xe98b0000); // ld r12,0(r11) + write32(buf + 40, 0xe96b0008); // ld r11,8(r11) + write32(buf + 44, 0x7d8903a6); // mtctr r12 + write32(buf + 48, 0x4e800420); // bctr // The 'bcl' instruction will set the link register to the address of the // following instruction ('mflr r11'). Here we store the offset from that // instruction to the first entry in the GotPlt section. - int64_t GotPltOffset = In.GotPlt->getVA() - (In.Plt->getVA() + 8); - write64(Buf + 52, GotPltOffset); + int64_t gotPltOffset = in.gotPlt->getVA() - (in.plt->getVA() + 8); + write64(buf + 52, gotPltOffset); } -void PPC64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - int32_t Offset = PltHeaderSize + Index * PltEntrySize; +void PPC64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + int32_t offset = pltHeaderSize + index * pltEntrySize; // bl __glink_PLTresolve - write32(Buf, 0x48000000 | ((-Offset) & 0x03FFFFFc)); + write32(buf, 0x48000000 | ((-offset) & 0x03FFFFFc)); } -static std::pair toAddr16Rel(RelType Type, uint64_t Val) { +static std::pair toAddr16Rel(RelType type, uint64_t val) { // Relocations relative to the toc-base need to be adjusted by the Toc offset. - uint64_t TocBiasedVal = Val - PPC64TocOffset; + uint64_t tocBiasedVal = val - ppc64TocOffset; // Relocations relative to dtv[dtpmod] need to be adjusted by the DTP offset. - uint64_t DTPBiasedVal = Val - DynamicThreadPointerOffset; + uint64_t dtpBiasedVal = val - dynamicThreadPointerOffset; - switch (Type) { + switch (type) { // TOC biased relocation. case R_PPC64_GOT16: case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSLD16: case R_PPC64_TOC16: - return {R_PPC64_ADDR16, TocBiasedVal}; + return {R_PPC64_ADDR16, tocBiasedVal}; case R_PPC64_GOT16_DS: case R_PPC64_TOC16_DS: case R_PPC64_GOT_TPREL16_DS: case R_PPC64_GOT_DTPREL16_DS: - return {R_PPC64_ADDR16_DS, TocBiasedVal}; + return {R_PPC64_ADDR16_DS, tocBiasedVal}; case R_PPC64_GOT16_HA: case R_PPC64_GOT_TLSGD16_HA: case R_PPC64_GOT_TLSLD16_HA: case R_PPC64_GOT_TPREL16_HA: case R_PPC64_GOT_DTPREL16_HA: case R_PPC64_TOC16_HA: - return {R_PPC64_ADDR16_HA, TocBiasedVal}; + return {R_PPC64_ADDR16_HA, tocBiasedVal}; case R_PPC64_GOT16_HI: case R_PPC64_GOT_TLSGD16_HI: case R_PPC64_GOT_TLSLD16_HI: case R_PPC64_GOT_TPREL16_HI: case R_PPC64_GOT_DTPREL16_HI: case R_PPC64_TOC16_HI: - return {R_PPC64_ADDR16_HI, TocBiasedVal}; + return {R_PPC64_ADDR16_HI, tocBiasedVal}; case R_PPC64_GOT16_LO: case R_PPC64_GOT_TLSGD16_LO: case R_PPC64_GOT_TLSLD16_LO: case R_PPC64_TOC16_LO: - return {R_PPC64_ADDR16_LO, TocBiasedVal}; + return {R_PPC64_ADDR16_LO, tocBiasedVal}; case R_PPC64_GOT16_LO_DS: case R_PPC64_TOC16_LO_DS: case R_PPC64_GOT_TPREL16_LO_DS: case R_PPC64_GOT_DTPREL16_LO_DS: - return {R_PPC64_ADDR16_LO_DS, TocBiasedVal}; + return {R_PPC64_ADDR16_LO_DS, tocBiasedVal}; // Dynamic Thread pointer biased relocation types. case R_PPC64_DTPREL16: - return {R_PPC64_ADDR16, DTPBiasedVal}; + return {R_PPC64_ADDR16, dtpBiasedVal}; case R_PPC64_DTPREL16_DS: - return {R_PPC64_ADDR16_DS, DTPBiasedVal}; + return {R_PPC64_ADDR16_DS, dtpBiasedVal}; case R_PPC64_DTPREL16_HA: - return {R_PPC64_ADDR16_HA, DTPBiasedVal}; + return {R_PPC64_ADDR16_HA, dtpBiasedVal}; case R_PPC64_DTPREL16_HI: - return {R_PPC64_ADDR16_HI, DTPBiasedVal}; + return {R_PPC64_ADDR16_HI, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHER: - return {R_PPC64_ADDR16_HIGHER, DTPBiasedVal}; + return {R_PPC64_ADDR16_HIGHER, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHERA: - return {R_PPC64_ADDR16_HIGHERA, DTPBiasedVal}; + return {R_PPC64_ADDR16_HIGHERA, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHEST: - return {R_PPC64_ADDR16_HIGHEST, DTPBiasedVal}; + return {R_PPC64_ADDR16_HIGHEST, dtpBiasedVal}; case R_PPC64_DTPREL16_HIGHESTA: - return {R_PPC64_ADDR16_HIGHESTA, DTPBiasedVal}; + return {R_PPC64_ADDR16_HIGHESTA, dtpBiasedVal}; case R_PPC64_DTPREL16_LO: - return {R_PPC64_ADDR16_LO, DTPBiasedVal}; + return {R_PPC64_ADDR16_LO, dtpBiasedVal}; case R_PPC64_DTPREL16_LO_DS: - return {R_PPC64_ADDR16_LO_DS, DTPBiasedVal}; + return {R_PPC64_ADDR16_LO_DS, dtpBiasedVal}; case R_PPC64_DTPREL64: - return {R_PPC64_ADDR64, DTPBiasedVal}; + return {R_PPC64_ADDR64, dtpBiasedVal}; default: - return {Type, Val}; + return {type, val}; } } -static bool isTocOptType(RelType Type) { - switch (Type) { +static bool isTocOptType(RelType type) { + switch (type) { case R_PPC64_GOT16_HA: case R_PPC64_GOT16_LO_DS: case R_PPC64_TOC16_HA: @@ -610,66 +737,69 @@ static bool isTocOptType(RelType Type) { } } -void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { // We need to save the original relocation type to use in diagnostics, and // use the original type to determine if we should toc-optimize the // instructions being relocated. - RelType OriginalType = Type; - bool ShouldTocOptimize = isTocOptType(Type); + RelType originalType = type; + bool shouldTocOptimize = isTocOptType(type); // For dynamic thread pointer relative, toc-relative, and got-indirect // relocations, proceed in terms of the corresponding ADDR16 relocation type. - std::tie(Type, Val) = toAddr16Rel(Type, Val); + std::tie(type, val) = toAddr16Rel(type, val); - switch (Type) { + switch (type) { case R_PPC64_ADDR14: { - checkAlignment(Loc, Val, 4, Type); + checkAlignment(loc, val, 4, type); // Preserve the AA/LK bits in the branch instruction - uint8_t AALK = Loc[3]; - write16(Loc + 2, (AALK & 3) | (Val & 0xfffc)); + uint8_t aalk = loc[3]; + write16(loc + 2, (aalk & 3) | (val & 0xfffc)); break; } case R_PPC64_ADDR16: - case R_PPC64_TPREL16: - checkInt(Loc, Val, 16, OriginalType); - write16(Loc, Val); + checkIntUInt(loc, val, 16, originalType); + write16(loc, val); + break; + case R_PPC64_ADDR32: + checkIntUInt(loc, val, 32, originalType); + write32(loc, val); break; case R_PPC64_ADDR16_DS: case R_PPC64_TPREL16_DS: { - checkInt(Loc, Val, 16, OriginalType); + checkInt(loc, val, 16, originalType); // DQ-form instructions use bits 28-31 as part of the instruction encoding // DS-form instructions only use bits 30-31. - uint16_t Mask = isDQFormInstruction(readInstrFromHalf16(Loc)) ? 0xF : 0x3; - checkAlignment(Loc, lo(Val), Mask + 1, OriginalType); - write16(Loc, (read16(Loc) & Mask) | lo(Val)); + uint16_t mask = isDQFormInstruction(readFromHalf16(loc)) ? 0xf : 0x3; + checkAlignment(loc, lo(val), mask + 1, originalType); + write16(loc, (read16(loc) & mask) | lo(val)); } break; case R_PPC64_ADDR16_HA: case R_PPC64_REL16_HA: case R_PPC64_TPREL16_HA: - if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) - writeInstrFromHalf16(Loc, 0x60000000); + if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) + writeFromHalf16(loc, 0x60000000); else - write16(Loc, ha(Val)); + write16(loc, ha(val)); break; case R_PPC64_ADDR16_HI: case R_PPC64_REL16_HI: case R_PPC64_TPREL16_HI: - write16(Loc, hi(Val)); + write16(loc, hi(val)); break; case R_PPC64_ADDR16_HIGHER: case R_PPC64_TPREL16_HIGHER: - write16(Loc, higher(Val)); + write16(loc, higher(val)); break; case R_PPC64_ADDR16_HIGHERA: case R_PPC64_TPREL16_HIGHERA: - write16(Loc, highera(Val)); + write16(loc, highera(val)); break; case R_PPC64_ADDR16_HIGHEST: case R_PPC64_TPREL16_HIGHEST: - write16(Loc, highest(Val)); + write16(loc, highest(val)); break; case R_PPC64_ADDR16_HIGHESTA: case R_PPC64_TPREL16_HIGHESTA: - write16(Loc, highesta(Val)); + write16(loc, highesta(val)); break; case R_PPC64_ADDR16_LO: case R_PPC64_REL16_LO: @@ -677,104 +807,119 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { // When the high-adjusted part of a toc relocation evalutes to 0, it is // changed into a nop. The lo part then needs to be updated to use the // toc-pointer register r2, as the base register. - if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) { - uint32_t Instr = readInstrFromHalf16(Loc); - if (isInstructionUpdateForm(Instr)) - error(getErrorLocation(Loc) + + if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) { + uint32_t insn = readFromHalf16(loc); + if (isInstructionUpdateForm(insn)) + error(getErrorLocation(loc) + "can't toc-optimize an update instruction: 0x" + - utohexstr(Instr)); - Instr = (Instr & 0xFFE00000) | 0x00020000; - writeInstrFromHalf16(Loc, Instr); + utohexstr(insn)); + writeFromHalf16(loc, (insn & 0xffe00000) | 0x00020000 | lo(val)); + } else { + write16(loc, lo(val)); } - write16(Loc, lo(Val)); break; case R_PPC64_ADDR16_LO_DS: case R_PPC64_TPREL16_LO_DS: { // DQ-form instructions use bits 28-31 as part of the instruction encoding // DS-form instructions only use bits 30-31. - uint32_t Inst = readInstrFromHalf16(Loc); - uint16_t Mask = isDQFormInstruction(Inst) ? 0xF : 0x3; - checkAlignment(Loc, lo(Val), Mask + 1, OriginalType); - if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) { + uint32_t insn = readFromHalf16(loc); + uint16_t mask = isDQFormInstruction(insn) ? 0xf : 0x3; + checkAlignment(loc, lo(val), mask + 1, originalType); + if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) { // When the high-adjusted part of a toc relocation evalutes to 0, it is // changed into a nop. The lo part then needs to be updated to use the toc // pointer register r2, as the base register. - if (isInstructionUpdateForm(Inst)) - error(getErrorLocation(Loc) + + if (isInstructionUpdateForm(insn)) + error(getErrorLocation(loc) + "Can't toc-optimize an update instruction: 0x" + - Twine::utohexstr(Inst)); - Inst = (Inst & 0xFFE0000F) | 0x00020000; - writeInstrFromHalf16(Loc, Inst); + Twine::utohexstr(insn)); + insn &= 0xffe00000 | mask; + writeFromHalf16(loc, insn | 0x00020000 | lo(val)); + } else { + write16(loc, (read16(loc) & mask) | lo(val)); } - write16(Loc, (read16(Loc) & Mask) | lo(Val)); } break; - case R_PPC64_ADDR32: + case R_PPC64_TPREL16: + checkInt(loc, val, 16, originalType); + write16(loc, val); + break; case R_PPC64_REL32: - checkInt(Loc, Val, 32, Type); - write32(Loc, Val); + checkInt(loc, val, 32, type); + write32(loc, val); break; case R_PPC64_ADDR64: case R_PPC64_REL64: case R_PPC64_TOC: - write64(Loc, Val); + write64(loc, val); break; case R_PPC64_REL14: { - uint32_t Mask = 0x0000FFFC; - checkInt(Loc, Val, 16, Type); - checkAlignment(Loc, Val, 4, Type); - write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask)); + uint32_t mask = 0x0000FFFC; + checkInt(loc, val, 16, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); break; } case R_PPC64_REL24: { - uint32_t Mask = 0x03FFFFFC; - checkInt(Loc, Val, 26, Type); - checkAlignment(Loc, Val, 4, Type); - write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask)); + uint32_t mask = 0x03FFFFFC; + checkInt(loc, val, 26, type); + checkAlignment(loc, val, 4, type); + write32(loc, (read32(loc) & ~mask) | (val & mask)); break; } case R_PPC64_DTPREL64: - write64(Loc, Val - DynamicThreadPointerOffset); + write64(loc, val - dynamicThreadPointerOffset); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); } } -bool PPC64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, - uint64_t BranchAddr, const Symbol &S) const { - if (Type != R_PPC64_REL14 && Type != R_PPC64_REL24) +bool PPC64::needsThunk(RelExpr expr, RelType type, const InputFile *file, + uint64_t branchAddr, const Symbol &s) const { + if (type != R_PPC64_REL14 && type != R_PPC64_REL24) return false; // If a function is in the Plt it needs to be called with a call-stub. - if (S.isInPlt()) + if (s.isInPlt()) return true; // If a symbol is a weak undefined and we are compiling an executable // it doesn't need a range-extending thunk since it can't be called. - if (S.isUndefWeak() && !Config->Shared) + if (s.isUndefWeak() && !config->shared) return false; // If the offset exceeds the range of the branch type then it will need // a range-extending thunk. - return !inBranchRange(Type, BranchAddr, S.getVA()); + // See the comment in getRelocTargetVA() about R_PPC64_CALL. + return !inBranchRange(type, branchAddr, + s.getVA() + + getPPC64GlobalEntryToLocalEntryOffset(s.stOther)); } -bool PPC64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { - int64_t Offset = Dst - Src; - if (Type == R_PPC64_REL14) - return isInt<16>(Offset); - if (Type == R_PPC64_REL24) - return isInt<26>(Offset); +uint32_t PPC64::getThunkSectionSpacing() const { + // See comment in Arch/ARM.cpp for a more detailed explanation of + // getThunkSectionSpacing(). For PPC64 we pick the constant here based on + // R_PPC64_REL24, which is used by unconditional branch instructions. + // 0x2000000 = (1 << 24-1) * 4 + return 0x2000000; +} + +bool PPC64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { + int64_t offset = dst - src; + if (type == R_PPC64_REL14) + return isInt<16>(offset); + if (type == R_PPC64_REL24) + return isInt<26>(offset); llvm_unreachable("unsupported relocation type used in branch"); } -RelExpr PPC64::adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const { - if (Expr == R_RELAX_TLS_GD_TO_IE) +RelExpr PPC64::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + if (expr == R_RELAX_TLS_GD_TO_IE) return R_RELAX_TLS_GD_TO_IE_GOT_OFF; - if (Expr == R_RELAX_TLS_LD_TO_LE) + if (expr == R_RELAX_TLS_LD_TO_LE) return R_RELAX_TLS_LD_TO_LE_ABS; - return Expr; + return expr; } // Reference: 3.7.4.1 of the 64-bit ELF V2 abi supplement. @@ -794,24 +939,25 @@ RelExpr PPC64::adjustRelaxExpr(RelType Type, const uint8_t *Data, // thread pointer. // Since the nop must directly follow the call, the R_PPC64_TLSGD relocation is // used as the relaxation hint for both steps 2 and 3. -void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void PPC64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_PPC64_GOT_TLSGD16_HA: // This is relaxed from addis rT, r2, sym@got@tlsgd@ha to // addis rT, r2, sym@got@tprel@ha. - relocateOne(Loc, R_PPC64_GOT_TPREL16_HA, Val); + relocateOne(loc, R_PPC64_GOT_TPREL16_HA, val); return; + case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: { // Relax from addi r3, rA, sym@got@tlsgd@l to // ld r3, sym@got@tprel@l(rA) - uint32_t InputRegister = (readInstrFromHalf16(Loc) & (0x1f << 16)); - writeInstrFromHalf16(Loc, 0xE8600000 | InputRegister); - relocateOne(Loc, R_PPC64_GOT_TPREL16_LO_DS, Val); + uint32_t ra = (readFromHalf16(loc) & (0x1f << 16)); + writeFromHalf16(loc, 0xe8600000 | ra); + relocateOne(loc, R_PPC64_GOT_TPREL16_LO_DS, val); return; } case R_PPC64_TLSGD: - write32(Loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop - write32(Loc + 4, 0x7c636A14); // nop --> add r3, r3, r13 + write32(loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop + write32(loc + 4, 0x7c636A14); // nop --> add r3, r3, r13 return; default: llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); @@ -846,86 +992,86 @@ void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { // pair by split-stack-size-adjust. // addis r12, r1, ha(-stack-frame size - split-stack-adjust-size) // addi r12, r12, l(-stack-frame size - split-stack-adjust-size) -bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End, - uint8_t StOther) const { +bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const { // If the caller has a global entry point adjust the buffer past it. The start // of the split-stack prologue will be at the local entry point. - Loc += getPPC64GlobalEntryToLocalEntryOffset(StOther); + loc += getPPC64GlobalEntryToLocalEntryOffset(stOther); // At the very least we expect to see a load of some split-stack data from the // tcb, and 2 instructions that calculate the ending stack address this // function will require. If there is not enough room for at least 3 // instructions it can't be a split-stack prologue. - if (Loc + 12 >= End) + if (loc + 12 >= end) return false; // First instruction must be `ld r0, -0x7000-64(r13)` - if (read32(Loc) != 0xe80d8fc0) + if (read32(loc) != 0xe80d8fc0) return false; - int16_t HiImm = 0; - int16_t LoImm = 0; + int16_t hiImm = 0; + int16_t loImm = 0; // First instruction can be either an addis if the frame size is larger then // 32K, or an addi if the size is less then 32K. - int32_t FirstInstr = read32(Loc + 4); - if (getPrimaryOpCode(FirstInstr) == 15) { - HiImm = FirstInstr & 0xFFFF; - } else if (getPrimaryOpCode(FirstInstr) == 14) { - LoImm = FirstInstr & 0xFFFF; + int32_t firstInstr = read32(loc + 4); + if (getPrimaryOpCode(firstInstr) == 15) { + hiImm = firstInstr & 0xFFFF; + } else if (getPrimaryOpCode(firstInstr) == 14) { + loImm = firstInstr & 0xFFFF; } else { return false; } // Second instruction is either an addi or a nop. If the first instruction was // an addi then LoImm is set and the second instruction must be a nop. - uint32_t SecondInstr = read32(Loc + 8); - if (!LoImm && getPrimaryOpCode(SecondInstr) == 14) { - LoImm = SecondInstr & 0xFFFF; - } else if (SecondInstr != 0x60000000) { + uint32_t secondInstr = read32(loc + 8); + if (!loImm && getPrimaryOpCode(secondInstr) == 14) { + loImm = secondInstr & 0xFFFF; + } else if (secondInstr != 0x60000000) { return false; } // The register operands of the first instruction should be the stack-pointer // (r1) as the input (RA) and r12 as the output (RT). If the second // instruction is not a nop, then it should use r12 as both input and output. - auto CheckRegOperands = [](uint32_t Instr, uint8_t ExpectedRT, - uint8_t ExpectedRA) { - return ((Instr & 0x3E00000) >> 21 == ExpectedRT) && - ((Instr & 0x1F0000) >> 16 == ExpectedRA); + auto checkRegOperands = [](uint32_t instr, uint8_t expectedRT, + uint8_t expectedRA) { + return ((instr & 0x3E00000) >> 21 == expectedRT) && + ((instr & 0x1F0000) >> 16 == expectedRA); }; - if (!CheckRegOperands(FirstInstr, 12, 1)) + if (!checkRegOperands(firstInstr, 12, 1)) return false; - if (SecondInstr != 0x60000000 && !CheckRegOperands(SecondInstr, 12, 12)) + if (secondInstr != 0x60000000 && !checkRegOperands(secondInstr, 12, 12)) return false; - int32_t StackFrameSize = (HiImm * 65536) + LoImm; + int32_t stackFrameSize = (hiImm * 65536) + loImm; // Check that the adjusted size doesn't overflow what we can represent with 2 // instructions. - if (StackFrameSize < Config->SplitStackAdjustSize + INT32_MIN) { - error(getErrorLocation(Loc) + "split-stack prologue adjustment overflows"); + if (stackFrameSize < config->splitStackAdjustSize + INT32_MIN) { + error(getErrorLocation(loc) + "split-stack prologue adjustment overflows"); return false; } - int32_t AdjustedStackFrameSize = - StackFrameSize - Config->SplitStackAdjustSize; + int32_t adjustedStackFrameSize = + stackFrameSize - config->splitStackAdjustSize; - LoImm = AdjustedStackFrameSize & 0xFFFF; - HiImm = (AdjustedStackFrameSize + 0x8000) >> 16; - if (HiImm) { - write32(Loc + 4, 0x3D810000 | (uint16_t)HiImm); + loImm = adjustedStackFrameSize & 0xFFFF; + hiImm = (adjustedStackFrameSize + 0x8000) >> 16; + if (hiImm) { + write32(loc + 4, 0x3D810000 | (uint16_t)hiImm); // If the low immediate is zero the second instruction will be a nop. - SecondInstr = LoImm ? 0x398C0000 | (uint16_t)LoImm : 0x60000000; - write32(Loc + 8, SecondInstr); + secondInstr = loImm ? 0x398C0000 | (uint16_t)loImm : 0x60000000; + write32(loc + 8, secondInstr); } else { // addi r12, r1, imm - write32(Loc + 4, (0x39810000) | (uint16_t)LoImm); - write32(Loc + 8, 0x60000000); + write32(loc + 4, (0x39810000) | (uint16_t)loImm); + write32(loc + 8, 0x60000000); } return true; } TargetInfo *elf::getPPC64TargetInfo() { - static PPC64 Target; - return &Target; + static PPC64 target; + return ⌖ } diff --git a/ELF/Arch/RISCV.cpp b/ELF/Arch/RISCV.cpp index 461e8d3..6f16ade 100644 --- a/ELF/Arch/RISCV.cpp +++ b/ELF/Arch/RISCV.cpp @@ -1,13 +1,13 @@ //===- RISCV.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 "SyntheticSections.h" #include "Target.h" using namespace llvm; @@ -23,59 +23,207 @@ class RISCV final : public TargetInfo { public: RISCV(); uint32_t calcEFlags() const override; - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void writeGotHeader(uint8_t *buf) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + RelType getDynRel(RelType type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // end anonymous namespace -RISCV::RISCV() { NoneRel = R_RISCV_NONE; } +const uint64_t dtpOffset = 0x800; + +enum Op { + ADDI = 0x13, + AUIPC = 0x17, + JALR = 0x67, + LD = 0x3003, + LW = 0x2003, + SRLI = 0x5013, + SUB = 0x40000033, +}; + +enum Reg { + X_RA = 1, + X_T0 = 5, + X_T1 = 6, + X_T2 = 7, + X_T3 = 28, +}; + +static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; } +static uint32_t lo12(uint32_t val) { return val & 4095; } + +static uint32_t itype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t imm) { + return op | (rd << 7) | (rs1 << 15) | (imm << 20); +} +static uint32_t rtype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t rs2) { + return op | (rd << 7) | (rs1 << 15) | (rs2 << 20); +} +static uint32_t utype(uint32_t op, uint32_t rd, uint32_t imm) { + return op | (rd << 7) | (imm << 12); +} + +RISCV::RISCV() { + copyRel = R_RISCV_COPY; + noneRel = R_RISCV_NONE; + pltRel = R_RISCV_JUMP_SLOT; + relativeRel = R_RISCV_RELATIVE; + if (config->is64) { + symbolicRel = R_RISCV_64; + tlsModuleIndexRel = R_RISCV_TLS_DTPMOD64; + tlsOffsetRel = R_RISCV_TLS_DTPREL64; + tlsGotRel = R_RISCV_TLS_TPREL64; + } else { + symbolicRel = R_RISCV_32; + tlsModuleIndexRel = R_RISCV_TLS_DTPMOD32; + tlsOffsetRel = R_RISCV_TLS_DTPREL32; + tlsGotRel = R_RISCV_TLS_TPREL32; + } + gotRel = symbolicRel; + + // .got[0] = _DYNAMIC + gotBaseSymInGotPlt = false; + gotHeaderEntriesNum = 1; + + // .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map + gotPltHeaderEntriesNum = 2; -static uint32_t getEFlags(InputFile *F) { - if (Config->Is64) - return cast>(F)->getObj().getHeader()->e_flags; - return cast>(F)->getObj().getHeader()->e_flags; + pltEntrySize = 16; + pltHeaderSize = 32; +} + +static uint32_t getEFlags(InputFile *f) { + if (config->is64) + return cast>(f)->getObj().getHeader()->e_flags; + return cast>(f)->getObj().getHeader()->e_flags; } uint32_t RISCV::calcEFlags() const { - assert(!ObjectFiles.empty()); + assert(!objectFiles.empty()); - uint32_t Target = getEFlags(ObjectFiles.front()); + uint32_t target = getEFlags(objectFiles.front()); - for (InputFile *F : ObjectFiles) { - uint32_t EFlags = getEFlags(F); - if (EFlags & EF_RISCV_RVC) - Target |= EF_RISCV_RVC; + for (InputFile *f : objectFiles) { + uint32_t eflags = getEFlags(f); + if (eflags & EF_RISCV_RVC) + target |= EF_RISCV_RVC; - if ((EFlags & EF_RISCV_FLOAT_ABI) != (Target & EF_RISCV_FLOAT_ABI)) - error(toString(F) + + if ((eflags & EF_RISCV_FLOAT_ABI) != (target & EF_RISCV_FLOAT_ABI)) + error(toString(f) + ": cannot link object files with different floating-point ABI"); - if ((EFlags & EF_RISCV_RVE) != (Target & EF_RISCV_RVE)) - error(toString(F) + + if ((eflags & EF_RISCV_RVE) != (target & EF_RISCV_RVE)) + error(toString(f) + ": cannot link object files with different EF_RISCV_RVE"); } - return Target; + return target; +} + +void RISCV::writeGotHeader(uint8_t *buf) const { + if (config->is64) + write64le(buf, mainPart->dynamic->getVA()); + else + write32le(buf, mainPart->dynamic->getVA()); +} + +void RISCV::writeGotPlt(uint8_t *buf, const Symbol &s) const { + if (config->is64) + write64le(buf, in.plt->getVA()); + else + write32le(buf, in.plt->getVA()); +} + +void RISCV::writePltHeader(uint8_t *buf) const { + // 1: auipc t2, %pcrel_hi(.got.plt) + // sub t1, t1, t3 + // l[wd] t3, %pcrel_lo(1b)(t2); t3 = _dl_runtime_resolve + // addi t1, t1, -pltHeaderSize-12; t1 = &.plt[i] - &.plt[0] + // addi t0, t2, %pcrel_lo(1b) + // srli t1, t1, (rv64?1:2); t1 = &.got.plt[i] - &.got.plt[0] + // l[wd] t0, Wordsize(t0); t0 = link_map + // jr t3 + uint32_t offset = in.gotPlt->getVA() - in.plt->getVA(); + uint32_t load = config->is64 ? LD : LW; + write32le(buf + 0, utype(AUIPC, X_T2, hi20(offset))); + write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T3)); + write32le(buf + 8, itype(load, X_T3, X_T2, lo12(offset))); + write32le(buf + 12, itype(ADDI, X_T1, X_T1, -target->pltHeaderSize - 12)); + write32le(buf + 16, itype(ADDI, X_T0, X_T2, lo12(offset))); + write32le(buf + 20, itype(SRLI, X_T1, X_T1, config->is64 ? 1 : 2)); + write32le(buf + 24, itype(load, X_T0, X_T0, config->wordsize)); + write32le(buf + 28, itype(JALR, 0, X_T3, 0)); } -RelExpr RISCV::getRelExpr(const RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +void RISCV::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + // 1: auipc t3, %pcrel_hi(f@.got.plt) + // l[wd] t3, %pcrel_lo(1b)(t3) + // jalr t1, t3 + // nop + uint32_t offset = gotPltEntryAddr - pltEntryAddr; + write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset))); + write32le(buf + 4, itype(config->is64 ? LD : LW, X_T3, X_T3, lo12(offset))); + write32le(buf + 8, itype(JALR, X_T1, X_T3, 0)); + write32le(buf + 12, itype(ADDI, 0, 0, 0)); +} + +RelType RISCV::getDynRel(RelType type) const { + return type == target->symbolicRel ? type + : static_cast(R_RISCV_NONE); +} + +RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_RISCV_ADD8: + case R_RISCV_ADD16: + case R_RISCV_ADD32: + case R_RISCV_ADD64: + case R_RISCV_SET6: + case R_RISCV_SET8: + case R_RISCV_SET16: + case R_RISCV_SET32: + case R_RISCV_SUB6: + case R_RISCV_SUB8: + case R_RISCV_SUB16: + case R_RISCV_SUB32: + case R_RISCV_SUB64: + return R_RISCV_ADD; case R_RISCV_JAL: case R_RISCV_BRANCH: - case R_RISCV_CALL: case R_RISCV_PCREL_HI20: case R_RISCV_RVC_BRANCH: case R_RISCV_RVC_JUMP: case R_RISCV_32_PCREL: return R_PC; + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: + return R_PLT_PC; + case R_RISCV_GOT_HI20: + return R_GOT_PC; case R_RISCV_PCREL_LO12_I: case R_RISCV_PCREL_LO12_S: return R_RISCV_PC_INDIRECT; + case R_RISCV_TLS_GD_HI20: + return R_TLSGD_PC; + case R_RISCV_TLS_GOT_HI20: + config->hasStaticTlsModel = true; + return R_GOT_PC; + case R_RISCV_TPREL_HI20: + case R_RISCV_TPREL_LO12_I: + case R_RISCV_TPREL_LO12_S: + return R_TLS; case R_RISCV_RELAX: case R_RISCV_ALIGN: + case R_RISCV_TPREL_ADD: return R_HINT; default: return R_ABS; @@ -83,175 +231,190 @@ RelExpr RISCV::getRelExpr(const RelType Type, const Symbol &S, } // Extract bits V[Begin:End], where range is inclusive, and Begin must be < 63. -static uint32_t extractBits(uint64_t V, uint32_t Begin, uint32_t End) { - return (V & ((1ULL << (Begin + 1)) - 1)) >> End; +static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) { + return (v & ((1ULL << (begin + 1)) - 1)) >> end; } -void RISCV::relocateOne(uint8_t *Loc, const RelType Type, - const uint64_t Val) const { - switch (Type) { +void RISCV::relocateOne(uint8_t *loc, const RelType type, + const uint64_t val) const { + const unsigned bits = config->wordsize * 8; + + switch (type) { case R_RISCV_32: - write32le(Loc, Val); + write32le(loc, val); return; case R_RISCV_64: - write64le(Loc, Val); + write64le(loc, val); return; case R_RISCV_RVC_BRANCH: { - checkInt(Loc, static_cast(Val) >> 1, 8, Type); - checkAlignment(Loc, Val, 2, Type); - uint16_t Insn = read16le(Loc) & 0xE383; - uint16_t Imm8 = extractBits(Val, 8, 8) << 12; - uint16_t Imm4_3 = extractBits(Val, 4, 3) << 10; - uint16_t Imm7_6 = extractBits(Val, 7, 6) << 5; - uint16_t Imm2_1 = extractBits(Val, 2, 1) << 3; - uint16_t Imm5 = extractBits(Val, 5, 5) << 2; - Insn |= Imm8 | Imm4_3 | Imm7_6 | Imm2_1 | Imm5; - - write16le(Loc, Insn); + checkInt(loc, static_cast(val) >> 1, 8, type); + checkAlignment(loc, val, 2, type); + uint16_t insn = read16le(loc) & 0xE383; + uint16_t imm8 = extractBits(val, 8, 8) << 12; + uint16_t imm4_3 = extractBits(val, 4, 3) << 10; + uint16_t imm7_6 = extractBits(val, 7, 6) << 5; + uint16_t imm2_1 = extractBits(val, 2, 1) << 3; + uint16_t imm5 = extractBits(val, 5, 5) << 2; + insn |= imm8 | imm4_3 | imm7_6 | imm2_1 | imm5; + + write16le(loc, insn); return; } case R_RISCV_RVC_JUMP: { - checkInt(Loc, static_cast(Val) >> 1, 11, Type); - checkAlignment(Loc, Val, 2, Type); - uint16_t Insn = read16le(Loc) & 0xE003; - uint16_t Imm11 = extractBits(Val, 11, 11) << 12; - uint16_t Imm4 = extractBits(Val, 4, 4) << 11; - uint16_t Imm9_8 = extractBits(Val, 9, 8) << 9; - uint16_t Imm10 = extractBits(Val, 10, 10) << 8; - uint16_t Imm6 = extractBits(Val, 6, 6) << 7; - uint16_t Imm7 = extractBits(Val, 7, 7) << 6; - uint16_t Imm3_1 = extractBits(Val, 3, 1) << 3; - uint16_t Imm5 = extractBits(Val, 5, 5) << 2; - Insn |= Imm11 | Imm4 | Imm9_8 | Imm10 | Imm6 | Imm7 | Imm3_1 | Imm5; - - write16le(Loc, Insn); + checkInt(loc, static_cast(val) >> 1, 11, type); + checkAlignment(loc, val, 2, type); + uint16_t insn = read16le(loc) & 0xE003; + uint16_t imm11 = extractBits(val, 11, 11) << 12; + uint16_t imm4 = extractBits(val, 4, 4) << 11; + uint16_t imm9_8 = extractBits(val, 9, 8) << 9; + uint16_t imm10 = extractBits(val, 10, 10) << 8; + uint16_t imm6 = extractBits(val, 6, 6) << 7; + uint16_t imm7 = extractBits(val, 7, 7) << 6; + uint16_t imm3_1 = extractBits(val, 3, 1) << 3; + uint16_t imm5 = extractBits(val, 5, 5) << 2; + insn |= imm11 | imm4 | imm9_8 | imm10 | imm6 | imm7 | imm3_1 | imm5; + + write16le(loc, insn); return; } case R_RISCV_RVC_LUI: { - int32_t Imm = ((Val + 0x800) >> 12); - checkUInt(Loc, Imm, 6, Type); - if (Imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0` - write16le(Loc, (read16le(Loc) & 0x0F83) | 0x4000); + int64_t imm = SignExtend64(val + 0x800, bits) >> 12; + checkInt(loc, imm, 6, type); + if (imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0` + write16le(loc, (read16le(loc) & 0x0F83) | 0x4000); } else { - uint16_t Imm17 = extractBits(Val + 0x800, 17, 17) << 12; - uint16_t Imm16_12 = extractBits(Val + 0x800, 16, 12) << 2; - write16le(Loc, (read16le(Loc) & 0xEF83) | Imm17 | Imm16_12); + uint16_t imm17 = extractBits(val + 0x800, 17, 17) << 12; + uint16_t imm16_12 = extractBits(val + 0x800, 16, 12) << 2; + write16le(loc, (read16le(loc) & 0xEF83) | imm17 | imm16_12); } return; } case R_RISCV_JAL: { - checkInt(Loc, static_cast(Val) >> 1, 20, Type); - checkAlignment(Loc, Val, 2, Type); + checkInt(loc, static_cast(val) >> 1, 20, type); + checkAlignment(loc, val, 2, type); - uint32_t Insn = read32le(Loc) & 0xFFF; - uint32_t Imm20 = extractBits(Val, 20, 20) << 31; - uint32_t Imm10_1 = extractBits(Val, 10, 1) << 21; - uint32_t Imm11 = extractBits(Val, 11, 11) << 20; - uint32_t Imm19_12 = extractBits(Val, 19, 12) << 12; - Insn |= Imm20 | Imm10_1 | Imm11 | Imm19_12; + uint32_t insn = read32le(loc) & 0xFFF; + uint32_t imm20 = extractBits(val, 20, 20) << 31; + uint32_t imm10_1 = extractBits(val, 10, 1) << 21; + uint32_t imm11 = extractBits(val, 11, 11) << 20; + uint32_t imm19_12 = extractBits(val, 19, 12) << 12; + insn |= imm20 | imm10_1 | imm11 | imm19_12; - write32le(Loc, Insn); + write32le(loc, insn); return; } case R_RISCV_BRANCH: { - checkInt(Loc, static_cast(Val) >> 1, 12, Type); - checkAlignment(Loc, Val, 2, Type); + checkInt(loc, static_cast(val) >> 1, 12, type); + checkAlignment(loc, val, 2, type); - uint32_t Insn = read32le(Loc) & 0x1FFF07F; - uint32_t Imm12 = extractBits(Val, 12, 12) << 31; - uint32_t Imm10_5 = extractBits(Val, 10, 5) << 25; - uint32_t Imm4_1 = extractBits(Val, 4, 1) << 8; - uint32_t Imm11 = extractBits(Val, 11, 11) << 7; - Insn |= Imm12 | Imm10_5 | Imm4_1 | Imm11; + uint32_t insn = read32le(loc) & 0x1FFF07F; + uint32_t imm12 = extractBits(val, 12, 12) << 31; + uint32_t imm10_5 = extractBits(val, 10, 5) << 25; + uint32_t imm4_1 = extractBits(val, 4, 1) << 8; + uint32_t imm11 = extractBits(val, 11, 11) << 7; + insn |= imm12 | imm10_5 | imm4_1 | imm11; - write32le(Loc, Insn); + write32le(loc, insn); return; } // auipc + jalr pair - case R_RISCV_CALL: { - checkInt(Loc, Val, 32, Type); - if (isInt<32>(Val)) { - relocateOne(Loc, R_RISCV_PCREL_HI20, Val); - relocateOne(Loc + 4, R_RISCV_PCREL_LO12_I, Val); + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: { + int64_t hi = SignExtend64(val + 0x800, bits) >> 12; + checkInt(loc, hi, 20, type); + if (isInt<20>(hi)) { + relocateOne(loc, R_RISCV_PCREL_HI20, val); + relocateOne(loc + 4, R_RISCV_PCREL_LO12_I, val); } return; } + case R_RISCV_GOT_HI20: case R_RISCV_PCREL_HI20: + case R_RISCV_TLS_GD_HI20: + case R_RISCV_TLS_GOT_HI20: + case R_RISCV_TPREL_HI20: case R_RISCV_HI20: { - checkInt(Loc, Val, 32, Type); - uint32_t Hi = Val + 0x800; - write32le(Loc, (read32le(Loc) & 0xFFF) | (Hi & 0xFFFFF000)); + uint64_t hi = val + 0x800; + checkInt(loc, SignExtend64(hi, bits) >> 12, 20, type); + write32le(loc, (read32le(loc) & 0xFFF) | (hi & 0xFFFFF000)); return; } case R_RISCV_PCREL_LO12_I: + case R_RISCV_TPREL_LO12_I: case R_RISCV_LO12_I: { - checkInt(Loc, Val, 32, Type); - uint32_t Hi = Val + 0x800; - uint32_t Lo = Val - (Hi & 0xFFFFF000); - write32le(Loc, (read32le(Loc) & 0xFFFFF) | ((Lo & 0xFFF) << 20)); + uint64_t hi = (val + 0x800) >> 12; + uint64_t lo = val - (hi << 12); + write32le(loc, (read32le(loc) & 0xFFFFF) | ((lo & 0xFFF) << 20)); return; } case R_RISCV_PCREL_LO12_S: + case R_RISCV_TPREL_LO12_S: case R_RISCV_LO12_S: { - checkInt(Loc, Val, 32, Type); - uint32_t Hi = Val + 0x800; - uint32_t Lo = Val - (Hi & 0xFFFFF000); - uint32_t Imm11_5 = extractBits(Lo, 11, 5) << 25; - uint32_t Imm4_0 = extractBits(Lo, 4, 0) << 7; - write32le(Loc, (read32le(Loc) & 0x1FFF07F) | Imm11_5 | Imm4_0); + uint64_t hi = (val + 0x800) >> 12; + uint64_t lo = val - (hi << 12); + uint32_t imm11_5 = extractBits(lo, 11, 5) << 25; + uint32_t imm4_0 = extractBits(lo, 4, 0) << 7; + write32le(loc, (read32le(loc) & 0x1FFF07F) | imm11_5 | imm4_0); return; } case R_RISCV_ADD8: - *Loc += Val; + *loc += val; return; case R_RISCV_ADD16: - write16le(Loc, read16le(Loc) + Val); + write16le(loc, read16le(loc) + val); return; case R_RISCV_ADD32: - write32le(Loc, read32le(Loc) + Val); + write32le(loc, read32le(loc) + val); return; case R_RISCV_ADD64: - write64le(Loc, read64le(Loc) + Val); + write64le(loc, read64le(loc) + val); return; case R_RISCV_SUB6: - *Loc = (*Loc & 0xc0) | (((*Loc & 0x3f) - Val) & 0x3f); + *loc = (*loc & 0xc0) | (((*loc & 0x3f) - val) & 0x3f); return; case R_RISCV_SUB8: - *Loc -= Val; + *loc -= val; return; case R_RISCV_SUB16: - write16le(Loc, read16le(Loc) - Val); + write16le(loc, read16le(loc) - val); return; case R_RISCV_SUB32: - write32le(Loc, read32le(Loc) - Val); + write32le(loc, read32le(loc) - val); return; case R_RISCV_SUB64: - write64le(Loc, read64le(Loc) - Val); + write64le(loc, read64le(loc) - val); return; case R_RISCV_SET6: - *Loc = (*Loc & 0xc0) | (Val & 0x3f); + *loc = (*loc & 0xc0) | (val & 0x3f); return; case R_RISCV_SET8: - *Loc = Val; + *loc = val; return; case R_RISCV_SET16: - write16le(Loc, Val); + write16le(loc, val); return; case R_RISCV_SET32: case R_RISCV_32_PCREL: - write32le(Loc, Val); + write32le(loc, val); return; + case R_RISCV_TLS_DTPREL32: + write32le(loc, val - dtpOffset); + break; + case R_RISCV_TLS_DTPREL64: + write64le(loc, val - dtpOffset); + break; + case R_RISCV_ALIGN: case R_RISCV_RELAX: return; // Ignored (for now) @@ -267,13 +430,13 @@ void RISCV::relocateOne(uint8_t *Loc, const RelType Type, case R_RISCV_GPREL_I: case R_RISCV_GPREL_S: default: - error(getErrorLocation(Loc) + - "unimplemented relocation: " + toString(Type)); + error(getErrorLocation(loc) + + "unimplemented relocation: " + toString(type)); return; } } TargetInfo *elf::getRISCVTargetInfo() { - static RISCV Target; - return &Target; + static RISCV target; + return ⌖ } diff --git a/ELF/Arch/SPARCV9.cpp b/ELF/Arch/SPARCV9.cpp index 831aa20..5299206 100644 --- a/ELF/Arch/SPARCV9.cpp +++ b/ELF/Arch/SPARCV9.cpp @@ -1,9 +1,8 @@ //===- SPARCV9.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,32 +23,32 @@ namespace { class SPARCV9 final : public TargetInfo { public: SPARCV9(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void writePlt(uint8_t *buf, uint64_t gotEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace SPARCV9::SPARCV9() { - CopyRel = R_SPARC_COPY; - GotRel = R_SPARC_GLOB_DAT; - NoneRel = R_SPARC_NONE; - PltRel = R_SPARC_JMP_SLOT; - RelativeRel = R_SPARC_RELATIVE; - GotEntrySize = 8; - PltEntrySize = 32; - PltHeaderSize = 4 * PltEntrySize; + copyRel = R_SPARC_COPY; + gotRel = R_SPARC_GLOB_DAT; + noneRel = R_SPARC_NONE; + pltRel = R_SPARC_JMP_SLOT; + relativeRel = R_SPARC_RELATIVE; + symbolicRel = R_SPARC_64; + pltEntrySize = 32; + pltHeaderSize = 4 * pltEntrySize; - PageSize = 8192; - DefaultMaxPageSize = 0x100000; - DefaultImageBase = 0x100000; + defaultCommonPageSize = 8192; + defaultMaxPageSize = 0x100000; + defaultImageBase = 0x100000; } -RelExpr SPARCV9::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { case R_SPARC_32: case R_SPARC_UA32: case R_SPARC_64: @@ -69,64 +68,65 @@ RelExpr SPARCV9::getRelExpr(RelType Type, const Symbol &S, case R_SPARC_NONE: return R_NONE; default: - return R_INVALID; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } -void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void SPARCV9::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_SPARC_32: case R_SPARC_UA32: // V-word32 - checkUInt(Loc, Val, 32, Type); - write32be(Loc, Val); + checkUInt(loc, val, 32, type); + write32be(loc, val); break; case R_SPARC_DISP32: // V-disp32 - checkInt(Loc, Val, 32, Type); - write32be(Loc, Val); + checkInt(loc, val, 32, type); + write32be(loc, val); break; case R_SPARC_WDISP30: case R_SPARC_WPLT30: // V-disp30 - checkInt(Loc, Val, 32, Type); - write32be(Loc, (read32be(Loc) & ~0x3fffffff) | ((Val >> 2) & 0x3fffffff)); + checkInt(loc, val, 32, type); + write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff)); break; case R_SPARC_22: // V-imm22 - checkUInt(Loc, Val, 22, Type); - write32be(Loc, (read32be(Loc) & ~0x003fffff) | (Val & 0x003fffff)); + checkUInt(loc, val, 22, type); + write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff)); break; case R_SPARC_GOT22: case R_SPARC_PC22: // T-imm22 - write32be(Loc, (read32be(Loc) & ~0x003fffff) | ((Val >> 10) & 0x003fffff)); + write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff)); break; case R_SPARC_WDISP19: // V-disp19 - checkInt(Loc, Val, 21, Type); - write32be(Loc, (read32be(Loc) & ~0x0007ffff) | ((Val >> 2) & 0x0007ffff)); + checkInt(loc, val, 21, type); + write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff)); break; case R_SPARC_GOT10: case R_SPARC_PC10: // T-simm10 - write32be(Loc, (read32be(Loc) & ~0x000003ff) | (Val & 0x000003ff)); + write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff)); break; case R_SPARC_64: case R_SPARC_UA64: - case R_SPARC_GLOB_DAT: // V-xword64 - write64be(Loc, Val); + write64be(loc, val); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -void SPARCV9::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t PltData[] = { +void SPARCV9::writePlt(uint8_t *buf, uint64_t gotEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t pltData[] = { 0x03, 0x00, 0x00, 0x00, // sethi (. - .PLT0), %g1 0x30, 0x68, 0x00, 0x00, // ba,a %xcc, .PLT1 0x01, 0x00, 0x00, 0x00, // nop @@ -136,14 +136,14 @@ void SPARCV9::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, 0x01, 0x00, 0x00, 0x00, // nop 0x01, 0x00, 0x00, 0x00 // nop }; - memcpy(Buf, PltData, sizeof(PltData)); + memcpy(buf, pltData, sizeof(pltData)); - uint64_t Off = getPltEntryOffset(Index); - relocateOne(Buf, R_SPARC_22, Off); - relocateOne(Buf + 4, R_SPARC_WDISP19, -(Off + 4 - PltEntrySize)); + uint64_t off = pltHeaderSize + pltEntrySize * index; + relocateOne(buf, R_SPARC_22, off); + relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize)); } TargetInfo *elf::getSPARCV9TargetInfo() { - static SPARCV9 Target; - return &Target; + static SPARCV9 target; + return ⌖ } diff --git a/ELF/Arch/X86.cpp b/ELF/Arch/X86.cpp index e910375..e1dd231 100644 --- a/ELF/Arch/X86.cpp +++ b/ELF/Arch/X86.cpp @@ -1,9 +1,8 @@ //===- X86.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,63 +23,73 @@ namespace { class X86 : public TargetInfo { public: X86(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override; - void writeGotPltHeader(uint8_t *Buf) const override; - RelType getDynRel(RelType Type) const override; - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writeIgotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - - RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const override; - void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; + int getTlsGdRelaxSkip(RelType type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; + void writeGotPltHeader(uint8_t *buf) const override; + RelType getDynRel(RelType type) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writeIgotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; }; } // namespace X86::X86() { - CopyRel = R_386_COPY; - GotRel = R_386_GLOB_DAT; - NoneRel = R_386_NONE; - PltRel = R_386_JUMP_SLOT; - IRelativeRel = R_386_IRELATIVE; - RelativeRel = R_386_RELATIVE; - TlsGotRel = R_386_TLS_TPOFF; - TlsModuleIndexRel = R_386_TLS_DTPMOD32; - TlsOffsetRel = R_386_TLS_DTPOFF32; - GotEntrySize = 4; - GotPltEntrySize = 4; - PltEntrySize = 16; - PltHeaderSize = 16; - TlsGdRelaxSkip = 2; - TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 + copyRel = R_386_COPY; + gotRel = R_386_GLOB_DAT; + noneRel = R_386_NONE; + pltRel = R_386_JUMP_SLOT; + iRelativeRel = R_386_IRELATIVE; + relativeRel = R_386_RELATIVE; + symbolicRel = R_386_32; + tlsGotRel = R_386_TLS_TPOFF; + tlsModuleIndexRel = R_386_TLS_DTPMOD32; + tlsOffsetRel = R_386_TLS_DTPOFF32; + pltEntrySize = 16; + pltHeaderSize = 16; + trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 // Align to the non-PAE large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. - DefaultImageBase = 0x400000; + defaultImageBase = 0x400000; } -static bool hasBaseReg(uint8_t ModRM) { return (ModRM & 0xc7) != 0x5; } +int X86::getTlsGdRelaxSkip(RelType type) const { + return 2; +} -RelExpr X86::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +RelExpr X86::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + // There are 4 different TLS variable models with varying degrees of + // flexibility and performance. LocalExec and InitialExec models are fast but + // less-flexible models. If they are in use, we set DF_STATIC_TLS flag in the + // dynamic section to let runtime know about that. + if (type == R_386_TLS_LE || type == R_386_TLS_LE_32 || type == R_386_TLS_IE || + type == R_386_TLS_GOTIE) + config->hasStaticTlsModel = true; + + switch (type) { case R_386_8: case R_386_16: case R_386_32: - case R_386_TLS_LDO_32: return R_ABS; + case R_386_TLS_LDO_32: + return R_DTPREL; case R_386_TLS_GD: - return R_TLSGD_GOT_FROM_END; + return R_TLSGD_GOTPLT; case R_386_TLS_LDM: - return R_TLSLD_GOT_FROM_END; + return R_TLSLD_GOTPLT; case R_386_PLT32: return R_PLT_PC; case R_386_PC8: @@ -88,7 +97,7 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S, case R_386_PC32: return R_PC; case R_386_GOTPC: - return R_GOTONLY_PC_FROM_END; + return R_GOTPLTONLY_PC; case R_386_TLS_IE: return R_GOT; case R_386_GOT32: @@ -108,14 +117,14 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S, // load an GOT address to a register, which is usually %ebx. // // So, there are two ways to refer to symbol foo's GOT entry: foo@GOT or - // foo@GOT(%reg). + // foo@GOT(%ebx). // // foo@GOT is not usable in PIC. If we are creating a PIC output and if we // find such relocation, we should report an error. foo@GOT is resolved to // an *absolute* address of foo's GOT entry, because both GOT address and // foo's offset are known. In other words, it's G + A. // - // foo@GOT(%reg) needs to be resolved to a *relative* offset from a GOT to + // foo@GOT(%ebx) needs to be resolved to a *relative* offset from a GOT to // foo's GOT entry in the table, because GOT address is not known but foo's // offset in the table is known. It's G + A - GOT. // @@ -123,16 +132,16 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S, // different use cases. In order to distinguish them, we have to read a // machine instruction. // - // The following code implements it. We assume that Loc[0] is the first - // byte of a displacement or an immediate field of a valid machine + // The following code implements it. We assume that Loc[0] is the first byte + // of a displacement or an immediate field of a valid machine // instruction. That means a ModRM byte is at Loc[-1]. By taking a look at - // the byte, we can determine whether the instruction is register-relative - // (i.e. it was generated for foo@GOT(%reg)) or absolute (i.e. foo@GOT). - return hasBaseReg(Loc[-1]) ? R_GOT_FROM_END : R_GOT; + // the byte, we can determine whether the instruction uses the operand as an + // absolute address (R_GOT) or a register-relative address (R_GOTPLT). + return (loc[-1] & 0xc7) == 0x5 ? R_GOT : R_GOTPLT; case R_386_TLS_GOTIE: - return R_GOT_FROM_END; + return R_GOTPLT; case R_386_GOTOFF: - return R_GOTREL_FROM_END; + return R_GOTPLTREL; case R_386_TLS_LE: return R_TLS; case R_386_TLS_LE_32: @@ -140,105 +149,102 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S, case R_386_NONE: return R_NONE; default: - return R_INVALID; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } -RelExpr X86::adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const { - switch (Expr) { +RelExpr X86::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const { + switch (expr) { default: - return Expr; + return expr; case R_RELAX_TLS_GD_TO_IE: - return R_RELAX_TLS_GD_TO_IE_END; + return R_RELAX_TLS_GD_TO_IE_GOTPLT; case R_RELAX_TLS_GD_TO_LE: return R_RELAX_TLS_GD_TO_LE_NEG; } } -void X86::writeGotPltHeader(uint8_t *Buf) const { - write32le(Buf, In.Dynamic->getVA()); +void X86::writeGotPltHeader(uint8_t *buf) const { + write32le(buf, mainPart->dynamic->getVA()); } -void X86::writeGotPlt(uint8_t *Buf, const Symbol &S) const { +void X86::writeGotPlt(uint8_t *buf, const Symbol &s) const { // Entries in .got.plt initially points back to the corresponding // PLT entries with a fixed offset to skip the first instruction. - write32le(Buf, S.getPltVA() + 6); + write32le(buf, s.getPltVA() + 6); } -void X86::writeIgotPlt(uint8_t *Buf, const Symbol &S) const { +void X86::writeIgotPlt(uint8_t *buf, const Symbol &s) const { // An x86 entry is the address of the ifunc resolver function. - write32le(Buf, S.getVA()); + write32le(buf, s.getVA()); } -RelType X86::getDynRel(RelType Type) const { - if (Type == R_386_TLS_LE) +RelType X86::getDynRel(RelType type) const { + if (type == R_386_TLS_LE) return R_386_TLS_TPOFF; - if (Type == R_386_TLS_LE_32) + if (type == R_386_TLS_LE_32) return R_386_TLS_TPOFF32; - return Type; + return type; } -void X86::writePltHeader(uint8_t *Buf) const { - if (Config->Pic) { - const uint8_t V[] = { - 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl GOTPLT+4(%ebx) - 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *GOTPLT+8(%ebx) +void X86::writePltHeader(uint8_t *buf) const { + if (config->isPic) { + const uint8_t v[] = { + 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl 4(%ebx) + 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *8(%ebx) 0x90, 0x90, 0x90, 0x90 // nop }; - memcpy(Buf, V, sizeof(V)); - - uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); - uint32_t GotPlt = In.GotPlt->getVA() - Ebx; - write32le(Buf + 2, GotPlt + 4); - write32le(Buf + 8, GotPlt + 8); + memcpy(buf, v, sizeof(v)); return; } - const uint8_t PltData[] = { + const uint8_t pltData[] = { 0xff, 0x35, 0, 0, 0, 0, // pushl (GOTPLT+4) 0xff, 0x25, 0, 0, 0, 0, // jmp *(GOTPLT+8) 0x90, 0x90, 0x90, 0x90, // nop }; - memcpy(Buf, PltData, sizeof(PltData)); - uint32_t GotPlt = In.GotPlt->getVA(); - write32le(Buf + 2, GotPlt + 4); - write32le(Buf + 8, GotPlt + 8); + memcpy(buf, pltData, sizeof(pltData)); + uint32_t gotPlt = in.gotPlt->getVA(); + write32le(buf + 2, gotPlt + 4); + write32le(buf + 8, gotPlt + 8); } -void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Inst[] = { - 0xff, 0x00, 0, 0, 0, 0, // jmp *foo_in_GOT or jmp *foo@GOT(%ebx) - 0x68, 0, 0, 0, 0, // pushl $reloc_offset - 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC - }; - memcpy(Buf, Inst, sizeof(Inst)); - - if (Config->Pic) { - // jmp *foo@GOT(%ebx) - uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); - Buf[1] = 0xa3; - write32le(Buf + 2, GotPltEntryAddr - Ebx); +void X86::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + if (config->isPic) { + const uint8_t inst[] = { + 0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx) + 0x68, 0, 0, 0, 0, // pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC + }; + memcpy(buf, inst, sizeof(inst)); + write32le(buf + 2, gotPltEntryAddr - in.gotPlt->getVA()); } else { - // jmp *foo_in_GOT - Buf[1] = 0x25; - write32le(Buf + 2, GotPltEntryAddr); + const uint8_t inst[] = { + 0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT + 0x68, 0, 0, 0, 0, // pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC + }; + memcpy(buf, inst, sizeof(inst)); + write32le(buf + 2, gotPltEntryAddr); } - write32le(Buf + 7, RelOff); - write32le(Buf + 12, -getPltEntryOffset(Index) - 16); + write32le(buf + 7, relOff); + write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16); } -int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const { - switch (Type) { +int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { case R_386_8: case R_386_PC8: - return SignExtend64<8>(*Buf); + return SignExtend64<8>(*buf); case R_386_16: case R_386_PC16: - return SignExtend64<16>(read16le(Buf)); + return SignExtend64<16>(read16le(buf)); case R_386_32: case R_386_GOT32: case R_386_GOT32X: @@ -248,28 +254,28 @@ int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const { case R_386_PLT32: case R_386_TLS_LDO_32: case R_386_TLS_LE: - return SignExtend64<32>(read32le(Buf)); + return SignExtend64<32>(read32le(buf)); default: return 0; } } -void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_386_8: // R_386_{PC,}{8,16} are not part of the i386 psABI, but they are // being used for some 16-bit programs such as boot loaders, so // we want to support them. - checkIntUInt(Loc, Val, 8, Type); - *Loc = Val; + checkIntUInt(loc, val, 8, type); + *loc = val; break; case R_386_PC8: - checkInt(Loc, Val, 8, Type); - *Loc = Val; + checkInt(loc, val, 8, type); + *loc = val; break; case R_386_16: - checkIntUInt(Loc, Val, 16, Type); - write16le(Loc, Val); + checkIntUInt(loc, val, 16, type); + write16le(loc, val); break; case R_386_PC16: // R_386_PC16 is normally used with 16 bit code. In that situation @@ -282,11 +288,10 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { // current location subtracted from it. // We just check that Val fits in 17 bits. This misses some cases, but // should have no false positives. - checkInt(Loc, Val, 17, Type); - write16le(Loc, Val); + checkInt(loc, val, 17, type); + write16le(loc, val); break; case R_386_32: - case R_386_GLOB_DAT: case R_386_GOT32: case R_386_GOT32X: case R_386_GOTOFF: @@ -305,86 +310,86 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_386_TLS_LE_32: case R_386_TLS_TPOFF: case R_386_TLS_TPOFF32: - checkInt(Loc, Val, 32, Type); - write32le(Loc, Val); + checkInt(loc, val, 32, type); + write32le(loc, val); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -void X86::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void X86::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // Convert // leal x@tlsgd(, %ebx, 1), // call __tls_get_addr@plt // to // movl %gs:0,%eax // subl $x@ntpoff,%eax - const uint8_t Inst[] = { + const uint8_t inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax 0x81, 0xe8, 0, 0, 0, 0, // subl Val(%ebx), %eax }; - memcpy(Loc - 3, Inst, sizeof(Inst)); - write32le(Loc + 5, Val); + memcpy(loc - 3, inst, sizeof(inst)); + write32le(loc + 5, val); } -void X86::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void X86::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { // Convert // leal x@tlsgd(, %ebx, 1), // call __tls_get_addr@plt // to // movl %gs:0, %eax // addl x@gotntpoff(%ebx), %eax - const uint8_t Inst[] = { + const uint8_t inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax 0x03, 0x83, 0, 0, 0, 0, // addl Val(%ebx), %eax }; - memcpy(Loc - 3, Inst, sizeof(Inst)); - write32le(Loc + 5, Val); + memcpy(loc - 3, inst, sizeof(inst)); + write32le(loc + 5, val); } // In some conditions, relocations can be optimized to avoid using GOT. // This function does that for Initial Exec to Local Exec case. -void X86::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { +void X86::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { // Ulrich's document section 6.2 says that @gotntpoff can // be used with MOVL or ADDL instructions. // @indntpoff is similar to @gotntpoff, but for use in // position dependent code. - uint8_t Reg = (Loc[-1] >> 3) & 7; + uint8_t reg = (loc[-1] >> 3) & 7; - if (Type == R_386_TLS_IE) { - if (Loc[-1] == 0xa1) { + if (type == R_386_TLS_IE) { + if (loc[-1] == 0xa1) { // "movl foo@indntpoff,%eax" -> "movl $foo,%eax" // This case is different from the generic case below because // this is a 5 byte instruction while below is 6 bytes. - Loc[-1] = 0xb8; - } else if (Loc[-2] == 0x8b) { + loc[-1] = 0xb8; + } else if (loc[-2] == 0x8b) { // "movl foo@indntpoff,%reg" -> "movl $foo,%reg" - Loc[-2] = 0xc7; - Loc[-1] = 0xc0 | Reg; + loc[-2] = 0xc7; + loc[-1] = 0xc0 | reg; } else { // "addl foo@indntpoff,%reg" -> "addl $foo,%reg" - Loc[-2] = 0x81; - Loc[-1] = 0xc0 | Reg; + loc[-2] = 0x81; + loc[-1] = 0xc0 | reg; } } else { - assert(Type == R_386_TLS_GOTIE); - if (Loc[-2] == 0x8b) { + assert(type == R_386_TLS_GOTIE); + if (loc[-2] == 0x8b) { // "movl foo@gottpoff(%rip),%reg" -> "movl $foo,%reg" - Loc[-2] = 0xc7; - Loc[-1] = 0xc0 | Reg; + loc[-2] = 0xc7; + loc[-1] = 0xc0 | reg; } else { // "addl foo@gotntpoff(%rip),%reg" -> "leal foo(%reg),%reg" - Loc[-2] = 0x8d; - Loc[-1] = 0x80 | (Reg << 3) | Reg; + loc[-2] = 0x8d; + loc[-1] = 0x80 | (reg << 3) | reg; } } - write32le(Loc, Val); + write32le(loc, val); } -void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { - if (Type == R_386_TLS_LDO_32) { - write32le(Loc, Val); +void X86::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_386_TLS_LDO_32) { + write32le(loc, val); return; } @@ -395,48 +400,48 @@ void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // movl %gs:0,%eax // nop // leal 0(%esi,1),%esi - const uint8_t Inst[] = { + const uint8_t inst[] = { 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax 0x90, // nop 0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi }; - memcpy(Loc - 2, Inst, sizeof(Inst)); + memcpy(loc - 2, inst, sizeof(inst)); } namespace { class RetpolinePic : public X86 { public: RetpolinePic(); - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; class RetpolineNoPic : public X86 { public: RetpolineNoPic(); - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; } // namespace RetpolinePic::RetpolinePic() { - PltHeaderSize = 48; - PltEntrySize = 32; + pltHeaderSize = 48; + pltEntrySize = 32; } -void RetpolinePic::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - write32le(Buf, S.getPltVA() + 17); +void RetpolinePic::writeGotPlt(uint8_t *buf, const Symbol &s) const { + write32le(buf, s.getPltVA() + 17); } -void RetpolinePic::writePltHeader(uint8_t *Buf) const { - const uint8_t Insn[] = { - 0xff, 0xb3, 0, 0, 0, 0, // 0: pushl GOTPLT+4(%ebx) +void RetpolinePic::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { + 0xff, 0xb3, 4, 0, 0, 0, // 0: pushl 4(%ebx) 0x50, // 6: pushl %eax - 0x8b, 0x83, 0, 0, 0, 0, // 7: mov GOTPLT+8(%ebx), %eax + 0x8b, 0x83, 8, 0, 0, 0, // 7: mov 8(%ebx), %eax 0xe8, 0x0e, 0x00, 0x00, 0x00, // d: call next 0xf3, 0x90, // 12: loop: pause 0x0f, 0xae, 0xe8, // 14: lfence @@ -450,18 +455,13 @@ void RetpolinePic::writePltHeader(uint8_t *Buf) const { 0xc3, // 2e: ret 0xcc, // 2f: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); - - uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); - uint32_t GotPlt = In.GotPlt->getVA() - Ebx; - write32le(Buf + 2, GotPlt + 4); - write32le(Buf + 9, GotPlt + 8); + memcpy(buf, insn, sizeof(insn)); } -void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Insn[] = { +void RetpolinePic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { 0x50, // pushl %eax 0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax 0xe8, 0, 0, 0, 0, // call plt+0x20 @@ -470,28 +470,28 @@ void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, 0xe9, 0, 0, 0, 0, // jmp plt+0 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); - - uint32_t Ebx = In.Got->getVA() + In.Got->getSize(); - unsigned Off = getPltEntryOffset(Index); - write32le(Buf + 3, GotPltEntryAddr - Ebx); - write32le(Buf + 8, -Off - 12 + 32); - write32le(Buf + 13, -Off - 17 + 18); - write32le(Buf + 18, RelOff); - write32le(Buf + 23, -Off - 27); + memcpy(buf, insn, sizeof(insn)); + + uint32_t ebx = in.gotPlt->getVA(); + unsigned off = pltHeaderSize + pltEntrySize * index; + write32le(buf + 3, gotPltEntryAddr - ebx); + write32le(buf + 8, -off - 12 + 32); + write32le(buf + 13, -off - 17 + 18); + write32le(buf + 18, relOff); + write32le(buf + 23, -off - 27); } RetpolineNoPic::RetpolineNoPic() { - PltHeaderSize = 48; - PltEntrySize = 32; + pltHeaderSize = 48; + pltEntrySize = 32; } -void RetpolineNoPic::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - write32le(Buf, S.getPltVA() + 16); +void RetpolineNoPic::writeGotPlt(uint8_t *buf, const Symbol &s) const { + write32le(buf, s.getPltVA() + 16); } -void RetpolineNoPic::writePltHeader(uint8_t *Buf) const { - const uint8_t Insn[] = { +void RetpolineNoPic::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { 0xff, 0x35, 0, 0, 0, 0, // 0: pushl GOTPLT+4 0x50, // 6: pushl %eax 0xa1, 0, 0, 0, 0, // 7: mov GOTPLT+8, %eax @@ -509,17 +509,17 @@ void RetpolineNoPic::writePltHeader(uint8_t *Buf) const { 0xc3, // 2e: ret 0xcc, // 2f: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); - uint32_t GotPlt = In.GotPlt->getVA(); - write32le(Buf + 2, GotPlt + 4); - write32le(Buf + 8, GotPlt + 8); + uint32_t gotPlt = in.gotPlt->getVA(); + write32le(buf + 2, gotPlt + 4); + write32le(buf + 8, gotPlt + 8); } -void RetpolineNoPic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Insn[] = { +void RetpolineNoPic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { 0x50, // 0: pushl %eax 0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax 0xe8, 0, 0, 0, 0, // 6: call plt+0x20 @@ -529,26 +529,26 @@ void RetpolineNoPic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding 0xcc, // 1f: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); - - unsigned Off = getPltEntryOffset(Index); - write32le(Buf + 2, GotPltEntryAddr); - write32le(Buf + 7, -Off - 11 + 32); - write32le(Buf + 12, -Off - 16 + 17); - write32le(Buf + 17, RelOff); - write32le(Buf + 22, -Off - 26); + memcpy(buf, insn, sizeof(insn)); + + unsigned off = pltHeaderSize + pltEntrySize * index; + write32le(buf + 2, gotPltEntryAddr); + write32le(buf + 7, -off - 11 + 32); + write32le(buf + 12, -off - 16 + 17); + write32le(buf + 17, relOff); + write32le(buf + 22, -off - 26); } TargetInfo *elf::getX86TargetInfo() { - if (Config->ZRetpolineplt) { - if (Config->Pic) { - static RetpolinePic T; - return &T; + if (config->zRetpolineplt) { + if (config->isPic) { + static RetpolinePic t; + return &t; } - static RetpolineNoPic T; - return &T; + static RetpolineNoPic t; + return &t; } - static X86 T; - return &T; + static X86 t; + return &t; } diff --git a/ELF/Arch/X86_64.cpp b/ELF/Arch/X86_64.cpp index 0631415..de67aa5 100644 --- a/ELF/Arch/X86_64.cpp +++ b/ELF/Arch/X86_64.cpp @@ -1,9 +1,8 @@ //===- X86_64.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 // //===----------------------------------------------------------------------===// @@ -23,71 +22,74 @@ using namespace lld; using namespace lld::elf; namespace { -template class X86_64 : public TargetInfo { +class X86_64 : public TargetInfo { public: X86_64(); - RelExpr getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const override; - RelType getDynRel(RelType Type) const override; - void writeGotPltHeader(uint8_t *Buf) const override; - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; - - RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr Expr) const override; - void relaxGot(uint8_t *Loc, uint64_t Val) const override; - void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; - bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End, - uint8_t StOther) const override; - -private: - void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, - uint8_t ModRm) const; + int getTlsGdRelaxSkip(RelType type) const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writeGotPltHeader(uint8_t *buf) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; + void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + + RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr expr) const override; + void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const override; }; } // namespace -template X86_64::X86_64() { - CopyRel = R_X86_64_COPY; - GotRel = R_X86_64_GLOB_DAT; - NoneRel = R_X86_64_NONE; - PltRel = R_X86_64_JUMP_SLOT; - RelativeRel = R_X86_64_RELATIVE; - IRelativeRel = R_X86_64_IRELATIVE; - TlsGotRel = R_X86_64_TPOFF64; - TlsModuleIndexRel = R_X86_64_DTPMOD64; - TlsOffsetRel = R_X86_64_DTPOFF64; - GotEntrySize = 8; - GotPltEntrySize = 8; - PltEntrySize = 16; - PltHeaderSize = 16; - TlsGdRelaxSkip = 2; - TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 +X86_64::X86_64() { + copyRel = R_X86_64_COPY; + gotRel = R_X86_64_GLOB_DAT; + noneRel = R_X86_64_NONE; + pltRel = R_X86_64_JUMP_SLOT; + relativeRel = R_X86_64_RELATIVE; + iRelativeRel = R_X86_64_IRELATIVE; + symbolicRel = R_X86_64_64; + tlsDescRel = R_X86_64_TLSDESC; + tlsGotRel = R_X86_64_TPOFF64; + tlsModuleIndexRel = R_X86_64_DTPMOD64; + tlsOffsetRel = R_X86_64_DTPOFF64; + pltEntrySize = 16; + pltHeaderSize = 16; + trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 // Align to the large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. - DefaultImageBase = 0x200000; + defaultImageBase = 0x200000; } -template -RelExpr X86_64::getRelExpr(RelType Type, const Symbol &S, - const uint8_t *Loc) const { - switch (Type) { +int X86_64::getTlsGdRelaxSkip(RelType type) const { return 2; } + +RelExpr X86_64::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + if (type == R_X86_64_GOTTPOFF) + config->hasStaticTlsModel = true; + + switch (type) { case R_X86_64_8: case R_X86_64_16: case R_X86_64_32: case R_X86_64_32S: case R_X86_64_64: + return R_ABS; case R_X86_64_DTPOFF32: case R_X86_64_DTPOFF64: - return R_ABS; + return R_DTPREL; case R_X86_64_TPOFF32: return R_TLS; + case R_X86_64_TLSDESC_CALL: + return R_TLSDESC_CALL; case R_X86_64_TLSLD: return R_TLSLD_PC; case R_X86_64_TLSGD: @@ -97,218 +99,280 @@ RelExpr X86_64::getRelExpr(RelType Type, const Symbol &S, return R_SIZE; case R_X86_64_PLT32: return R_PLT_PC; + case R_X86_64_PC8: + case R_X86_64_PC16: case R_X86_64_PC32: case R_X86_64_PC64: return R_PC; case R_X86_64_GOT32: case R_X86_64_GOT64: - return R_GOT_FROM_END; + return R_GOTPLT; + case R_X86_64_GOTPC32_TLSDESC: + return R_TLSDESC_PC; case R_X86_64_GOTPCREL: case R_X86_64_GOTPCRELX: case R_X86_64_REX_GOTPCRELX: case R_X86_64_GOTTPOFF: return R_GOT_PC; case R_X86_64_GOTOFF64: - return R_GOTREL_FROM_END; + return R_GOTPLTREL; case R_X86_64_GOTPC32: case R_X86_64_GOTPC64: - return R_GOTONLY_PC_FROM_END; + return R_GOTPLTONLY_PC; case R_X86_64_NONE: return R_NONE; default: - return R_INVALID; + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; } } -template void X86_64::writeGotPltHeader(uint8_t *Buf) const { +void X86_64::writeGotPltHeader(uint8_t *buf) const { // The first entry holds the value of _DYNAMIC. It is not clear why that is // required, but it is documented in the psabi and the glibc dynamic linker // seems to use it (note that this is relevant for linking ld.so, not any // other program). - write64le(Buf, In.Dynamic->getVA()); + write64le(buf, mainPart->dynamic->getVA()); } -template -void X86_64::writeGotPlt(uint8_t *Buf, const Symbol &S) const { +void X86_64::writeGotPlt(uint8_t *buf, const Symbol &s) const { // See comments in X86::writeGotPlt. - write64le(Buf, S.getPltVA() + 6); + write64le(buf, s.getPltVA() + 6); } -template void X86_64::writePltHeader(uint8_t *Buf) const { - const uint8_t PltData[] = { +void X86_64::writePltHeader(uint8_t *buf) const { + const uint8_t pltData[] = { 0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip) 0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip) 0x0f, 0x1f, 0x40, 0x00, // nop }; - memcpy(Buf, PltData, sizeof(PltData)); - uint64_t GotPlt = In.GotPlt->getVA(); - uint64_t Plt = In.Plt->getVA(); - write32le(Buf + 2, GotPlt - Plt + 2); // GOTPLT+8 - write32le(Buf + 8, GotPlt - Plt + 4); // GOTPLT+16 + memcpy(buf, pltData, sizeof(pltData)); + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + write32le(buf + 2, gotPlt - plt + 2); // GOTPLT+8 + write32le(buf + 8, gotPlt - plt + 4); // GOTPLT+16 } -template -void X86_64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Inst[] = { +void X86_64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t inst[] = { 0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip) 0x68, 0, 0, 0, 0, // pushq 0xe9, 0, 0, 0, 0, // jmpq plt[0] }; - memcpy(Buf, Inst, sizeof(Inst)); + memcpy(buf, inst, sizeof(inst)); - write32le(Buf + 2, GotPltEntryAddr - PltEntryAddr - 6); - write32le(Buf + 7, Index); - write32le(Buf + 12, -getPltEntryOffset(Index) - 16); + write32le(buf + 2, gotPltEntryAddr - pltEntryAddr - 6); + write32le(buf + 7, index); + write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16); } -template RelType X86_64::getDynRel(RelType Type) const { - if (Type == R_X86_64_64 || Type == R_X86_64_PC64 || Type == R_X86_64_SIZE32 || - Type == R_X86_64_SIZE64) - return Type; +RelType X86_64::getDynRel(RelType type) const { + if (type == R_X86_64_64 || type == R_X86_64_PC64 || type == R_X86_64_SIZE32 || + type == R_X86_64_SIZE64) + return type; return R_X86_64_NONE; } -template -void X86_64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, - uint64_t Val) const { - // Convert - // .byte 0x66 - // leaq x@tlsgd(%rip), %rdi - // .word 0x6666 - // rex64 - // call __tls_get_addr@plt - // to - // mov %fs:0x0,%rax - // lea x@tpoff,%rax - const uint8_t Inst[] = { - 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax - 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax - }; - memcpy(Loc - 4, Inst, sizeof(Inst)); - - // The original code used a pc relative relocation and so we have to - // compensate for the -4 in had in the addend. - write32le(Loc + 8, Val + 4); +void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_X86_64_TLSGD) { + // Convert + // .byte 0x66 + // leaq x@tlsgd(%rip), %rdi + // .word 0x6666 + // rex64 + // call __tls_get_addr@plt + // to the following two instructions. + const uint8_t inst[] = { + 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, + 0x00, 0x00, // mov %fs:0x0,%rax + 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax + }; + memcpy(loc - 4, inst, sizeof(inst)); + + // The original code used a pc relative relocation and so we have to + // compensate for the -4 in had in the addend. + write32le(loc + 8, val + 4); + } else { + // Convert + // lea x@tlsgd(%rip), %rax + // call *(%rax) + // to the following two instructions. + assert(type == R_X86_64_GOTPC32_TLSDESC); + if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { + error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " + "in callq *x@tlsdesc(%rip), %rax"); + return; + } + // movq $x@tpoff(%rip),%rax + loc[-2] = 0xc7; + loc[-1] = 0xc0; + write32le(loc, val + 4); + // xchg ax,ax + loc[4] = 0x66; + loc[5] = 0x90; + } } -template -void X86_64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, - uint64_t Val) const { - // Convert - // .byte 0x66 - // leaq x@tlsgd(%rip), %rdi - // .word 0x6666 - // rex64 - // call __tls_get_addr@plt - // to - // mov %fs:0x0,%rax - // addq x@tpoff,%rax - const uint8_t Inst[] = { - 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax - 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@tpoff,%rax - }; - memcpy(Loc - 4, Inst, sizeof(Inst)); - - // Both code sequences are PC relatives, but since we are moving the constant - // forward by 8 bytes we have to subtract the value by 8. - write32le(Loc + 8, Val - 8); +void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_X86_64_TLSGD) { + // Convert + // .byte 0x66 + // leaq x@tlsgd(%rip), %rdi + // .word 0x6666 + // rex64 + // call __tls_get_addr@plt + // to the following two instructions. + const uint8_t inst[] = { + 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, + 0x00, 0x00, // mov %fs:0x0,%rax + 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@gottpoff(%rip),%rax + }; + memcpy(loc - 4, inst, sizeof(inst)); + + // Both code sequences are PC relatives, but since we are moving the + // constant forward by 8 bytes we have to subtract the value by 8. + write32le(loc + 8, val - 8); + } else { + // Convert + // lea x@tlsgd(%rip), %rax + // call *(%rax) + // to the following two instructions. + assert(type == R_X86_64_GOTPC32_TLSDESC); + if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { + error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " + "in callq *x@tlsdesc(%rip), %rax"); + return; + } + // movq x@gottpoff(%rip),%rax + loc[-2] = 0x8b; + write32le(loc, val); + // xchg ax,ax + loc[4] = 0x66; + loc[5] = 0x90; + } } // In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to // R_X86_64_TPOFF32 so that it does not use GOT. -template -void X86_64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, - uint64_t Val) const { - uint8_t *Inst = Loc - 3; - uint8_t Reg = Loc[-1] >> 3; - uint8_t *RegSlot = Loc - 1; +void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { + uint8_t *inst = loc - 3; + uint8_t reg = loc[-1] >> 3; + uint8_t *regSlot = loc - 1; // Note that ADD with RSP or R12 is converted to ADD instead of LEA // because LEA with these registers needs 4 bytes to encode and thus // wouldn't fit the space. - if (memcmp(Inst, "\x48\x03\x25", 3) == 0) { + if (memcmp(inst, "\x48\x03\x25", 3) == 0) { // "addq foo@gottpoff(%rip),%rsp" -> "addq $foo,%rsp" - memcpy(Inst, "\x48\x81\xc4", 3); - } else if (memcmp(Inst, "\x4c\x03\x25", 3) == 0) { + memcpy(inst, "\x48\x81\xc4", 3); + } else if (memcmp(inst, "\x4c\x03\x25", 3) == 0) { // "addq foo@gottpoff(%rip),%r12" -> "addq $foo,%r12" - memcpy(Inst, "\x49\x81\xc4", 3); - } else if (memcmp(Inst, "\x4c\x03", 2) == 0) { + memcpy(inst, "\x49\x81\xc4", 3); + } else if (memcmp(inst, "\x4c\x03", 2) == 0) { // "addq foo@gottpoff(%rip),%r[8-15]" -> "leaq foo(%r[8-15]),%r[8-15]" - memcpy(Inst, "\x4d\x8d", 2); - *RegSlot = 0x80 | (Reg << 3) | Reg; - } else if (memcmp(Inst, "\x48\x03", 2) == 0) { + memcpy(inst, "\x4d\x8d", 2); + *regSlot = 0x80 | (reg << 3) | reg; + } else if (memcmp(inst, "\x48\x03", 2) == 0) { // "addq foo@gottpoff(%rip),%reg -> "leaq foo(%reg),%reg" - memcpy(Inst, "\x48\x8d", 2); - *RegSlot = 0x80 | (Reg << 3) | Reg; - } else if (memcmp(Inst, "\x4c\x8b", 2) == 0) { + memcpy(inst, "\x48\x8d", 2); + *regSlot = 0x80 | (reg << 3) | reg; + } else if (memcmp(inst, "\x4c\x8b", 2) == 0) { // "movq foo@gottpoff(%rip),%r[8-15]" -> "movq $foo,%r[8-15]" - memcpy(Inst, "\x49\xc7", 2); - *RegSlot = 0xc0 | Reg; - } else if (memcmp(Inst, "\x48\x8b", 2) == 0) { + memcpy(inst, "\x49\xc7", 2); + *regSlot = 0xc0 | reg; + } else if (memcmp(inst, "\x48\x8b", 2) == 0) { // "movq foo@gottpoff(%rip),%reg" -> "movq $foo,%reg" - memcpy(Inst, "\x48\xc7", 2); - *RegSlot = 0xc0 | Reg; + memcpy(inst, "\x48\xc7", 2); + *regSlot = 0xc0 | reg; } else { - error(getErrorLocation(Loc - 3) + + error(getErrorLocation(loc - 3) + "R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only"); } // The original code used a PC relative relocation. // Need to compensate for the -4 it had in the addend. - write32le(Loc, Val + 4); + write32le(loc, val + 4); } -template -void X86_64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, - uint64_t Val) const { - // Convert - // leaq bar@tlsld(%rip), %rdi - // callq __tls_get_addr@PLT - // leaq bar@dtpoff(%rax), %rcx - // to - // .word 0x6666 - // .byte 0x66 - // mov %fs:0,%rax - // leaq bar@tpoff(%rax), %rcx - if (Type == R_X86_64_DTPOFF64) { - write64le(Loc, Val); +void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { + if (type == R_X86_64_DTPOFF64) { + write64le(loc, val); return; } - if (Type == R_X86_64_DTPOFF32) { - write32le(Loc, Val); + if (type == R_X86_64_DTPOFF32) { + write32le(loc, val); return; } - const uint8_t Inst[] = { + const uint8_t inst[] = { 0x66, 0x66, // .word 0x6666 0x66, // .byte 0x66 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0,%rax }; - memcpy(Loc - 3, Inst, sizeof(Inst)); + + if (loc[4] == 0xe8) { + // Convert + // leaq bar@tlsld(%rip), %rdi # 48 8d 3d + // callq __tls_get_addr@PLT # e8 + // leaq bar@dtpoff(%rax), %rcx + // to + // .word 0x6666 + // .byte 0x66 + // mov %fs:0,%rax + // leaq bar@tpoff(%rax), %rcx + memcpy(loc - 3, inst, sizeof(inst)); + return; + } + + if (loc[4] == 0xff && loc[5] == 0x15) { + // Convert + // leaq x@tlsld(%rip),%rdi # 48 8d 3d + // call *__tls_get_addr@GOTPCREL(%rip) # ff 15 + // to + // .long 0x66666666 + // movq %fs:0,%rax + // See "Table 11.9: LD -> LE Code Transition (LP64)" in + // https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-1.0.pdf + loc[-3] = 0x66; + memcpy(loc - 2, inst, sizeof(inst)); + return; + } + + error(getErrorLocation(loc - 3) + + "expected R_X86_64_PLT32 or R_X86_64_GOTPCRELX after R_X86_64_TLSLD"); } -template -void X86_64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { - switch (Type) { +void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { + switch (type) { case R_X86_64_8: - checkUInt(Loc, Val, 8, Type); - *Loc = Val; + checkIntUInt(loc, val, 8, type); + *loc = val; + break; + case R_X86_64_PC8: + checkInt(loc, val, 8, type); + *loc = val; break; case R_X86_64_16: - checkUInt(Loc, Val, 16, Type); - write16le(Loc, Val); + checkIntUInt(loc, val, 16, type); + write16le(loc, val); + break; + case R_X86_64_PC16: + checkInt(loc, val, 16, type); + write16le(loc, val); break; case R_X86_64_32: - checkUInt(Loc, Val, 32, Type); - write32le(Loc, Val); + checkUInt(loc, val, 32, type); + write32le(loc, val); break; case R_X86_64_32S: case R_X86_64_TPOFF32: case R_X86_64_GOT32: case R_X86_64_GOTPC32: + case R_X86_64_GOTPC32_TLSDESC: case R_X86_64_GOTPCREL: case R_X86_64_GOTPCRELX: case R_X86_64_REX_GOTPCRELX: @@ -319,49 +383,47 @@ void X86_64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_X86_64_TLSLD: case R_X86_64_DTPOFF32: case R_X86_64_SIZE32: - checkInt(Loc, Val, 32, Type); - write32le(Loc, Val); + checkInt(loc, val, 32, type); + write32le(loc, val); break; case R_X86_64_64: case R_X86_64_DTPOFF64: - case R_X86_64_GLOB_DAT: case R_X86_64_PC64: case R_X86_64_SIZE64: case R_X86_64_GOT64: case R_X86_64_GOTOFF64: case R_X86_64_GOTPC64: - write64le(Loc, Val); + write64le(loc, val); break; default: - error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + llvm_unreachable("unknown relocation"); } } -template -RelExpr X86_64::adjustRelaxExpr(RelType Type, const uint8_t *Data, - RelExpr RelExpr) const { - if (Type != R_X86_64_GOTPCRELX && Type != R_X86_64_REX_GOTPCRELX) - return RelExpr; - const uint8_t Op = Data[-2]; - const uint8_t ModRm = Data[-1]; +RelExpr X86_64::adjustRelaxExpr(RelType type, const uint8_t *data, + RelExpr relExpr) const { + if (type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX) + return relExpr; + const uint8_t op = data[-2]; + const uint8_t modRm = data[-1]; // FIXME: When PIC is disabled and foo is defined locally in the // lower 32 bit address space, memory operand in mov can be converted into // immediate operand. Otherwise, mov must be changed to lea. We support only // latter relaxation at this moment. - if (Op == 0x8b) + if (op == 0x8b) return R_RELAX_GOT_PC; // Relax call and jmp. - if (Op == 0xff && (ModRm == 0x15 || ModRm == 0x25)) + if (op == 0xff && (modRm == 0x15 || modRm == 0x25)) return R_RELAX_GOT_PC; // Relaxation of test, adc, add, and, cmp, or, sbb, sub, xor. // If PIC then no relaxation is available. // We also don't relax test/binop instructions without REX byte, // they are 32bit operations and not common to have. - assert(Type == R_X86_64_REX_GOTPCRELX); - return Config->Pic ? RelExpr : R_RELAX_GOT_PC_NOPIC; + assert(type == R_X86_64_REX_GOTPCRELX); + return config->isPic ? relExpr : R_RELAX_GOT_PC_NOPIC; } // A subset of relaxations can only be applied for no-PIC. This method @@ -369,12 +431,11 @@ RelExpr X86_64::adjustRelaxExpr(RelType Type, const uint8_t *Data, // "Intel 64 and IA-32 Architectures Software Developer's Manual V2" // (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/ // 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf) -template -void X86_64::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, - uint8_t ModRm) const { - const uint8_t Rex = Loc[-3]; +static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op, + uint8_t modRm) { + const uint8_t rex = loc[-3]; // Convert "test %reg, foo@GOTPCREL(%rip)" to "test $foo, %reg". - if (Op == 0x85) { + if (op == 0x85) { // See "TEST-Logical Compare" (4-428 Vol. 2B), // TEST r/m64, r64 uses "full" ModR / M byte (no opcode extension). @@ -391,11 +452,11 @@ void X86_64::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, // 0x38 == 00 111 000 binary. // We transfer reg2 to reg1 here as operand. // See "2.1.3 ModR/M and SIB Bytes" (Vol. 2A 2-3). - Loc[-1] = 0xc0 | (ModRm & 0x38) >> 3; // ModR/M byte. + loc[-1] = 0xc0 | (modRm & 0x38) >> 3; // ModR/M byte. // Change opcode from TEST r/m64, r64 to TEST r/m64, imm32 // See "TEST-Logical Compare" (4-428 Vol. 2B). - Loc[-2] = 0xf7; + loc[-2] = 0xf7; // Move R bit to the B bit in REX byte. // REX byte is encoded as 0100WRXB, where @@ -408,8 +469,8 @@ void X86_64::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, // REX.B This 1-bit value is an extension to the MODRM.rm field or the // SIB.base field. // See "2.2.1.2 More on REX Prefix Fields " (2-8 Vol. 2A). - Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2; - write32le(Loc, Val); + loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2; + write32le(loc, val); return; } @@ -419,7 +480,7 @@ void X86_64::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, // Convert "binop foo@GOTPCREL(%rip), %reg" to "binop $foo, %reg". // Logic is close to one for test instruction above, but we also // write opcode extension here, see below for details. - Loc[-1] = 0xc0 | (ModRm & 0x38) >> 3 | (Op & 0x3c); // ModR/M byte. + loc[-1] = 0xc0 | (modRm & 0x38) >> 3 | (op & 0x3c); // ModR/M byte. // Primary opcode is 0x81, opcode extension is one of: // 000b = ADD, 001b is OR, 010b is ADC, 011b is SBB, @@ -428,69 +489,67 @@ void X86_64::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op, // See "3.2 INSTRUCTIONS (A-M)" (Vol. 2A 3-15), // "INSTRUCTION SET REFERENCE, N-Z" (Vol. 2B 4-1) for // descriptions about each operation. - Loc[-2] = 0x81; - Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2; - write32le(Loc, Val); + loc[-2] = 0x81; + loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2; + write32le(loc, val); } -template -void X86_64::relaxGot(uint8_t *Loc, uint64_t Val) const { - const uint8_t Op = Loc[-2]; - const uint8_t ModRm = Loc[-1]; +void X86_64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { + const uint8_t op = loc[-2]; + const uint8_t modRm = loc[-1]; // Convert "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg". - if (Op == 0x8b) { - Loc[-2] = 0x8d; - write32le(Loc, Val); + if (op == 0x8b) { + loc[-2] = 0x8d; + write32le(loc, val); return; } - if (Op != 0xff) { + if (op != 0xff) { // We are relaxing a rip relative to an absolute, so compensate // for the old -4 addend. - assert(!Config->Pic); - relaxGotNoPic(Loc, Val + 4, Op, ModRm); + assert(!config->isPic); + relaxGotNoPic(loc, val + 4, op, modRm); return; } // Convert call/jmp instructions. - if (ModRm == 0x15) { + if (modRm == 0x15) { // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call foo". // Instead we convert to "addr32 call foo" where addr32 is an instruction // prefix. That makes result expression to be a single instruction. - Loc[-2] = 0x67; // addr32 prefix - Loc[-1] = 0xe8; // call - write32le(Loc, Val); + loc[-2] = 0x67; // addr32 prefix + loc[-1] = 0xe8; // call + write32le(loc, val); return; } // Convert "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop". // jmp doesn't return, so it is fine to use nop here, it is just a stub. - assert(ModRm == 0x25); - Loc[-2] = 0xe9; // jmp - Loc[3] = 0x90; // nop - write32le(Loc - 1, Val + 1); + assert(modRm == 0x25); + loc[-2] = 0xe9; // jmp + loc[3] = 0x90; // nop + write32le(loc - 1, val + 1); } -// This anonymous namespace works around a warning bug in -// old versions of gcc. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 -namespace { - // A split-stack prologue starts by checking the amount of stack remaining // in one of two ways: // A) Comparing of the stack pointer to a field in the tcb. // B) Or a load of a stack pointer offset with an lea to r10 or r11. -template <> -bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *Loc, - uint8_t *End, - uint8_t StOther) const { - if (Loc + 8 >= End) +bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, + uint8_t stOther) const { + if (!config->is64) { + error("Target doesn't support split stacks."); + return false; + } + + if (loc + 8 >= end) return false; // Replace "cmp %fs:0x70,%rsp" and subsequent branch // with "stc, nopl 0x0(%rax,%rax,1)" - if (memcmp(Loc, "\x64\x48\x3b\x24\x25", 5) == 0) { - memcpy(Loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8); + if (memcmp(loc, "\x64\x48\x3b\x24\x25", 5) == 0) { + memcpy(loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8); return true; } @@ -498,25 +557,16 @@ bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *Loc, // be r10 or r11. The lea instruction feeds a subsequent compare which checks // if there is X available stack space. Making X larger effectively reserves // that much additional space. The stack grows downward so subtract the value. - if (memcmp(Loc, "\x4c\x8d\x94\x24", 4) == 0 || - memcmp(Loc, "\x4c\x8d\x9c\x24", 4) == 0) { + if (memcmp(loc, "\x4c\x8d\x94\x24", 4) == 0 || + memcmp(loc, "\x4c\x8d\x9c\x24", 4) == 0) { // The offset bytes are encoded four bytes after the start of the // instruction. - write32le(Loc + 4, read32le(Loc + 4) - 0x4000); + write32le(loc + 4, read32le(loc + 4) - 0x4000); return true; } return false; } -template <> -bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *Loc, - uint8_t *End, - uint8_t StOther) const { - llvm_unreachable("Target doesn't support split stacks."); -} - -} // namespace - // These nonstandard PLT entries are to migtigate Spectre v2 security // vulnerability. In order to mitigate Spectre v2, we want to avoid indirect // branch instructions such as `jmp *GOTPLT(%rip)`. So, in the following PLT @@ -527,37 +577,36 @@ bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *Loc, // is specified, all dynamic symbols are resolved at load-time. Thus, when // that option is given, we can omit code for symbol lazy resolution. namespace { -template class Retpoline : public X86_64 { +class Retpoline : public X86_64 { public: Retpoline(); - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; -template class RetpolineZNow : public X86_64 { +class RetpolineZNow : public X86_64 { public: RetpolineZNow(); - void writeGotPlt(uint8_t *Buf, const Symbol &S) const override {} - void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, - int32_t Index, unsigned RelOff) const override; + void writeGotPlt(uint8_t *buf, const Symbol &s) const override {} + void writePltHeader(uint8_t *buf) const override; + void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index, unsigned relOff) const override; }; } // namespace -template Retpoline::Retpoline() { - TargetInfo::PltHeaderSize = 48; - TargetInfo::PltEntrySize = 32; +Retpoline::Retpoline() { + pltHeaderSize = 48; + pltEntrySize = 32; } -template -void Retpoline::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - write64le(Buf, S.getPltVA() + 17); +void Retpoline::writeGotPlt(uint8_t *buf, const Symbol &s) const { + write64le(buf, s.getPltVA() + 17); } -template void Retpoline::writePltHeader(uint8_t *Buf) const { - const uint8_t Insn[] = { +void Retpoline::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { 0xff, 0x35, 0, 0, 0, 0, // 0: pushq GOTPLT+8(%rip) 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 6: mov GOTPLT+16(%rip), %r11 0xe8, 0x0e, 0x00, 0x00, 0x00, // d: callq next @@ -570,19 +619,18 @@ template void Retpoline::writePltHeader(uint8_t *Buf) const { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 25: int3; padding 0xcc, 0xcc, 0xcc, 0xcc, // 2c: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); - uint64_t GotPlt = In.GotPlt->getVA(); - uint64_t Plt = In.Plt->getVA(); - write32le(Buf + 2, GotPlt - Plt - 6 + 8); - write32le(Buf + 9, GotPlt - Plt - 13 + 16); + uint64_t gotPlt = in.gotPlt->getVA(); + uint64_t plt = in.plt->getVA(); + write32le(buf + 2, gotPlt - plt - 6 + 8); + write32le(buf + 9, gotPlt - plt - 13 + 16); } -template -void Retpoline::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Insn[] = { +void Retpoline::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 0: mov foo@GOTPLT(%rip), %r11 0xe8, 0, 0, 0, 0, // 7: callq plt+0x20 0xe9, 0, 0, 0, 0, // c: jmp plt+0x12 @@ -590,25 +638,24 @@ void Retpoline::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, 0xe9, 0, 0, 0, 0, // 16: jmp plt+0 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1b: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); - uint64_t Off = getPltEntryOffset(Index); + uint64_t off = pltHeaderSize + pltEntrySize * index; - write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7); - write32le(Buf + 8, -Off - 12 + 32); - write32le(Buf + 13, -Off - 17 + 18); - write32le(Buf + 18, Index); - write32le(Buf + 23, -Off - 27); + write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7); + write32le(buf + 8, -off - 12 + 32); + write32le(buf + 13, -off - 17 + 18); + write32le(buf + 18, index); + write32le(buf + 23, -off - 27); } -template RetpolineZNow::RetpolineZNow() { - TargetInfo::PltHeaderSize = 32; - TargetInfo::PltEntrySize = 16; +RetpolineZNow::RetpolineZNow() { + pltHeaderSize = 32; + pltEntrySize = 16; } -template -void RetpolineZNow::writePltHeader(uint8_t *Buf) const { - const uint8_t Insn[] = { +void RetpolineZNow::writePltHeader(uint8_t *buf) const { + const uint8_t insn[] = { 0xe8, 0x0b, 0x00, 0x00, 0x00, // 0: call next 0xf3, 0x90, // 5: loop: pause 0x0f, 0xae, 0xe8, // 7: lfence @@ -620,37 +667,35 @@ void RetpolineZNow::writePltHeader(uint8_t *Buf) const { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding 0xcc, // 1f: int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); } -template -void RetpolineZNow::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - const uint8_t Insn[] = { +void RetpolineZNow::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index, + unsigned relOff) const { + const uint8_t insn[] = { 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // mov foo@GOTPLT(%rip), %r11 0xe9, 0, 0, 0, 0, // jmp plt+0 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding }; - memcpy(Buf, Insn, sizeof(Insn)); + memcpy(buf, insn, sizeof(insn)); - write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7); - write32le(Buf + 8, -getPltEntryOffset(Index) - 12); + write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7); + write32le(buf + 8, -pltHeaderSize - pltEntrySize * index - 12); } -template static TargetInfo *getTargetInfo() { - if (Config->ZRetpolineplt) { - if (Config->ZNow) { - static RetpolineZNow T; - return &T; +static TargetInfo *getTargetInfo() { + if (config->zRetpolineplt) { + if (config->zNow) { + static RetpolineZNow t; + return &t; } - static Retpoline T; - return &T; + static Retpoline t; + return &t; } - static X86_64 T; - return &T; + static X86_64 t; + return &t; } -TargetInfo *elf::getX32TargetInfo() { return getTargetInfo(); } -TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); } +TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); } diff --git a/ELF/Bits.h b/ELF/Bits.h deleted file mode 100644 index 13d4032..0000000 --- a/ELF/Bits.h +++ /dev/null @@ -1,35 +0,0 @@ -//===- Bits.h ---------------------------------------------------*- C++ -*-===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLD_ELF_BITS_H -#define LLD_ELF_BITS_H - -#include "Config.h" -#include "llvm/Support/Endian.h" - -namespace lld { -namespace elf { - -inline uint64_t readUint(uint8_t *Buf) { - if (Config->Is64) - return llvm::support::endian::read64(Buf, Config->Endianness); - return llvm::support::endian::read32(Buf, Config->Endianness); -} - -inline void writeUint(uint8_t *Buf, uint64_t Val) { - if (Config->Is64) - llvm::support::endian::write64(Buf, Val, Config->Endianness); - else - llvm::support::endian::write32(Buf, Val, Config->Endianness); -} - -} // namespace elf -} // namespace lld - -#endif diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt index a1c23b0..7057874 100644 --- a/ELF/CMakeLists.txt +++ b/ELF/CMakeLists.txt @@ -27,7 +27,6 @@ add_lld_library(lldELF Driver.cpp DriverUtils.cpp EhFrame.cpp - Filesystem.cpp ICF.cpp InputFiles.cpp InputSection.cpp diff --git a/ELF/CallGraphSort.cpp b/ELF/CallGraphSort.cpp index 2a7d786..9aaadd4 100644 --- a/ELF/CallGraphSort.cpp +++ b/ELF/CallGraphSort.cpp @@ -1,9 +1,8 @@ //===- CallGraphSort.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 // //===----------------------------------------------------------------------===// /// @@ -52,24 +51,24 @@ using namespace lld::elf; namespace { struct Edge { - int From; - uint64_t Weight; + int from; + uint64_t weight; }; struct Cluster { - Cluster(int Sec, size_t S) : Sections{Sec}, Size(S) {} + Cluster(int sec, size_t s) : sections{sec}, size(s) {} double getDensity() const { - if (Size == 0) + if (size == 0) return 0; - return double(Weight) / double(Size); + return double(weight) / double(size); } - std::vector Sections; - size_t Size = 0; - uint64_t Weight = 0; - uint64_t InitialWeight = 0; - Edge BestPred = {-1, 0}; + std::vector sections; + size_t size = 0; + uint64_t weight = 0; + uint64_t initialWeight = 0; + Edge bestPred = {-1, 0}; }; class CallGraphSort { @@ -79,8 +78,8 @@ public: DenseMap run(); private: - std::vector Clusters; - std::vector Sections; + std::vector clusters; + std::vector sections; void groupClusters(); }; @@ -93,30 +92,30 @@ constexpr int MAX_DENSITY_DEGRADATION = 8; constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024; } // end anonymous namespace -typedef std::pair - SectionPair; +using SectionPair = + std::pair; // Take the edge list in Config->CallGraphProfile, resolve symbol names to // Symbols, and generate a graph between InputSections with the provided // weights. CallGraphSort::CallGraphSort() { - MapVector &Profile = Config->CallGraphProfile; - DenseMap SecToCluster; - - auto GetOrCreateNode = [&](const InputSectionBase *IS) -> int { - auto Res = SecToCluster.insert(std::make_pair(IS, Clusters.size())); - if (Res.second) { - Sections.push_back(IS); - Clusters.emplace_back(Clusters.size(), IS->getSize()); + MapVector &profile = config->callGraphProfile; + DenseMap secToCluster; + + auto getOrCreateNode = [&](const InputSectionBase *isec) -> int { + auto res = secToCluster.insert(std::make_pair(isec, clusters.size())); + if (res.second) { + sections.push_back(isec); + clusters.emplace_back(clusters.size(), isec->getSize()); } - return Res.first->second; + return res.first->second; }; // Create the graph. - for (std::pair &C : Profile) { - const auto *FromSB = cast(C.first.first->Repl); - const auto *ToSB = cast(C.first.second->Repl); - uint64_t Weight = C.second; + for (std::pair &c : profile) { + const auto *fromSB = cast(c.first.first->repl); + const auto *toSB = cast(c.first.second->repl); + uint64_t weight = c.second; // Ignore edges between input sections belonging to different output // sections. This is done because otherwise we would end up with clusters @@ -124,110 +123,130 @@ CallGraphSort::CallGraphSort() { // output. This messes with the cluster size and density calculations. We // would also end up moving input sections in other output sections without // moving them closer to what calls them. - if (FromSB->getOutputSection() != ToSB->getOutputSection()) + if (fromSB->getOutputSection() != toSB->getOutputSection()) continue; - int From = GetOrCreateNode(FromSB); - int To = GetOrCreateNode(ToSB); + int from = getOrCreateNode(fromSB); + int to = getOrCreateNode(toSB); - Clusters[To].Weight += Weight; + clusters[to].weight += weight; - if (From == To) + if (from == to) continue; // Remember the best edge. - Cluster &ToC = Clusters[To]; - if (ToC.BestPred.From == -1 || ToC.BestPred.Weight < Weight) { - ToC.BestPred.From = From; - ToC.BestPred.Weight = Weight; + Cluster &toC = clusters[to]; + if (toC.bestPred.from == -1 || toC.bestPred.weight < weight) { + toC.bestPred.from = from; + toC.bestPred.weight = weight; } } - for (Cluster &C : Clusters) - C.InitialWeight = C.Weight; + for (Cluster &c : clusters) + c.initialWeight = c.weight; } // It's bad to merge clusters which would degrade the density too much. -static bool isNewDensityBad(Cluster &A, Cluster &B) { - double NewDensity = double(A.Weight + B.Weight) / double(A.Size + B.Size); - return NewDensity < A.getDensity() / MAX_DENSITY_DEGRADATION; +static bool isNewDensityBad(Cluster &a, Cluster &b) { + double newDensity = double(a.weight + b.weight) / double(a.size + b.size); + return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION; } -static void mergeClusters(Cluster &Into, Cluster &From) { - Into.Sections.insert(Into.Sections.end(), From.Sections.begin(), - From.Sections.end()); - Into.Size += From.Size; - Into.Weight += From.Weight; - From.Sections.clear(); - From.Size = 0; - From.Weight = 0; +static void mergeClusters(Cluster &into, Cluster &from) { + into.sections.insert(into.sections.end(), from.sections.begin(), + from.sections.end()); + into.size += from.size; + into.weight += from.weight; + from.sections.clear(); + from.size = 0; + from.weight = 0; } // Group InputSections into clusters using the Call-Chain Clustering heuristic // then sort the clusters by density. void CallGraphSort::groupClusters() { - std::vector SortedSecs(Clusters.size()); - std::vector SecToCluster(Clusters.size()); + std::vector sortedSecs(clusters.size()); + std::vector secToCluster(clusters.size()); - for (size_t I = 0; I < Clusters.size(); ++I) { - SortedSecs[I] = I; - SecToCluster[I] = &Clusters[I]; + for (size_t i = 0; i < clusters.size(); ++i) { + sortedSecs[i] = i; + secToCluster[i] = &clusters[i]; } - std::stable_sort(SortedSecs.begin(), SortedSecs.end(), [&](int A, int B) { - return Clusters[B].getDensity() < Clusters[A].getDensity(); + llvm::stable_sort(sortedSecs, [&](int a, int b) { + return clusters[a].getDensity() > clusters[b].getDensity(); }); - for (int SI : SortedSecs) { - // Clusters[SI] is the same as SecToClusters[SI] here because it has not + for (int si : sortedSecs) { + // clusters[si] is the same as secToClusters[si] here because it has not // been merged into another cluster yet. - Cluster &C = Clusters[SI]; + Cluster &c = clusters[si]; // Don't consider merging if the edge is unlikely. - if (C.BestPred.From == -1 || C.BestPred.Weight * 10 <= C.InitialWeight) + if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight) continue; - Cluster *PredC = SecToCluster[C.BestPred.From]; - if (PredC == &C) + Cluster *predC = secToCluster[c.bestPred.from]; + if (predC == &c) continue; - if (C.Size + PredC->Size > MAX_CLUSTER_SIZE) + if (c.size + predC->size > MAX_CLUSTER_SIZE) continue; - if (isNewDensityBad(*PredC, C)) + if (isNewDensityBad(*predC, c)) continue; // NOTE: Consider using a disjoint-set to track section -> cluster mapping // if this is ever slow. - for (int SI : C.Sections) - SecToCluster[SI] = PredC; + for (int si : c.sections) + secToCluster[si] = predC; - mergeClusters(*PredC, C); + mergeClusters(*predC, c); } // Remove empty or dead nodes. Invalidates all cluster indices. - llvm::erase_if(Clusters, [](const Cluster &C) { - return C.Size == 0 || C.Sections.empty(); + llvm::erase_if(clusters, [](const Cluster &c) { + return c.size == 0 || c.sections.empty(); }); // Sort by density. - std::stable_sort(Clusters.begin(), Clusters.end(), - [](const Cluster &A, const Cluster &B) { - return A.getDensity() > B.getDensity(); - }); + llvm::stable_sort(clusters, [](const Cluster &a, const Cluster &b) { + return a.getDensity() > b.getDensity(); + }); } DenseMap CallGraphSort::run() { groupClusters(); // Generate order. - DenseMap OrderMap; - ssize_t CurOrder = 1; + DenseMap orderMap; + ssize_t curOrder = 1; + + for (const Cluster &c : clusters) + for (int secIndex : c.sections) + orderMap[sections[secIndex]] = curOrder++; + + if (!config->printSymbolOrder.empty()) { + std::error_code ec; + raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::F_None); + if (ec) { + error("cannot open " + config->printSymbolOrder + ": " + ec.message()); + return orderMap; + } - for (const Cluster &C : Clusters) - for (int SecIndex : C.Sections) - OrderMap[Sections[SecIndex]] = CurOrder++; + // Print the symbols ordered by C3, in the order of increasing curOrder + // Instead of sorting all the orderMap, just repeat the loops above. + for (const Cluster &c : clusters) + for (int secIndex : c.sections) + // Search all the symbols in the file of the section + // and find out a Defined symbol with name that is within the section. + for (Symbol *sym: sections[secIndex]->file->getSymbols()) + if (!sym->isSection()) // Filter out section-type symbols here. + if (auto *d = dyn_cast(sym)) + if (sections[secIndex] == d->section) + os << sym->getName() << "\n"; + } - return OrderMap; + return orderMap; } // Sort sections by the profile data provided by -callgraph-profile-file diff --git a/ELF/CallGraphSort.h b/ELF/CallGraphSort.h index 3f96dc8..5a09227 100644 --- a/ELF/CallGraphSort.h +++ b/ELF/CallGraphSort.h @@ -1,9 +1,8 @@ //===- CallGraphSort.h ------------------------------------------*- C++ -*-===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/ELF/Config.h b/ELF/Config.h index 8fb760e..ff9d3dc 100644 --- a/ELF/Config.h +++ b/ELF/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 // //===----------------------------------------------------------------------===// @@ -18,6 +17,7 @@ #include "llvm/Support/CachePruning.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Endian.h" +#include #include namespace lld { @@ -62,18 +62,17 @@ enum class Target2Policy { Abs, Rel, GotRel }; enum class ARMVFPArgKind { Default, Base, VFP, ToolChain }; struct SymbolVersion { - llvm::StringRef Name; - bool IsExternCpp; - bool HasWildcard; + llvm::StringRef name; + bool isExternCpp; + bool hasWildcard; }; // This struct contains symbols version definition that // can be found in version script if it is used for link. struct VersionDefinition { - llvm::StringRef Name; - uint16_t Id = 0; - std::vector Globals; - size_t NameOff = 0; // Offset in the string table + llvm::StringRef name; + uint16_t id = 0; + std::vector globals; }; // This struct contains the global configuration for the linker. @@ -81,162 +80,177 @@ struct VersionDefinition { // and such fields have the same name as the corresponding options. // Most fields are initialized by the driver. struct Configuration { - uint8_t OSABI = 0; - llvm::CachePruningPolicy ThinLTOCachePolicy; - llvm::StringMap SectionStartMap; - llvm::StringRef Chroot; - llvm::StringRef DynamicLinker; - llvm::StringRef DwoDir; - llvm::StringRef Entry; - llvm::StringRef Emulation; - llvm::StringRef Fini; - llvm::StringRef Init; - llvm::StringRef LTOAAPipeline; - llvm::StringRef LTONewPmPasses; - llvm::StringRef LTOObjPath; - llvm::StringRef LTOSampleProfile; - llvm::StringRef MapFile; - llvm::StringRef OutputFile; - llvm::StringRef OptRemarksFilename; - llvm::StringRef ProgName; - llvm::StringRef SoName; - llvm::StringRef Sysroot; - llvm::StringRef ThinLTOCacheDir; - llvm::StringRef ThinLTOIndexOnlyArg; - std::pair ThinLTOObjectSuffixReplace; - std::pair ThinLTOPrefixReplace; - std::string Rpath; - std::vector VersionDefinitions; - std::vector AuxiliaryList; - std::vector FilterList; - std::vector SearchPaths; - std::vector SymbolOrderingFile; - std::vector Undefined; - std::vector DynamicList; - std::vector VersionScriptGlobals; - std::vector VersionScriptLocals; - std::vector BuildIdVector; + uint8_t osabi = 0; + uint32_t andFeatures = 0; + llvm::CachePruningPolicy thinLTOCachePolicy; + llvm::StringMap sectionStartMap; + llvm::StringRef chroot; + llvm::StringRef dynamicLinker; + llvm::StringRef dwoDir; + llvm::StringRef entry; + llvm::StringRef emulation; + llvm::StringRef fini; + llvm::StringRef init; + llvm::StringRef ltoAAPipeline; + llvm::StringRef ltoCSProfileFile; + llvm::StringRef ltoNewPmPasses; + llvm::StringRef ltoObjPath; + llvm::StringRef ltoSampleProfile; + llvm::StringRef mapFile; + llvm::StringRef outputFile; + llvm::StringRef optRemarksFilename; + llvm::StringRef optRemarksPasses; + llvm::StringRef optRemarksFormat; + llvm::StringRef progName; + llvm::StringRef printSymbolOrder; + llvm::StringRef soName; + llvm::StringRef sysroot; + llvm::StringRef thinLTOCacheDir; + llvm::StringRef thinLTOIndexOnlyArg; + std::pair thinLTOObjectSuffixReplace; + std::pair thinLTOPrefixReplace; + std::string rpath; + std::vector versionDefinitions; + std::vector auxiliaryList; + std::vector filterList; + std::vector searchPaths; + std::vector symbolOrderingFile; + std::vector undefined; + std::vector dynamicList; + std::vector versionScriptGlobals; + std::vector versionScriptLocals; + std::vector buildIdVector; llvm::MapVector, uint64_t> - CallGraphProfile; - bool AllowMultipleDefinition; - bool AndroidPackDynRelocs; - bool ARMHasBlx = false; - bool ARMHasMovtMovw = false; - bool ARMJ1J2BranchEncoding = false; - bool AsNeeded = false; - bool Bsymbolic; - bool BsymbolicFunctions; - bool CallGraphProfileSort; - bool CheckSections; - bool CompressDebugSections; - bool Cref; - bool DefineCommon; - bool Demangle = true; - bool DisableVerify; - bool EhFrameHdr; - bool EmitLLVM; - bool EmitRelocs; - bool EnableNewDtags; - bool ExecuteOnly; - bool ExportDynamic; - bool FixCortexA53Errata843419; - bool FormatBinary = false; - bool GcSections; - bool GdbIndex; - bool GnuHash = false; - bool GnuUnique; - bool HasDynamicList = false; - bool HasDynSymTab; - bool IgnoreDataAddressEquality; - bool IgnoreFunctionAddressEquality; - bool LTODebugPassManager; - bool LTONewPassManager; - bool MergeArmExidx; - bool MipsN32Abi = false; - bool NoinhibitExec; - bool Nostdlib; - bool OFormatBinary; - bool Omagic; - bool OptRemarksWithHotness; - bool Pie; - bool PrintGcSections; - bool PrintIcfSections; - bool Relocatable; - bool RelrPackDynRelocs; - bool SaveTemps; - bool SingleRoRx; - bool Shared; - bool Static = false; - bool SysvHash = false; - bool Target1Rel; - bool Trace; - bool ThinLTOEmitImportsFiles; - bool ThinLTOIndexOnly; - bool TocOptimize; - bool UndefinedVersion; - bool UseAndroidRelrTags = false; - bool WarnBackrefs; - bool WarnCommon; - bool WarnIfuncTextrel; - bool WarnMissingEntry; - bool WarnSymbolOrdering; - bool WriteAddends; - bool ZCombreloc; - bool ZCopyreloc; - bool ZExecstack; - bool ZGlobal; - bool ZHazardplt; - bool ZInitfirst; - bool ZInterpose; - bool ZKeepTextSectionPrefix; - bool ZNodefaultlib; - bool ZNodelete; - bool ZNodlopen; - bool ZNow; - bool ZOrigin; - bool ZRelro; - bool ZRodynamic; - bool ZText; - bool ZRetpolineplt; - bool ZWxneeded; - DiscardPolicy Discard; - ICFLevel ICF; - OrphanHandlingPolicy OrphanHandling; - SortSectionPolicy SortSection; - StripPolicy Strip; - UnresolvedPolicy UnresolvedSymbols; - Target2Policy Target2; - ARMVFPArgKind ARMVFPArgs = ARMVFPArgKind::Default; - BuildIdKind BuildId = BuildIdKind::None; - ELFKind EKind = ELFNoneKind; - uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL; - uint16_t EMachine = llvm::ELF::EM_NONE; - llvm::Optional ImageBase; - uint64_t MaxPageSize; - uint64_t MipsGotSize; - uint64_t ZStackSize; - unsigned LTOPartitions; - unsigned LTOO; - unsigned Optimize; - unsigned ThinLTOJobs; - int32_t SplitStackAdjustSize; + callGraphProfile; + bool allowMultipleDefinition; + bool allowShlibUndefined; + bool androidPackDynRelocs; + bool armHasBlx = false; + bool armHasMovtMovw = false; + bool armJ1J2BranchEncoding = false; + bool asNeeded = false; + bool bsymbolic; + bool bsymbolicFunctions; + bool callGraphProfileSort; + bool checkSections; + bool compressDebugSections; + bool cref; + bool defineCommon; + bool demangle = true; + bool dependentLibraries; + bool disableVerify; + bool ehFrameHdr; + bool emitLLVM; + bool emitRelocs; + bool enableNewDtags; + bool executeOnly; + bool exportDynamic; + bool fixCortexA53Errata843419; + bool forceBTI; + bool formatBinary = false; + bool requireCET; + bool gcSections; + bool gdbIndex; + bool gnuHash = false; + bool gnuUnique; + bool hasDynamicList = false; + bool hasDynSymTab; + bool ignoreDataAddressEquality; + bool ignoreFunctionAddressEquality; + bool ltoCSProfileGenerate; + bool ltoDebugPassManager; + bool ltoNewPassManager; + bool mergeArmExidx; + bool mipsN32Abi = false; + bool nmagic; + bool noinhibitExec; + bool nostdlib; + bool oFormatBinary; + bool omagic; + bool optRemarksWithHotness; + bool pacPlt; + bool picThunk; + bool pie; + bool printGcSections; + bool printIcfSections; + bool relocatable; + bool relrPackDynRelocs; + bool saveTemps; + bool singleRoRx; + bool shared; + bool isStatic = false; + bool sysvHash = false; + bool target1Rel; + bool trace; + bool thinLTOEmitImportsFiles; + bool thinLTOIndexOnly; + bool tocOptimize; + bool undefinedVersion; + bool useAndroidRelrTags = false; + bool warnBackrefs; + bool warnCommon; + bool warnIfuncTextrel; + bool warnMissingEntry; + bool warnSymbolOrdering; + bool writeAddends; + bool zCombreloc; + bool zCopyreloc; + bool zExecstack; + bool zGlobal; + bool zHazardplt; + bool zIfuncNoplt; + bool zInitfirst; + bool zInterpose; + bool zKeepTextSectionPrefix; + bool zNodefaultlib; + bool zNodelete; + bool zNodlopen; + bool zNow; + bool zOrigin; + bool zRelro; + bool zRodynamic; + bool zText; + bool zRetpolineplt; + bool zWxneeded; + DiscardPolicy discard; + ICFLevel icf; + OrphanHandlingPolicy orphanHandling; + SortSectionPolicy sortSection; + StripPolicy strip; + UnresolvedPolicy unresolvedSymbols; + Target2Policy target2; + ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default; + BuildIdKind buildId = BuildIdKind::None; + ELFKind ekind = ELFNoneKind; + uint16_t defaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL; + uint16_t emachine = llvm::ELF::EM_NONE; + llvm::Optional imageBase; + uint64_t commonPageSize; + uint64_t maxPageSize; + uint64_t mipsGotSize; + uint64_t zStackSize; + unsigned ltoPartitions; + unsigned ltoo; + unsigned optimize; + unsigned thinLTOJobs; + int32_t splitStackAdjustSize; // The following config options do not directly correspond to any // particualr command line options. // True if we need to pass through relocations in input files to the // output file. Usually false because we consume relocations. - bool CopyRelocs; + bool copyRelocs; // True if the target is ELF64. False if ELF32. - bool Is64; + bool is64; // True if the target is little-endian. False if big-endian. - bool IsLE; + bool isLE; - // endianness::little if IsLE is true. endianness::big otherwise. - llvm::support::endianness Endianness; + // endianness::little if isLE is true. endianness::big otherwise. + llvm::support::endianness endianness; // True if the target is the little-endian MIPS64. // @@ -250,10 +264,24 @@ struct Configuration { // name whatever that means. A fun hypothesis is that "EL" is short for // little-endian written in the little-endian order, but I don't know // if that's true.) - bool IsMips64EL; + bool isMips64EL; + + // True if we need to set the DF_STATIC_TLS flag to an output file, + // which works as a hint to the dynamic loader that the file contains + // code compiled with the static TLS model. The thread-local variable + // compiled with the static TLS model is faster but less flexible, and + // it may not be loaded using dlopen(). + // + // We set this flag to true when we see a relocation for the static TLS + // model. Once this becomes true, it will never become false. + // + // Since the flag is updated by multi-threaded code, we use std::atomic. + // (Writing to a variable is not considered thread-safe even if the + // variable is boolean and we always set the same value from all threads.) + std::atomic hasStaticTlsModel{false}; // Holds set of ELF header flags for the target. - uint32_t EFlags = 0; + uint32_t eflags = 0; // The ELF spec defines two types of relocation table entries, RELA and // REL. RELA is a triplet of (offset, info, addend) while REL is a @@ -269,23 +297,23 @@ struct Configuration { // Each ABI defines its relocation type. IsRela is true if target // uses RELA. As far as we know, all 64-bit ABIs are using RELA. A // few 32-bit ABIs are using RELA too. - bool IsRela; + bool isRela; // True if we are creating position-independent code. - bool Pic; + bool isPic; // 4 for ELF32, 8 for ELF64. - int Wordsize; + int wordsize; }; // The only instance of Configuration struct. -extern Configuration *Config; +extern Configuration *config; -static inline void errorOrWarn(const Twine &Msg) { - if (!Config->NoinhibitExec) - error(Msg); +static inline void errorOrWarn(const Twine &msg) { + if (!config->noinhibitExec) + error(msg); else - warn(Msg); + warn(msg); } } // namespace elf } // namespace lld diff --git a/ELF/DWARF.cpp b/ELF/DWARF.cpp index 17e1a4d..1e4b36f 100644 --- a/ELF/DWARF.cpp +++ b/ELF/DWARF.cpp @@ -1,9 +1,8 @@ //===- DWARF.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 // //===----------------------------------------------------------------------===// // @@ -26,81 +25,102 @@ using namespace llvm::object; using namespace lld; using namespace lld::elf; -template LLDDwarfObj::LLDDwarfObj(ObjFile *Obj) { - for (InputSectionBase *Sec : Obj->getSections()) { - if (!Sec) +template LLDDwarfObj::LLDDwarfObj(ObjFile *obj) { + for (InputSectionBase *sec : obj->getSections()) { + if (!sec) continue; - if (LLDDWARFSection *M = - StringSwitch(Sec->Name) - .Case(".debug_addr", &AddrSection) - .Case(".debug_gnu_pubnames", &GnuPubNamesSection) - .Case(".debug_gnu_pubtypes", &GnuPubTypesSection) - .Case(".debug_info", &InfoSection) - .Case(".debug_ranges", &RangeSection) - .Case(".debug_rnglists", &RngListsSection) - .Case(".debug_line", &LineSection) + if (LLDDWARFSection *m = + StringSwitch(sec->name) + .Case(".debug_addr", &addrSection) + .Case(".debug_gnu_pubnames", &gnuPubNamesSection) + .Case(".debug_gnu_pubtypes", &gnuPubTypesSection) + .Case(".debug_info", &infoSection) + .Case(".debug_ranges", &rangeSection) + .Case(".debug_rnglists", &rngListsSection) + .Case(".debug_line", &lineSection) .Default(nullptr)) { - M->Data = toStringRef(Sec->data()); - M->Sec = Sec; + m->Data = toStringRef(sec->data()); + m->sec = sec; continue; } - if (Sec->Name == ".debug_abbrev") - AbbrevSection = toStringRef(Sec->data()); - else if (Sec->Name == ".debug_str") - StrSection = toStringRef(Sec->data()); - else if (Sec->Name == ".debug_line_str") - LineStringSection = toStringRef(Sec->data()); + if (sec->name == ".debug_abbrev") + abbrevSection = toStringRef(sec->data()); + else if (sec->name == ".debug_str") + strSection = toStringRef(sec->data()); + else if (sec->name == ".debug_line_str") + lineStringSection = toStringRef(sec->data()); } } +namespace { +template struct LLDRelocationResolver { + // In the ELF ABIs, S sepresents the value of the symbol in the relocation + // entry. For Rela, the addend is stored as part of the relocation entry. + static uint64_t resolve(object::RelocationRef ref, uint64_t s, + uint64_t /* A */) { + return s + ref.getRawDataRefImpl().p; + } +}; + +template struct LLDRelocationResolver> { + // For Rel, the addend A is supplied by the caller. + static uint64_t resolve(object::RelocationRef /*Ref*/, uint64_t s, + uint64_t a) { + return s + a; + } +}; +} // namespace + // Find if there is a relocation at Pos in Sec. The code is a bit // more complicated than usual because we need to pass a section index // to llvm since it has no idea about InputSection. template template Optional -LLDDwarfObj::findAux(const InputSectionBase &Sec, uint64_t Pos, - ArrayRef Rels) const { - auto It = std::lower_bound( - Rels.begin(), Rels.end(), Pos, - [](const RelTy &A, uint64_t B) { return A.r_offset < B; }); - if (It == Rels.end() || It->r_offset != Pos) +LLDDwarfObj::findAux(const InputSectionBase &sec, uint64_t pos, + ArrayRef rels) const { + auto it = + partition_point(rels, [=](const RelTy &a) { return a.r_offset < pos; }); + if (it == rels.end() || it->r_offset != pos) return None; - const RelTy &Rel = *It; + const RelTy &rel = *it; - const ObjFile *File = Sec.getFile(); - uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL); - const typename ELFT::Sym &Sym = File->getELFSyms()[SymIndex]; - uint32_t SecIndex = File->getSectionIndex(Sym); + const ObjFile *file = sec.getFile(); + uint32_t symIndex = rel.getSymbol(config->isMips64EL); + const typename ELFT::Sym &sym = file->template getELFSyms()[symIndex]; + uint32_t secIndex = file->getSectionIndex(sym); - // Broken debug info can point to a non-Defined symbol. - auto *DR = dyn_cast(&File->getRelocTargetSym(Rel)); - if (!DR) { - RelType Type = Rel.getType(Config->IsMips64EL); - if (Type != Target->NoneRel) - error(toString(File) + ": relocation " + lld::toString(Type) + " at 0x" + - llvm::utohexstr(Rel.r_offset) + " has unsupported target"); - return None; - } - uint64_t Val = DR->Value + getAddend(Rel); + // An undefined symbol may be a symbol defined in a discarded section. We + // shall still resolve it. This is important for --gdb-index: the end address + // offset of an entry in .debug_ranges is relocated. If it is not resolved, + // its zero value will terminate the decoding of .debug_ranges prematurely. + Symbol &s = file->getRelocTargetSym(rel); + uint64_t val = 0; + if (auto *dr = dyn_cast(&s)) { + val = dr->value; - // FIXME: We should be consistent about always adding the file - // offset or not. - if (DR->Section->Flags & ELF::SHF_ALLOC) - Val += cast(DR->Section)->getOffsetInFile(); + // FIXME: We should be consistent about always adding the file + // offset or not. + if (dr->section->flags & ELF::SHF_ALLOC) + val += cast(dr->section)->getOffsetInFile(); + } - return RelocAddrEntry{SecIndex, Val}; + DataRefImpl d; + d.p = getAddend(rel); + return RelocAddrEntry{secIndex, RelocationRef(d, nullptr), + val, Optional(), + 0, LLDRelocationResolver::resolve}; } template -Optional LLDDwarfObj::find(const llvm::DWARFSection &S, - uint64_t Pos) const { - auto &Sec = static_cast(S); - if (Sec.Sec->AreRelocsRela) - return findAux(*Sec.Sec, Pos, Sec.Sec->template relas()); - return findAux(*Sec.Sec, Pos, Sec.Sec->template rels()); +Optional LLDDwarfObj::find(const llvm::DWARFSection &s, + uint64_t pos) const { + auto &sec = static_cast(s); + if (sec.sec->areRelocsRela) + return findAux(*sec.sec, pos, sec.sec->template relas()); + return findAux(*sec.sec, pos, sec.sec->template rels()); } template class elf::LLDDwarfObj; diff --git a/ELF/DWARF.h b/ELF/DWARF.h index 8ecf02c..4260229 100644 --- a/ELF/DWARF.h +++ b/ELF/DWARF.h @@ -1,9 +1,8 @@ //===- DWARF.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 // //===-------------------------------------------------------------------===// @@ -21,70 +20,70 @@ namespace elf { class InputSection; struct LLDDWARFSection final : public llvm::DWARFSection { - InputSectionBase *Sec = nullptr; + InputSectionBase *sec = nullptr; }; template class LLDDwarfObj final : public llvm::DWARFObject { public: - explicit LLDDwarfObj(ObjFile *Obj); + explicit LLDDwarfObj(ObjFile *obj); void forEachInfoSections( - llvm::function_ref F) const override { - F(InfoSection); + llvm::function_ref f) const override { + f(infoSection); } const llvm::DWARFSection &getRangeSection() const override { - return RangeSection; + return rangeSection; } const llvm::DWARFSection &getRnglistsSection() const override { - return RngListsSection; + return rngListsSection; } const llvm::DWARFSection &getLineSection() const override { - return LineSection; + return lineSection; } const llvm::DWARFSection &getAddrSection() const override { - return AddrSection; + return addrSection; } const llvm::DWARFSection &getGnuPubNamesSection() const override { - return GnuPubNamesSection; + return gnuPubNamesSection; } const llvm::DWARFSection &getGnuPubTypesSection() const override { - return GnuPubTypesSection; + return gnuPubTypesSection; } StringRef getFileName() const override { return ""; } - StringRef getAbbrevSection() const override { return AbbrevSection; } - StringRef getStringSection() const override { return StrSection; } - StringRef getLineStringSection() const override { return LineStringSection; } + StringRef getAbbrevSection() const override { return abbrevSection; } + StringRef getStringSection() const override { return strSection; } + StringRef getLineStringSection() const override { return lineStringSection; } bool isLittleEndian() const override { return ELFT::TargetEndianness == llvm::support::little; } - llvm::Optional find(const llvm::DWARFSection &Sec, - uint64_t Pos) const override; + llvm::Optional find(const llvm::DWARFSection &sec, + uint64_t pos) const override; private: template - llvm::Optional findAux(const InputSectionBase &Sec, - uint64_t Pos, - ArrayRef Rels) const; - - LLDDWARFSection GnuPubNamesSection; - LLDDWARFSection GnuPubTypesSection; - LLDDWARFSection InfoSection; - LLDDWARFSection RangeSection; - LLDDWARFSection RngListsSection; - LLDDWARFSection LineSection; - LLDDWARFSection AddrSection; - StringRef AbbrevSection; - StringRef StrSection; - StringRef LineStringSection; + llvm::Optional findAux(const InputSectionBase &sec, + uint64_t pos, + ArrayRef rels) const; + + LLDDWARFSection gnuPubNamesSection; + LLDDWARFSection gnuPubTypesSection; + LLDDWARFSection infoSection; + LLDDWARFSection rangeSection; + LLDDWARFSection rngListsSection; + LLDDWARFSection lineSection; + LLDDWARFSection addrSection; + StringRef abbrevSection; + StringRef strSection; + StringRef lineStringSection; }; } // namespace elf diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp index 13b6119..fbfc71d 100644 --- a/ELF/Driver.cpp +++ b/ELF/Driver.cpp @@ -1,9 +1,8 @@ //===- 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 // //===----------------------------------------------------------------------===// // @@ -25,7 +24,6 @@ #include "Driver.h" #include "Config.h" -#include "Filesystem.h" #include "ICF.h" #include "InputFiles.h" #include "InputSection.h" @@ -41,6 +39,7 @@ #include "lld/Common/Args.h" #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" +#include "lld/Common/Filesystem.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "lld/Common/TargetOptionsCommandFlags.h" @@ -51,6 +50,7 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" +#include "llvm/Support/GlobPattern.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" @@ -68,44 +68,49 @@ using namespace llvm::support; using namespace lld; using namespace lld::elf; -Configuration *elf::Config; -LinkerDriver *elf::Driver; +Configuration *elf::config; +LinkerDriver *elf::driver; -static void setConfigs(opt::InputArgList &Args); +static void setConfigs(opt::InputArgList &args); +static void readConfigs(opt::InputArgList &args); -bool elf::link(ArrayRef Args, bool CanExitEarly, - raw_ostream &Error) { - errorHandler().LogName = args::getFilenameWithoutExe(Args[0]); - errorHandler().ErrorLimitExceededMsg = +bool elf::link(ArrayRef args, bool canExitEarly, + raw_ostream &error) { + errorHandler().logName = args::getFilenameWithoutExe(args[0]); + errorHandler().errorLimitExceededMsg = "too many errors emitted, stopping now (use " "-error-limit=0 to see all errors)"; - errorHandler().ErrorOS = &Error; - errorHandler().ExitEarly = CanExitEarly; - errorHandler().ColorDiagnostics = Error.has_colors(); + errorHandler().errorOS = &error; + errorHandler().exitEarly = canExitEarly; + errorHandler().colorDiagnostics = error.has_colors(); + + inputSections.clear(); + outputSections.clear(); + binaryFiles.clear(); + bitcodeFiles.clear(); + objectFiles.clear(); + sharedFiles.clear(); + + config = make(); + driver = make(); + script = make(); + symtab = make(); - InputSections.clear(); - OutputSections.clear(); - BinaryFiles.clear(); - BitcodeFiles.clear(); - ObjectFiles.clear(); - SharedFiles.clear(); + tar = nullptr; + memset(&in, 0, sizeof(in)); - Config = make(); - Driver = make(); - Script = make(); - Symtab = make(); + partitions = {Partition()}; - Tar = nullptr; - memset(&In, 0, sizeof(In)); + SharedFile::vernauxNum = 0; - Config->ProgName = Args[0]; + config->progName = args[0]; - Driver->main(Args); + driver->main(args); // Exit immediately if we don't need to return to the caller. // This saves time because the overhead of calling destructors // for all globally-allocated objects is not negligible. - if (CanExitEarly) + if (canExitEarly) exitLld(errorCount() ? 1 : 0); freeArena(); @@ -113,16 +118,16 @@ bool elf::link(ArrayRef Args, bool CanExitEarly, } // Parses a linker -m option. -static std::tuple parseEmulation(StringRef Emul) { - uint8_t OSABI = 0; - StringRef S = Emul; - if (S.endswith("_fbsd")) { - S = S.drop_back(5); - OSABI = ELFOSABI_FREEBSD; +static std::tuple parseEmulation(StringRef emul) { + uint8_t osabi = 0; + StringRef s = emul; + if (s.endswith("_fbsd")) { + s = s.drop_back(5); + osabi = ELFOSABI_FREEBSD; } - std::pair Ret = - StringSwitch>(S) + std::pair ret = + StringSwitch>(s) .Cases("aarch64elf", "aarch64linux", "aarch64_elf64_le_vec", {ELF64LEKind, EM_AARCH64}) .Cases("armelf", "armelf_linux_eabi", {ELF32LEKind, EM_ARM}) @@ -130,7 +135,7 @@ static std::tuple parseEmulation(StringRef Emul) { .Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS}) .Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS}) .Case("elf32lriscv", {ELF32LEKind, EM_RISCV}) - .Case("elf32ppc", {ELF32BEKind, EM_PPC}) + .Cases("elf32ppc", "elf32ppclinux", {ELF32BEKind, EM_PPC}) .Case("elf64btsmip", {ELF64BEKind, EM_MIPS}) .Case("elf64ltsmip", {ELF64LEKind, EM_MIPS}) .Case("elf64lriscv", {ELF64LEKind, EM_RISCV}) @@ -141,91 +146,101 @@ static std::tuple parseEmulation(StringRef Emul) { .Case("elf_iamcu", {ELF32LEKind, EM_IAMCU}) .Default({ELFNoneKind, EM_NONE}); - if (Ret.first == ELFNoneKind) - error("unknown emulation: " + Emul); - return std::make_tuple(Ret.first, Ret.second, OSABI); + if (ret.first == ELFNoneKind) + error("unknown emulation: " + emul); + return std::make_tuple(ret.first, ret.second, osabi); } // Returns slices of MB by parsing MB as an archive file. // Each slice consists of a member file in the archive. std::vector> static getArchiveMembers( - MemoryBufferRef MB) { - std::unique_ptr File = - CHECK(Archive::create(MB), - MB.getBufferIdentifier() + ": failed to parse archive"); - - std::vector> V; - Error Err = Error::success(); - bool AddToTar = File->isThin() && Tar; - for (const ErrorOr &COrErr : File->children(Err)) { - Archive::Child C = - CHECK(COrErr, MB.getBufferIdentifier() + + MemoryBufferRef mb) { + std::unique_ptr file = + CHECK(Archive::create(mb), + mb.getBufferIdentifier() + ": failed to parse archive"); + + std::vector> v; + Error err = Error::success(); + bool addToTar = file->isThin() && tar; + for (const ErrorOr &cOrErr : file->children(err)) { + Archive::Child c = + CHECK(cOrErr, mb.getBufferIdentifier() + ": could not get the child of the archive"); - MemoryBufferRef MBRef = - CHECK(C.getMemoryBufferRef(), - MB.getBufferIdentifier() + + MemoryBufferRef mbref = + CHECK(c.getMemoryBufferRef(), + mb.getBufferIdentifier() + ": could not get the buffer for a child of the archive"); - if (AddToTar) - Tar->append(relativeToRoot(check(C.getFullName())), MBRef.getBuffer()); - V.push_back(std::make_pair(MBRef, C.getChildOffset())); + if (addToTar) + tar->append(relativeToRoot(check(c.getFullName())), mbref.getBuffer()); + v.push_back(std::make_pair(mbref, c.getChildOffset())); } - if (Err) - fatal(MB.getBufferIdentifier() + ": Archive::children failed: " + - toString(std::move(Err))); + if (err) + fatal(mb.getBufferIdentifier() + ": Archive::children failed: " + + toString(std::move(err))); // Take ownership of memory buffers created for members of thin archives. - for (std::unique_ptr &MB : File->takeThinBuffers()) - make>(std::move(MB)); + for (std::unique_ptr &mb : file->takeThinBuffers()) + make>(std::move(mb)); - return V; + return v; } // Opens a file and create a file object. Path has to be resolved already. -void LinkerDriver::addFile(StringRef Path, bool WithLOption) { +void LinkerDriver::addFile(StringRef path, bool withLOption) { using namespace sys::fs; - Optional Buffer = readFile(Path); - if (!Buffer.hasValue()) + Optional buffer = readFile(path); + if (!buffer.hasValue()) return; - MemoryBufferRef MBRef = *Buffer; + MemoryBufferRef mbref = *buffer; - if (Config->FormatBinary) { - Files.push_back(make(MBRef)); + if (config->formatBinary) { + files.push_back(make(mbref)); return; } - switch (identify_magic(MBRef.getBuffer())) { + switch (identify_magic(mbref.getBuffer())) { case file_magic::unknown: - readLinkerScript(MBRef); + readLinkerScript(mbref); return; case file_magic::archive: { // Handle -whole-archive. - if (InWholeArchive) { - for (const auto &P : getArchiveMembers(MBRef)) - Files.push_back(createObjectFile(P.first, Path, P.second)); + if (inWholeArchive) { + for (const auto &p : getArchiveMembers(mbref)) + files.push_back(createObjectFile(p.first, path, p.second)); return; } - std::unique_ptr File = - CHECK(Archive::create(MBRef), Path + ": failed to parse archive"); + std::unique_ptr file = + CHECK(Archive::create(mbref), path + ": failed to parse archive"); // If an archive file has no symbol table, it is likely that a user // is attempting LTO and using a default ar command that doesn't // understand the LLVM bitcode file. It is a pretty common error, so // we'll handle it as if it had a symbol table. - if (!File->isEmpty() && !File->hasSymbolTable()) { - for (const auto &P : getArchiveMembers(MBRef)) - Files.push_back(make(P.first, Path, P.second)); + if (!file->isEmpty() && !file->hasSymbolTable()) { + // Check if all members are bitcode files. If not, ignore, which is the + // default action without the LTO hack described above. + for (const std::pair &p : + getArchiveMembers(mbref)) + if (identify_magic(p.first.getBuffer()) != file_magic::bitcode) { + error(path + ": archive has no index; run ranlib to add one"); + return; + } + + for (const std::pair &p : + getArchiveMembers(mbref)) + files.push_back(make(p.first, path, p.second)); return; } // Handle the regular case. - Files.push_back(make(std::move(File))); + files.push_back(make(std::move(file))); return; } case file_magic::elf_shared_object: - if (Config->Static || Config->Relocatable) { - error("attempted static link of dynamic object " + Path); + if (config->isStatic || config->relocatable) { + error("attempted static link of dynamic object " + path); return; } @@ -239,27 +254,27 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) { // If a file was specified by -lfoo, the directory part is not // significant, as a user did not specify it. This behavior is // compatible with GNU. - Files.push_back( - createSharedFile(MBRef, WithLOption ? path::filename(Path) : Path)); + files.push_back( + make(mbref, withLOption ? path::filename(path) : path)); return; case file_magic::bitcode: case file_magic::elf_relocatable: - if (InLib) - Files.push_back(make(MBRef, "", 0)); + if (inLib) + files.push_back(make(mbref, "", 0)); else - Files.push_back(createObjectFile(MBRef)); + files.push_back(createObjectFile(mbref)); break; default: - error(Path + ": unknown file type"); + error(path + ": unknown file type"); } } // Add a given library by searching it from input search paths. -void LinkerDriver::addLibrary(StringRef Name) { - if (Optional Path = searchLibrary(Name)) - addFile(*Path, /*WithLOption=*/true); +void LinkerDriver::addLibrary(StringRef name) { + if (Optional path = searchLibrary(name)) + addFile(*path, /*withLOption=*/true); else - error("unable to find library -l" + Name); + error("unable to find library -l" + name); } // This function is called on startup. We need this for LTO since @@ -278,102 +293,117 @@ static void initLLVM() { static void checkOptions() { // The MIPS ABI as of 2016 does not support the GNU-style symbol lookup // table which is a relatively new feature. - if (Config->EMachine == EM_MIPS && Config->GnuHash) + if (config->emachine == EM_MIPS && config->gnuHash) error("the .gnu.hash section is not compatible with the MIPS target"); - if (Config->FixCortexA53Errata843419 && Config->EMachine != EM_AARCH64) + if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64) error("--fix-cortex-a53-843419 is only supported on AArch64 targets"); - if (Config->TocOptimize && Config->EMachine != EM_PPC64) + if (config->tocOptimize && config->emachine != EM_PPC64) error("--toc-optimize is only supported on the PowerPC64 target"); - if (Config->Pie && Config->Shared) + if (config->pie && config->shared) error("-shared and -pie may not be used together"); - if (!Config->Shared && !Config->FilterList.empty()) + if (!config->shared && !config->filterList.empty()) error("-F may not be used without -shared"); - if (!Config->Shared && !Config->AuxiliaryList.empty()) + if (!config->shared && !config->auxiliaryList.empty()) error("-f may not be used without -shared"); - if (!Config->Relocatable && !Config->DefineCommon) + if (!config->relocatable && !config->defineCommon) error("-no-define-common not supported in non relocatable output"); - if (Config->Relocatable) { - if (Config->Shared) + if (config->zText && config->zIfuncNoplt) + error("-z text and -z ifunc-noplt may not be used together"); + + if (config->relocatable) { + if (config->shared) error("-r and -shared may not be used together"); - if (Config->GcSections) + if (config->gcSections) error("-r and --gc-sections may not be used together"); - if (Config->GdbIndex) + if (config->gdbIndex) error("-r and --gdb-index may not be used together"); - if (Config->ICF != ICFLevel::None) + if (config->icf != ICFLevel::None) error("-r and --icf may not be used together"); - if (Config->Pie) + if (config->pie) error("-r and -pie may not be used together"); } - if (Config->ExecuteOnly) { - if (Config->EMachine != EM_AARCH64) + if (config->executeOnly) { + if (config->emachine != EM_AARCH64) error("-execute-only is only supported on AArch64 targets"); - if (Config->SingleRoRx && !Script->HasSectionsCommand) + if (config->singleRoRx && !script->hasSectionsCommand) error("-execute-only and -no-rosegment cannot be used together"); } + + if (config->zRetpolineplt && config->requireCET) + error("--require-cet may not be used with -z retpolineplt"); + + if (config->emachine != EM_AARCH64) { + if (config->pacPlt) + error("--pac-plt only supported on AArch64"); + if (config->forceBTI) + error("--force-bti only supported on AArch64"); + } } -static const char *getReproduceOption(opt::InputArgList &Args) { - if (auto *Arg = Args.getLastArg(OPT_reproduce)) - return Arg->getValue(); +static const char *getReproduceOption(opt::InputArgList &args) { + if (auto *arg = args.getLastArg(OPT_reproduce)) + return arg->getValue(); return getenv("LLD_REPRODUCE"); } -static bool hasZOption(opt::InputArgList &Args, StringRef Key) { - for (auto *Arg : Args.filtered(OPT_z)) - if (Key == Arg->getValue()) +static bool hasZOption(opt::InputArgList &args, StringRef key) { + for (auto *arg : args.filtered(OPT_z)) + if (key == arg->getValue()) return true; return false; } -static bool getZFlag(opt::InputArgList &Args, StringRef K1, StringRef K2, +static bool getZFlag(opt::InputArgList &args, StringRef k1, StringRef k2, bool Default) { - for (auto *Arg : Args.filtered_reverse(OPT_z)) { - if (K1 == Arg->getValue()) + for (auto *arg : args.filtered_reverse(OPT_z)) { + if (k1 == arg->getValue()) return true; - if (K2 == Arg->getValue()) + if (k2 == arg->getValue()) return false; } return Default; } -static bool isKnownZFlag(StringRef S) { - return S == "combreloc" || S == "copyreloc" || S == "defs" || - S == "execstack" || S == "global" || S == "hazardplt" || - S == "initfirst" || S == "interpose" || - S == "keep-text-section-prefix" || S == "lazy" || S == "muldefs" || - S == "nocombreloc" || S == "nocopyreloc" || S == "nodefaultlib" || - S == "nodelete" || S == "nodlopen" || S == "noexecstack" || - S == "nokeep-text-section-prefix" || S == "norelro" || S == "notext" || - S == "now" || S == "origin" || S == "relro" || S == "retpolineplt" || - S == "rodynamic" || S == "text" || S == "wxneeded" || - S.startswith("max-page-size=") || S.startswith("stack-size="); +static bool isKnownZFlag(StringRef s) { + return s == "combreloc" || s == "copyreloc" || s == "defs" || + s == "execstack" || s == "global" || s == "hazardplt" || + s == "ifunc-noplt" || s == "initfirst" || s == "interpose" || + s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" || + s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" || + s == "nodelete" || s == "nodlopen" || s == "noexecstack" || + s == "nokeep-text-section-prefix" || s == "norelro" || s == "notext" || + s == "now" || s == "origin" || s == "relro" || s == "retpolineplt" || + s == "rodynamic" || s == "text" || s == "wxneeded" || + s.startswith("common-page-size") || s.startswith("max-page-size=") || + s.startswith("stack-size="); } // Report an error for an unknown -z option. -static void checkZOptions(opt::InputArgList &Args) { - for (auto *Arg : Args.filtered(OPT_z)) - if (!isKnownZFlag(Arg->getValue())) - error("unknown -z value: " + StringRef(Arg->getValue())); +static void checkZOptions(opt::InputArgList &args) { + for (auto *arg : args.filtered(OPT_z)) + if (!isKnownZFlag(arg->getValue())) + error("unknown -z value: " + StringRef(arg->getValue())); } -void LinkerDriver::main(ArrayRef ArgsArr) { - ELFOptTable Parser; - opt::InputArgList Args = Parser.parse(ArgsArr.slice(1)); +void LinkerDriver::main(ArrayRef argsArr) { + ELFOptTable parser; + opt::InputArgList args = parser.parse(argsArr.slice(1)); // Interpret this flag early because error() depends on them. - errorHandler().ErrorLimit = args::getInteger(Args, OPT_error_limit, 20); + errorHandler().errorLimit = args::getInteger(args, OPT_error_limit, 20); + checkZOptions(args); // Handle -help - if (Args.hasArg(OPT_help)) { + if (args.hasArg(OPT_help)) { printHelp(); return; } @@ -393,213 +423,218 @@ void LinkerDriver::main(ArrayRef ArgsArr) { // lot of "configure" scripts out there that are generated by old version // of Libtool. We cannot convince every software developer to migrate to // the latest version and re-generate scripts. So we have this hack. - if (Args.hasArg(OPT_v) || Args.hasArg(OPT_version)) + if (args.hasArg(OPT_v) || args.hasArg(OPT_version)) message(getLLDVersion() + " (compatible with GNU linkers)"); - if (const char *Path = getReproduceOption(Args)) { + if (const char *path = getReproduceOption(args)) { // Note that --reproduce is a debug option so you can ignore it // if you are trying to understand the whole picture of the code. - Expected> ErrOrWriter = - TarWriter::create(Path, path::stem(Path)); - if (ErrOrWriter) { - Tar = std::move(*ErrOrWriter); - Tar->append("response.txt", createResponseFile(Args)); - Tar->append("version.txt", getLLDVersion() + "\n"); + Expected> errOrWriter = + TarWriter::create(path, path::stem(path)); + if (errOrWriter) { + tar = std::move(*errOrWriter); + tar->append("response.txt", createResponseFile(args)); + tar->append("version.txt", getLLDVersion() + "\n"); } else { - error("--reproduce: " + toString(ErrOrWriter.takeError())); + error("--reproduce: " + toString(errOrWriter.takeError())); } } - readConfigs(Args); - checkZOptions(Args); + readConfigs(args); // The behavior of -v or --version is a bit strange, but this is // needed for compatibility with GNU linkers. - if (Args.hasArg(OPT_v) && !Args.hasArg(OPT_INPUT)) + if (args.hasArg(OPT_v) && !args.hasArg(OPT_INPUT)) return; - if (Args.hasArg(OPT_version)) + if (args.hasArg(OPT_version)) return; initLLVM(); - createFiles(Args); + createFiles(args); if (errorCount()) return; inferMachineType(); - setConfigs(Args); + setConfigs(args); checkOptions(); if (errorCount()) return; - switch (Config->EKind) { + // The Target instance handles target-specific stuff, such as applying + // relocations or writing a PLT section. It also contains target-dependent + // values such as a default image base address. + target = getTarget(); + + switch (config->ekind) { case ELF32LEKind: - link(Args); + link(args); return; case ELF32BEKind: - link(Args); + link(args); return; case ELF64LEKind: - link(Args); + link(args); return; case ELF64BEKind: - link(Args); + link(args); return; default: llvm_unreachable("unknown Config->EKind"); } } -static std::string getRpath(opt::InputArgList &Args) { - std::vector V = args::getStrings(Args, OPT_rpath); - return llvm::join(V.begin(), V.end(), ":"); +static std::string getRpath(opt::InputArgList &args) { + std::vector v = args::getStrings(args, OPT_rpath); + return llvm::join(v.begin(), v.end(), ":"); } // Determines what we should do if there are remaining unresolved // symbols after the name resolution. -static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &Args) { - UnresolvedPolicy ErrorOrWarn = Args.hasFlag(OPT_error_unresolved_symbols, +static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) { + UnresolvedPolicy errorOrWarn = args.hasFlag(OPT_error_unresolved_symbols, OPT_warn_unresolved_symbols, true) ? UnresolvedPolicy::ReportError : UnresolvedPolicy::Warn; // Process the last of -unresolved-symbols, -no-undefined or -z defs. - for (auto *Arg : llvm::reverse(Args)) { - switch (Arg->getOption().getID()) { + for (auto *arg : llvm::reverse(args)) { + switch (arg->getOption().getID()) { case OPT_unresolved_symbols: { - StringRef S = Arg->getValue(); - if (S == "ignore-all" || S == "ignore-in-object-files") + StringRef s = arg->getValue(); + if (s == "ignore-all" || s == "ignore-in-object-files") return UnresolvedPolicy::Ignore; - if (S == "ignore-in-shared-libs" || S == "report-all") - return ErrorOrWarn; - error("unknown --unresolved-symbols value: " + S); + if (s == "ignore-in-shared-libs" || s == "report-all") + return errorOrWarn; + error("unknown --unresolved-symbols value: " + s); continue; } case OPT_no_undefined: - return ErrorOrWarn; + return errorOrWarn; case OPT_z: - if (StringRef(Arg->getValue()) == "defs") - return ErrorOrWarn; + if (StringRef(arg->getValue()) == "defs") + return errorOrWarn; continue; } } // -shared implies -unresolved-symbols=ignore-all because missing // symbols are likely to be resolved at runtime using other DSOs. - if (Config->Shared) + if (config->shared) return UnresolvedPolicy::Ignore; - return ErrorOrWarn; + return errorOrWarn; } -static Target2Policy getTarget2(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_target2, "got-rel"); - if (S == "rel") +static Target2Policy getTarget2(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_target2, "got-rel"); + if (s == "rel") return Target2Policy::Rel; - if (S == "abs") + if (s == "abs") return Target2Policy::Abs; - if (S == "got-rel") + if (s == "got-rel") return Target2Policy::GotRel; - error("unknown --target2 option: " + S); + error("unknown --target2 option: " + s); return Target2Policy::GotRel; } -static bool isOutputFormatBinary(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_oformat, "elf"); - if (S == "binary") +static bool isOutputFormatBinary(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_oformat, "elf"); + if (s == "binary") return true; - if (!S.startswith("elf")) - error("unknown --oformat value: " + S); + if (!s.startswith("elf")) + error("unknown --oformat value: " + s); return false; } -static DiscardPolicy getDiscard(opt::InputArgList &Args) { - if (Args.hasArg(OPT_relocatable)) +static DiscardPolicy getDiscard(opt::InputArgList &args) { + if (args.hasArg(OPT_relocatable)) return DiscardPolicy::None; - auto *Arg = - Args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none); - if (!Arg) + auto *arg = + args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none); + if (!arg) return DiscardPolicy::Default; - if (Arg->getOption().getID() == OPT_discard_all) + if (arg->getOption().getID() == OPT_discard_all) return DiscardPolicy::All; - if (Arg->getOption().getID() == OPT_discard_locals) + if (arg->getOption().getID() == OPT_discard_locals) return DiscardPolicy::Locals; return DiscardPolicy::None; } -static StringRef getDynamicLinker(opt::InputArgList &Args) { - auto *Arg = Args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker); - if (!Arg || Arg->getOption().getID() == OPT_no_dynamic_linker) +static StringRef getDynamicLinker(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker); + if (!arg || arg->getOption().getID() == OPT_no_dynamic_linker) return ""; - return Arg->getValue(); + return arg->getValue(); } -static ICFLevel getICF(opt::InputArgList &Args) { - auto *Arg = Args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all); - if (!Arg || Arg->getOption().getID() == OPT_icf_none) +static ICFLevel getICF(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all); + if (!arg || arg->getOption().getID() == OPT_icf_none) return ICFLevel::None; - if (Arg->getOption().getID() == OPT_icf_safe) + if (arg->getOption().getID() == OPT_icf_safe) return ICFLevel::Safe; return ICFLevel::All; } -static StripPolicy getStrip(opt::InputArgList &Args) { - if (Args.hasArg(OPT_relocatable)) +static StripPolicy getStrip(opt::InputArgList &args) { + if (args.hasArg(OPT_relocatable)) return StripPolicy::None; - auto *Arg = Args.getLastArg(OPT_strip_all, OPT_strip_debug); - if (!Arg) + auto *arg = args.getLastArg(OPT_strip_all, OPT_strip_debug); + if (!arg) return StripPolicy::None; - if (Arg->getOption().getID() == OPT_strip_all) + if (arg->getOption().getID() == OPT_strip_all) return StripPolicy::All; return StripPolicy::Debug; } -static uint64_t parseSectionAddress(StringRef S, const opt::Arg &Arg) { - uint64_t VA = 0; - if (S.startswith("0x")) - S = S.drop_front(2); - if (!to_integer(S, VA, 16)) - error("invalid argument: " + toString(Arg)); - return VA; +static uint64_t parseSectionAddress(StringRef s, opt::InputArgList &args, + const opt::Arg &arg) { + uint64_t va = 0; + if (s.startswith("0x")) + s = s.drop_front(2); + if (!to_integer(s, va, 16)) + error("invalid argument: " + arg.getAsString(args)); + return va; } -static StringMap getSectionStartMap(opt::InputArgList &Args) { - StringMap Ret; - for (auto *Arg : Args.filtered(OPT_section_start)) { - StringRef Name; - StringRef Addr; - std::tie(Name, Addr) = StringRef(Arg->getValue()).split('='); - Ret[Name] = parseSectionAddress(Addr, *Arg); +static StringMap getSectionStartMap(opt::InputArgList &args) { + StringMap ret; + for (auto *arg : args.filtered(OPT_section_start)) { + StringRef name; + StringRef addr; + std::tie(name, addr) = StringRef(arg->getValue()).split('='); + ret[name] = parseSectionAddress(addr, args, *arg); } - if (auto *Arg = Args.getLastArg(OPT_Ttext)) - Ret[".text"] = parseSectionAddress(Arg->getValue(), *Arg); - if (auto *Arg = Args.getLastArg(OPT_Tdata)) - Ret[".data"] = parseSectionAddress(Arg->getValue(), *Arg); - if (auto *Arg = Args.getLastArg(OPT_Tbss)) - Ret[".bss"] = parseSectionAddress(Arg->getValue(), *Arg); - return Ret; + if (auto *arg = args.getLastArg(OPT_Ttext)) + ret[".text"] = parseSectionAddress(arg->getValue(), args, *arg); + if (auto *arg = args.getLastArg(OPT_Tdata)) + ret[".data"] = parseSectionAddress(arg->getValue(), args, *arg); + if (auto *arg = args.getLastArg(OPT_Tbss)) + ret[".bss"] = parseSectionAddress(arg->getValue(), args, *arg); + return ret; } -static SortSectionPolicy getSortSection(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_sort_section); - if (S == "alignment") +static SortSectionPolicy getSortSection(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_sort_section); + if (s == "alignment") return SortSectionPolicy::Alignment; - if (S == "name") + if (s == "name") return SortSectionPolicy::Name; - if (!S.empty()) - error("unknown --sort-section rule: " + S); + if (!s.empty()) + error("unknown --sort-section rule: " + s); return SortSectionPolicy::Default; } -static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_orphan_handling, "place"); - if (S == "warn") +static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_orphan_handling, "place"); + if (s == "warn") return OrphanHandlingPolicy::Warn; - if (S == "error") + if (s == "error") return OrphanHandlingPolicy::Error; - if (S != "place") - error("unknown --orphan-handling mode: " + S); + if (s != "place") + error("unknown --orphan-handling mode: " + s); return OrphanHandlingPolicy::Place; } @@ -607,388 +642,412 @@ static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &Args) { // synonym for "sha1" because all our hash functions including // -build-id=sha1 are actually tree hashes for performance reasons. static std::pair> -getBuildId(opt::InputArgList &Args) { - auto *Arg = Args.getLastArg(OPT_build_id, OPT_build_id_eq); - if (!Arg) +getBuildId(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_build_id, OPT_build_id_eq); + if (!arg) return {BuildIdKind::None, {}}; - if (Arg->getOption().getID() == OPT_build_id) + if (arg->getOption().getID() == OPT_build_id) return {BuildIdKind::Fast, {}}; - StringRef S = Arg->getValue(); - if (S == "fast") + StringRef s = arg->getValue(); + if (s == "fast") return {BuildIdKind::Fast, {}}; - if (S == "md5") + if (s == "md5") return {BuildIdKind::Md5, {}}; - if (S == "sha1" || S == "tree") + if (s == "sha1" || s == "tree") return {BuildIdKind::Sha1, {}}; - if (S == "uuid") + if (s == "uuid") return {BuildIdKind::Uuid, {}}; - if (S.startswith("0x")) - return {BuildIdKind::Hexstring, parseHex(S.substr(2))}; + if (s.startswith("0x")) + return {BuildIdKind::Hexstring, parseHex(s.substr(2))}; - if (S != "none") - error("unknown --build-id style: " + S); + if (s != "none") + error("unknown --build-id style: " + s); return {BuildIdKind::None, {}}; } -static std::pair getPackDynRelocs(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_pack_dyn_relocs, "none"); - if (S == "android") +static std::pair getPackDynRelocs(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_pack_dyn_relocs, "none"); + if (s == "android") return {true, false}; - if (S == "relr") + if (s == "relr") return {false, true}; - if (S == "android+relr") + if (s == "android+relr") return {true, true}; - if (S != "none") - error("unknown -pack-dyn-relocs format: " + S); + if (s != "none") + error("unknown -pack-dyn-relocs format: " + s); return {false, false}; } -static void readCallGraph(MemoryBufferRef MB) { +static void readCallGraph(MemoryBufferRef mb) { // Build a map from symbol name to section - DenseMap Map; - for (InputFile *File : ObjectFiles) - for (Symbol *Sym : File->getSymbols()) - Map[Sym->getName()] = Sym; - - auto FindSection = [&](StringRef Name) -> InputSectionBase * { - Symbol *Sym = Map.lookup(Name); - if (!Sym) { - if (Config->WarnSymbolOrdering) - warn(MB.getBufferIdentifier() + ": no such symbol: " + Name); + DenseMap map; + for (InputFile *file : objectFiles) + for (Symbol *sym : file->getSymbols()) + map[sym->getName()] = sym; + + auto findSection = [&](StringRef name) -> InputSectionBase * { + Symbol *sym = map.lookup(name); + if (!sym) { + if (config->warnSymbolOrdering) + warn(mb.getBufferIdentifier() + ": no such symbol: " + name); return nullptr; } - maybeWarnUnorderableSymbol(Sym); + maybeWarnUnorderableSymbol(sym); - if (Defined *DR = dyn_cast_or_null(Sym)) - return dyn_cast_or_null(DR->Section); + if (Defined *dr = dyn_cast_or_null(sym)) + return dyn_cast_or_null(dr->section); return nullptr; }; - for (StringRef Line : args::getLines(MB)) { - SmallVector Fields; - Line.split(Fields, ' '); - uint64_t Count; + for (StringRef line : args::getLines(mb)) { + SmallVector fields; + line.split(fields, ' '); + uint64_t count; - if (Fields.size() != 3 || !to_integer(Fields[2], Count)) { - error(MB.getBufferIdentifier() + ": parse error"); + if (fields.size() != 3 || !to_integer(fields[2], count)) { + error(mb.getBufferIdentifier() + ": parse error"); return; } - if (InputSectionBase *From = FindSection(Fields[0])) - if (InputSectionBase *To = FindSection(Fields[1])) - Config->CallGraphProfile[std::make_pair(From, To)] += Count; + if (InputSectionBase *from = findSection(fields[0])) + if (InputSectionBase *to = findSection(fields[1])) + config->callGraphProfile[std::make_pair(from, to)] += count; } } template static void readCallGraphsFromObjectFiles() { - for (auto File : ObjectFiles) { - auto *Obj = cast>(File); + for (auto file : objectFiles) { + auto *obj = cast>(file); - for (const Elf_CGProfile_Impl &CGPE : Obj->CGProfile) { - auto *FromSym = dyn_cast(&Obj->getSymbol(CGPE.cgp_from)); - auto *ToSym = dyn_cast(&Obj->getSymbol(CGPE.cgp_to)); - if (!FromSym || !ToSym) + for (const Elf_CGProfile_Impl &cgpe : obj->cgProfile) { + auto *fromSym = dyn_cast(&obj->getSymbol(cgpe.cgp_from)); + auto *toSym = dyn_cast(&obj->getSymbol(cgpe.cgp_to)); + if (!fromSym || !toSym) continue; - auto *From = dyn_cast_or_null(FromSym->Section); - auto *To = dyn_cast_or_null(ToSym->Section); - if (From && To) - Config->CallGraphProfile[{From, To}] += CGPE.cgp_weight; + auto *from = dyn_cast_or_null(fromSym->section); + auto *to = dyn_cast_or_null(toSym->section); + if (from && to) + config->callGraphProfile[{from, to}] += cgpe.cgp_weight; } } } -static bool getCompressDebugSections(opt::InputArgList &Args) { - StringRef S = Args.getLastArgValue(OPT_compress_debug_sections, "none"); - if (S == "none") +static bool getCompressDebugSections(opt::InputArgList &args) { + StringRef s = args.getLastArgValue(OPT_compress_debug_sections, "none"); + if (s == "none") return false; - if (S != "zlib") - error("unknown --compress-debug-sections value: " + S); + if (s != "zlib") + error("unknown --compress-debug-sections value: " + s); if (!zlib::isAvailable()) error("--compress-debug-sections: zlib is not available"); return true; } -static std::pair getOldNewOptions(opt::InputArgList &Args, - unsigned Id) { - auto *Arg = Args.getLastArg(Id); - if (!Arg) +static std::pair getOldNewOptions(opt::InputArgList &args, + unsigned id) { + auto *arg = args.getLastArg(id); + if (!arg) return {"", ""}; - StringRef S = Arg->getValue(); - std::pair Ret = S.split(';'); - if (Ret.second.empty()) - error(Arg->getSpelling() + " expects 'old;new' format, but got " + S); - return Ret; + StringRef s = arg->getValue(); + std::pair ret = s.split(';'); + if (ret.second.empty()) + error(arg->getSpelling() + " expects 'old;new' format, but got " + s); + return ret; } // Parse the symbol ordering file and warn for any duplicate entries. -static std::vector getSymbolOrderingFile(MemoryBufferRef MB) { - SetVector Names; - for (StringRef S : args::getLines(MB)) - if (!Names.insert(S) && Config->WarnSymbolOrdering) - warn(MB.getBufferIdentifier() + ": duplicate ordered symbol: " + S); +static std::vector getSymbolOrderingFile(MemoryBufferRef mb) { + SetVector names; + for (StringRef s : args::getLines(mb)) + if (!names.insert(s) && config->warnSymbolOrdering) + warn(mb.getBufferIdentifier() + ": duplicate ordered symbol: " + s); - return Names.takeVector(); + return names.takeVector(); } -static void parseClangOption(StringRef Opt, const Twine &Msg) { - std::string Err; - raw_string_ostream OS(Err); +static void parseClangOption(StringRef opt, const Twine &msg) { + std::string err; + raw_string_ostream os(err); - const char *Argv[] = {Config->ProgName.data(), Opt.data()}; - if (cl::ParseCommandLineOptions(2, Argv, "", &OS)) + const char *argv[] = {config->progName.data(), opt.data()}; + if (cl::ParseCommandLineOptions(2, argv, "", &os)) return; - OS.flush(); - error(Msg + ": " + StringRef(Err).trim()); + os.flush(); + error(msg + ": " + StringRef(err).trim()); } // Initializes Config members by the command line options. -void LinkerDriver::readConfigs(opt::InputArgList &Args) { - errorHandler().Verbose = Args.hasArg(OPT_verbose); - errorHandler().FatalWarnings = - Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false); - ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true); - - Config->AllowMultipleDefinition = - Args.hasFlag(OPT_allow_multiple_definition, +static void readConfigs(opt::InputArgList &args) { + errorHandler().verbose = args.hasArg(OPT_verbose); + errorHandler().fatalWarnings = + args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false); + errorHandler().vsDiagnostics = + args.hasArg(OPT_visual_studio_diagnostics_format, false); + threadsEnabled = args.hasFlag(OPT_threads, OPT_no_threads, true); + + config->allowMultipleDefinition = + args.hasFlag(OPT_allow_multiple_definition, OPT_no_allow_multiple_definition, false) || - hasZOption(Args, "muldefs"); - Config->AuxiliaryList = args::getStrings(Args, OPT_auxiliary); - Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic); - Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); - Config->CheckSections = - Args.hasFlag(OPT_check_sections, OPT_no_check_sections, true); - Config->Chroot = Args.getLastArgValue(OPT_chroot); - Config->CompressDebugSections = getCompressDebugSections(Args); - Config->Cref = Args.hasFlag(OPT_cref, OPT_no_cref, false); - Config->DefineCommon = Args.hasFlag(OPT_define_common, OPT_no_define_common, - !Args.hasArg(OPT_relocatable)); - Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true); - Config->DisableVerify = Args.hasArg(OPT_disable_verify); - Config->Discard = getDiscard(Args); - Config->DwoDir = Args.getLastArgValue(OPT_plugin_opt_dwo_dir_eq); - Config->DynamicLinker = getDynamicLinker(Args); - Config->EhFrameHdr = - Args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false); - Config->EmitLLVM = Args.hasArg(OPT_plugin_opt_emit_llvm, false); - Config->EmitRelocs = Args.hasArg(OPT_emit_relocs); - Config->CallGraphProfileSort = Args.hasFlag( + hasZOption(args, "muldefs"); + config->allowShlibUndefined = + args.hasFlag(OPT_allow_shlib_undefined, OPT_no_allow_shlib_undefined, + args.hasArg(OPT_shared)); + config->auxiliaryList = args::getStrings(args, OPT_auxiliary); + config->bsymbolic = args.hasArg(OPT_Bsymbolic); + config->bsymbolicFunctions = args.hasArg(OPT_Bsymbolic_functions); + config->checkSections = + args.hasFlag(OPT_check_sections, OPT_no_check_sections, true); + config->chroot = args.getLastArgValue(OPT_chroot); + config->compressDebugSections = getCompressDebugSections(args); + config->cref = args.hasFlag(OPT_cref, OPT_no_cref, false); + config->defineCommon = args.hasFlag(OPT_define_common, OPT_no_define_common, + !args.hasArg(OPT_relocatable)); + config->demangle = args.hasFlag(OPT_demangle, OPT_no_demangle, true); + config->dependentLibraries = args.hasFlag(OPT_dependent_libraries, OPT_no_dependent_libraries, true); + config->disableVerify = args.hasArg(OPT_disable_verify); + config->discard = getDiscard(args); + config->dwoDir = args.getLastArgValue(OPT_plugin_opt_dwo_dir_eq); + config->dynamicLinker = getDynamicLinker(args); + config->ehFrameHdr = + args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false); + config->emitLLVM = args.hasArg(OPT_plugin_opt_emit_llvm, false); + config->emitRelocs = args.hasArg(OPT_emit_relocs); + config->callGraphProfileSort = args.hasFlag( OPT_call_graph_profile_sort, OPT_no_call_graph_profile_sort, true); - Config->EnableNewDtags = - Args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true); - Config->Entry = Args.getLastArgValue(OPT_entry); - Config->ExecuteOnly = - Args.hasFlag(OPT_execute_only, OPT_no_execute_only, false); - Config->ExportDynamic = - Args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false); - Config->FilterList = args::getStrings(Args, OPT_filter); - Config->Fini = Args.getLastArgValue(OPT_fini, "_fini"); - Config->FixCortexA53Errata843419 = Args.hasArg(OPT_fix_cortex_a53_843419); - Config->GcSections = Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); - Config->GnuUnique = Args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); - Config->GdbIndex = Args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false); - Config->ICF = getICF(Args); - Config->IgnoreDataAddressEquality = - Args.hasArg(OPT_ignore_data_address_equality); - Config->IgnoreFunctionAddressEquality = - Args.hasArg(OPT_ignore_function_address_equality); - Config->Init = Args.getLastArgValue(OPT_init, "_init"); - Config->LTOAAPipeline = Args.getLastArgValue(OPT_lto_aa_pipeline); - Config->LTODebugPassManager = Args.hasArg(OPT_lto_debug_pass_manager); - Config->LTONewPassManager = Args.hasArg(OPT_lto_new_pass_manager); - Config->LTONewPmPasses = Args.getLastArgValue(OPT_lto_newpm_passes); - Config->LTOO = args::getInteger(Args, OPT_lto_O, 2); - Config->LTOObjPath = Args.getLastArgValue(OPT_plugin_opt_obj_path_eq); - Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1); - Config->LTOSampleProfile = Args.getLastArgValue(OPT_lto_sample_profile); - Config->MapFile = Args.getLastArgValue(OPT_Map); - Config->MipsGotSize = args::getInteger(Args, OPT_mips_got_size, 0xfff0); - Config->MergeArmExidx = - Args.hasFlag(OPT_merge_exidx_entries, OPT_no_merge_exidx_entries, true); - Config->NoinhibitExec = Args.hasArg(OPT_noinhibit_exec); - Config->Nostdlib = Args.hasArg(OPT_nostdlib); - Config->OFormatBinary = isOutputFormatBinary(Args); - Config->Omagic = Args.hasFlag(OPT_omagic, OPT_no_omagic, false); - Config->OptRemarksFilename = Args.getLastArgValue(OPT_opt_remarks_filename); - Config->OptRemarksWithHotness = Args.hasArg(OPT_opt_remarks_with_hotness); - Config->Optimize = args::getInteger(Args, OPT_O, 1); - Config->OrphanHandling = getOrphanHandling(Args); - Config->OutputFile = Args.getLastArgValue(OPT_o); - Config->Pie = Args.hasFlag(OPT_pie, OPT_no_pie, false); - Config->PrintIcfSections = - Args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false); - Config->PrintGcSections = - Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false); - Config->Rpath = getRpath(Args); - Config->Relocatable = Args.hasArg(OPT_relocatable); - Config->SaveTemps = Args.hasArg(OPT_save_temps); - Config->SearchPaths = args::getStrings(Args, OPT_library_path); - Config->SectionStartMap = getSectionStartMap(Args); - Config->Shared = Args.hasArg(OPT_shared); - Config->SingleRoRx = Args.hasArg(OPT_no_rosegment); - Config->SoName = Args.getLastArgValue(OPT_soname); - Config->SortSection = getSortSection(Args); - Config->SplitStackAdjustSize = args::getInteger(Args, OPT_split_stack_adjust_size, 16384); - Config->Strip = getStrip(Args); - Config->Sysroot = Args.getLastArgValue(OPT_sysroot); - Config->Target1Rel = Args.hasFlag(OPT_target1_rel, OPT_target1_abs, false); - Config->Target2 = getTarget2(Args); - Config->ThinLTOCacheDir = Args.getLastArgValue(OPT_thinlto_cache_dir); - Config->ThinLTOCachePolicy = CHECK( - parseCachePruningPolicy(Args.getLastArgValue(OPT_thinlto_cache_policy)), + config->enableNewDtags = + args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true); + config->entry = args.getLastArgValue(OPT_entry); + config->executeOnly = + args.hasFlag(OPT_execute_only, OPT_no_execute_only, false); + config->exportDynamic = + args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false); + config->filterList = args::getStrings(args, OPT_filter); + config->fini = args.getLastArgValue(OPT_fini, "_fini"); + config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419); + config->forceBTI = args.hasArg(OPT_force_bti); + config->requireCET = args.hasArg(OPT_require_cet); + config->gcSections = args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); + config->gnuUnique = args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); + config->gdbIndex = args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false); + config->icf = getICF(args); + config->ignoreDataAddressEquality = + args.hasArg(OPT_ignore_data_address_equality); + config->ignoreFunctionAddressEquality = + args.hasArg(OPT_ignore_function_address_equality); + config->init = args.getLastArgValue(OPT_init, "_init"); + config->ltoAAPipeline = args.getLastArgValue(OPT_lto_aa_pipeline); + config->ltoCSProfileGenerate = args.hasArg(OPT_lto_cs_profile_generate); + config->ltoCSProfileFile = args.getLastArgValue(OPT_lto_cs_profile_file); + config->ltoDebugPassManager = args.hasArg(OPT_lto_debug_pass_manager); + config->ltoNewPassManager = args.hasArg(OPT_lto_new_pass_manager); + config->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes); + config->ltoo = args::getInteger(args, OPT_lto_O, 2); + config->ltoObjPath = args.getLastArgValue(OPT_plugin_opt_obj_path_eq); + config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1); + config->ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile); + config->mapFile = args.getLastArgValue(OPT_Map); + config->mipsGotSize = args::getInteger(args, OPT_mips_got_size, 0xfff0); + config->mergeArmExidx = + args.hasFlag(OPT_merge_exidx_entries, OPT_no_merge_exidx_entries, true); + config->nmagic = args.hasFlag(OPT_nmagic, OPT_no_nmagic, false); + config->noinhibitExec = args.hasArg(OPT_noinhibit_exec); + config->nostdlib = args.hasArg(OPT_nostdlib); + config->oFormatBinary = isOutputFormatBinary(args); + config->omagic = args.hasFlag(OPT_omagic, OPT_no_omagic, false); + config->optRemarksFilename = args.getLastArgValue(OPT_opt_remarks_filename); + config->optRemarksPasses = args.getLastArgValue(OPT_opt_remarks_passes); + config->optRemarksWithHotness = args.hasArg(OPT_opt_remarks_with_hotness); + config->optRemarksFormat = args.getLastArgValue(OPT_opt_remarks_format); + config->optimize = args::getInteger(args, OPT_O, 1); + config->orphanHandling = getOrphanHandling(args); + config->outputFile = args.getLastArgValue(OPT_o); + config->pacPlt = args.hasArg(OPT_pac_plt); + config->pie = args.hasFlag(OPT_pie, OPT_no_pie, false); + config->printIcfSections = + args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false); + config->printGcSections = + args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false); + config->printSymbolOrder = + args.getLastArgValue(OPT_print_symbol_order); + config->rpath = getRpath(args); + config->relocatable = args.hasArg(OPT_relocatable); + config->saveTemps = args.hasArg(OPT_save_temps); + config->searchPaths = args::getStrings(args, OPT_library_path); + config->sectionStartMap = getSectionStartMap(args); + config->shared = args.hasArg(OPT_shared); + config->singleRoRx = args.hasArg(OPT_no_rosegment); + config->soName = args.getLastArgValue(OPT_soname); + config->sortSection = getSortSection(args); + config->splitStackAdjustSize = args::getInteger(args, OPT_split_stack_adjust_size, 16384); + config->strip = getStrip(args); + config->sysroot = args.getLastArgValue(OPT_sysroot); + config->target1Rel = args.hasFlag(OPT_target1_rel, OPT_target1_abs, false); + config->target2 = getTarget2(args); + config->thinLTOCacheDir = args.getLastArgValue(OPT_thinlto_cache_dir); + config->thinLTOCachePolicy = CHECK( + parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)), "--thinlto-cache-policy: invalid cache policy"); - Config->ThinLTOEmitImportsFiles = - Args.hasArg(OPT_plugin_opt_thinlto_emit_imports_files); - Config->ThinLTOIndexOnly = Args.hasArg(OPT_plugin_opt_thinlto_index_only) || - Args.hasArg(OPT_plugin_opt_thinlto_index_only_eq); - Config->ThinLTOIndexOnlyArg = - Args.getLastArgValue(OPT_plugin_opt_thinlto_index_only_eq); - Config->ThinLTOJobs = args::getInteger(Args, OPT_thinlto_jobs, -1u); - Config->ThinLTOObjectSuffixReplace = - getOldNewOptions(Args, OPT_plugin_opt_thinlto_object_suffix_replace_eq); - Config->ThinLTOPrefixReplace = - getOldNewOptions(Args, OPT_plugin_opt_thinlto_prefix_replace_eq); - Config->Trace = Args.hasArg(OPT_trace); - Config->Undefined = args::getStrings(Args, OPT_undefined); - Config->UndefinedVersion = - Args.hasFlag(OPT_undefined_version, OPT_no_undefined_version, true); - Config->UseAndroidRelrTags = Args.hasFlag( + config->thinLTOEmitImportsFiles = + args.hasArg(OPT_plugin_opt_thinlto_emit_imports_files); + config->thinLTOIndexOnly = args.hasArg(OPT_plugin_opt_thinlto_index_only) || + args.hasArg(OPT_plugin_opt_thinlto_index_only_eq); + config->thinLTOIndexOnlyArg = + args.getLastArgValue(OPT_plugin_opt_thinlto_index_only_eq); + config->thinLTOJobs = args::getInteger(args, OPT_thinlto_jobs, -1u); + config->thinLTOObjectSuffixReplace = + getOldNewOptions(args, OPT_plugin_opt_thinlto_object_suffix_replace_eq); + config->thinLTOPrefixReplace = + getOldNewOptions(args, OPT_plugin_opt_thinlto_prefix_replace_eq); + config->trace = args.hasArg(OPT_trace); + config->undefined = args::getStrings(args, OPT_undefined); + config->undefinedVersion = + args.hasFlag(OPT_undefined_version, OPT_no_undefined_version, true); + config->useAndroidRelrTags = args.hasFlag( OPT_use_android_relr_tags, OPT_no_use_android_relr_tags, false); - Config->UnresolvedSymbols = getUnresolvedSymbolPolicy(Args); - Config->WarnBackrefs = - Args.hasFlag(OPT_warn_backrefs, OPT_no_warn_backrefs, false); - Config->WarnCommon = Args.hasFlag(OPT_warn_common, OPT_no_warn_common, false); - Config->WarnIfuncTextrel = - Args.hasFlag(OPT_warn_ifunc_textrel, OPT_no_warn_ifunc_textrel, false); - Config->WarnSymbolOrdering = - Args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true); - Config->ZCombreloc = getZFlag(Args, "combreloc", "nocombreloc", true); - Config->ZCopyreloc = getZFlag(Args, "copyreloc", "nocopyreloc", true); - Config->ZExecstack = getZFlag(Args, "execstack", "noexecstack", false); - Config->ZGlobal = hasZOption(Args, "global"); - Config->ZHazardplt = hasZOption(Args, "hazardplt"); - Config->ZInitfirst = hasZOption(Args, "initfirst"); - Config->ZInterpose = hasZOption(Args, "interpose"); - Config->ZKeepTextSectionPrefix = getZFlag( - Args, "keep-text-section-prefix", "nokeep-text-section-prefix", false); - Config->ZNodefaultlib = hasZOption(Args, "nodefaultlib"); - Config->ZNodelete = hasZOption(Args, "nodelete"); - Config->ZNodlopen = hasZOption(Args, "nodlopen"); - Config->ZNow = getZFlag(Args, "now", "lazy", false); - Config->ZOrigin = hasZOption(Args, "origin"); - Config->ZRelro = getZFlag(Args, "relro", "norelro", true); - Config->ZRetpolineplt = hasZOption(Args, "retpolineplt"); - Config->ZRodynamic = hasZOption(Args, "rodynamic"); - Config->ZStackSize = args::getZOptionValue(Args, OPT_z, "stack-size", 0); - Config->ZText = getZFlag(Args, "text", "notext", true); - Config->ZWxneeded = hasZOption(Args, "wxneeded"); + config->unresolvedSymbols = getUnresolvedSymbolPolicy(args); + config->warnBackrefs = + args.hasFlag(OPT_warn_backrefs, OPT_no_warn_backrefs, false); + config->warnCommon = args.hasFlag(OPT_warn_common, OPT_no_warn_common, false); + config->warnIfuncTextrel = + args.hasFlag(OPT_warn_ifunc_textrel, OPT_no_warn_ifunc_textrel, false); + config->warnSymbolOrdering = + args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true); + config->zCombreloc = getZFlag(args, "combreloc", "nocombreloc", true); + config->zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true); + config->zExecstack = getZFlag(args, "execstack", "noexecstack", false); + config->zGlobal = hasZOption(args, "global"); + config->zHazardplt = hasZOption(args, "hazardplt"); + config->zIfuncNoplt = hasZOption(args, "ifunc-noplt"); + config->zInitfirst = hasZOption(args, "initfirst"); + config->zInterpose = hasZOption(args, "interpose"); + config->zKeepTextSectionPrefix = getZFlag( + args, "keep-text-section-prefix", "nokeep-text-section-prefix", false); + config->zNodefaultlib = hasZOption(args, "nodefaultlib"); + config->zNodelete = hasZOption(args, "nodelete"); + config->zNodlopen = hasZOption(args, "nodlopen"); + config->zNow = getZFlag(args, "now", "lazy", false); + config->zOrigin = hasZOption(args, "origin"); + config->zRelro = getZFlag(args, "relro", "norelro", true); + config->zRetpolineplt = hasZOption(args, "retpolineplt"); + config->zRodynamic = hasZOption(args, "rodynamic"); + config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0); + config->zText = getZFlag(args, "text", "notext", true); + config->zWxneeded = hasZOption(args, "wxneeded"); // Parse LTO options. - if (auto *Arg = Args.getLastArg(OPT_plugin_opt_mcpu_eq)) - parseClangOption(Saver.save("-mcpu=" + StringRef(Arg->getValue())), - Arg->getSpelling()); + if (auto *arg = args.getLastArg(OPT_plugin_opt_mcpu_eq)) + parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())), + arg->getSpelling()); - for (auto *Arg : Args.filtered(OPT_plugin_opt)) - parseClangOption(Arg->getValue(), Arg->getSpelling()); + for (auto *arg : args.filtered(OPT_plugin_opt)) + parseClangOption(arg->getValue(), arg->getSpelling()); // Parse -mllvm options. - for (auto *Arg : Args.filtered(OPT_mllvm)) - parseClangOption(Arg->getValue(), Arg->getSpelling()); + for (auto *arg : args.filtered(OPT_mllvm)) + parseClangOption(arg->getValue(), arg->getSpelling()); - if (Config->LTOO > 3) - error("invalid optimization level for LTO: " + Twine(Config->LTOO)); - if (Config->LTOPartitions == 0) + if (config->ltoo > 3) + error("invalid optimization level for LTO: " + Twine(config->ltoo)); + if (config->ltoPartitions == 0) error("--lto-partitions: number of threads must be > 0"); - if (Config->ThinLTOJobs == 0) + if (config->thinLTOJobs == 0) error("--thinlto-jobs: number of threads must be > 0"); - if (Config->SplitStackAdjustSize < 0) + if (config->splitStackAdjustSize < 0) error("--split-stack-adjust-size: size must be >= 0"); // Parse ELF{32,64}{LE,BE} and CPU type. - if (auto *Arg = Args.getLastArg(OPT_m)) { - StringRef S = Arg->getValue(); - std::tie(Config->EKind, Config->EMachine, Config->OSABI) = - parseEmulation(S); - Config->MipsN32Abi = (S == "elf32btsmipn32" || S == "elf32ltsmipn32"); - Config->Emulation = S; + if (auto *arg = args.getLastArg(OPT_m)) { + StringRef s = arg->getValue(); + std::tie(config->ekind, config->emachine, config->osabi) = + parseEmulation(s); + config->mipsN32Abi = (s == "elf32btsmipn32" || s == "elf32ltsmipn32"); + config->emulation = s; } // Parse -hash-style={sysv,gnu,both}. - if (auto *Arg = Args.getLastArg(OPT_hash_style)) { - StringRef S = Arg->getValue(); - if (S == "sysv") - Config->SysvHash = true; - else if (S == "gnu") - Config->GnuHash = true; - else if (S == "both") - Config->SysvHash = Config->GnuHash = true; + if (auto *arg = args.getLastArg(OPT_hash_style)) { + StringRef s = arg->getValue(); + if (s == "sysv") + config->sysvHash = true; + else if (s == "gnu") + config->gnuHash = true; + else if (s == "both") + config->sysvHash = config->gnuHash = true; else - error("unknown -hash-style: " + S); + error("unknown -hash-style: " + s); } - if (Args.hasArg(OPT_print_map)) - Config->MapFile = "-"; - - // --omagic is an option to create old-fashioned executables in which - // .text segments are writable. Today, the option is still in use to - // create special-purpose programs such as boot loaders. It doesn't - // make sense to create PT_GNU_RELRO for such executables. - if (Config->Omagic) - Config->ZRelro = false; - - std::tie(Config->BuildId, Config->BuildIdVector) = getBuildId(Args); - - std::tie(Config->AndroidPackDynRelocs, Config->RelrPackDynRelocs) = - getPackDynRelocs(Args); - - if (auto *Arg = Args.getLastArg(OPT_symbol_ordering_file)) - if (Optional Buffer = readFile(Arg->getValue())) - Config->SymbolOrderingFile = getSymbolOrderingFile(*Buffer); + if (args.hasArg(OPT_print_map)) + config->mapFile = "-"; + + // Page alignment can be disabled by the -n (--nmagic) and -N (--omagic). + // As PT_GNU_RELRO relies on Paging, do not create it when we have disabled + // it. + if (config->nmagic || config->omagic) + config->zRelro = false; + + std::tie(config->buildId, config->buildIdVector) = getBuildId(args); + + std::tie(config->androidPackDynRelocs, config->relrPackDynRelocs) = + getPackDynRelocs(args); + + if (auto *arg = args.getLastArg(OPT_symbol_ordering_file)){ + if (args.hasArg(OPT_call_graph_ordering_file)) + error("--symbol-ordering-file and --call-graph-order-file " + "may not be used together"); + if (Optional buffer = readFile(arg->getValue())){ + config->symbolOrderingFile = getSymbolOrderingFile(*buffer); + // Also need to disable CallGraphProfileSort to prevent + // LLD order symbols with CGProfile + config->callGraphProfileSort = false; + } + } // If --retain-symbol-file is used, we'll keep only the symbols listed in // the file and discard all others. - if (auto *Arg = Args.getLastArg(OPT_retain_symbols_file)) { - Config->DefaultSymbolVersion = VER_NDX_LOCAL; - if (Optional Buffer = readFile(Arg->getValue())) - for (StringRef S : args::getLines(*Buffer)) - Config->VersionScriptGlobals.push_back( - {S, /*IsExternCpp*/ false, /*HasWildcard*/ false}); + if (auto *arg = args.getLastArg(OPT_retain_symbols_file)) { + config->defaultSymbolVersion = VER_NDX_LOCAL; + if (Optional buffer = readFile(arg->getValue())) + for (StringRef s : args::getLines(*buffer)) + config->versionScriptGlobals.push_back( + {s, /*IsExternCpp*/ false, /*HasWildcard*/ false}); } - bool HasExportDynamic = - Args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false); + bool hasExportDynamic = + args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false); // Parses -dynamic-list and -export-dynamic-symbol. They make some // symbols private. Note that -export-dynamic takes precedence over them // as it says all symbols should be exported. - if (!HasExportDynamic) { - for (auto *Arg : Args.filtered(OPT_dynamic_list)) - if (Optional Buffer = readFile(Arg->getValue())) - readDynamicList(*Buffer); - - for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol)) - Config->DynamicList.push_back( - {Arg->getValue(), /*IsExternCpp*/ false, /*HasWildcard*/ false}); + if (!hasExportDynamic) { + for (auto *arg : args.filtered(OPT_dynamic_list)) + if (Optional buffer = readFile(arg->getValue())) + readDynamicList(*buffer); + + for (auto *arg : args.filtered(OPT_export_dynamic_symbol)) + config->dynamicList.push_back( + {arg->getValue(), /*IsExternCpp*/ false, /*HasWildcard*/ false}); } // If --export-dynamic-symbol=foo is given and symbol foo is defined in // an object file in an archive file, that object file should be pulled // out and linked. (It doesn't have to behave like that from technical // point of view, but this is needed for compatibility with GNU.) - for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol)) - Config->Undefined.push_back(Arg->getValue()); + for (auto *arg : args.filtered(OPT_export_dynamic_symbol)) + config->undefined.push_back(arg->getValue()); - for (auto *Arg : Args.filtered(OPT_version_script)) - if (Optional Path = searchScript(Arg->getValue())) { - if (Optional Buffer = readFile(*Path)) - readVersionScript(*Buffer); + for (auto *arg : args.filtered(OPT_version_script)) + if (Optional path = searchScript(arg->getValue())) { + if (Optional buffer = readFile(*path)) + readVersionScript(*buffer); } else { - error(Twine("cannot find version script ") + Arg->getValue()); + error(Twine("cannot find version script ") + arg->getValue()); } } @@ -996,17 +1055,18 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { // command line options, but computed based on other Config values. // This function initialize such members. See Config.h for the details // of these values. -static void setConfigs(opt::InputArgList &Args) { - ELFKind K = Config->EKind; - uint16_t M = Config->EMachine; - - Config->CopyRelocs = (Config->Relocatable || Config->EmitRelocs); - Config->Is64 = (K == ELF64LEKind || K == ELF64BEKind); - Config->IsLE = (K == ELF32LEKind || K == ELF64LEKind); - Config->Endianness = Config->IsLE ? endianness::little : endianness::big; - Config->IsMips64EL = (K == ELF64LEKind && M == EM_MIPS); - Config->Pic = Config->Pie || Config->Shared; - Config->Wordsize = Config->Is64 ? 8 : 4; +static void setConfigs(opt::InputArgList &args) { + ELFKind k = config->ekind; + uint16_t m = config->emachine; + + config->copyRelocs = (config->relocatable || config->emitRelocs); + config->is64 = (k == ELF64LEKind || k == ELF64BEKind); + config->isLE = (k == ELF32LEKind || k == ELF64LEKind); + config->endianness = config->isLE ? endianness::little : endianness::big; + config->isMips64EL = (k == ELF64LEKind && m == EM_MIPS); + config->isPic = config->pie || config->shared; + config->picThunk = args.hasArg(OPT_pic_veneer, config->isPic); + config->wordsize = config->is64 ? 8 : 4; // ELF defines two different ways to store relocation addends as shown below: // @@ -1021,148 +1081,150 @@ static void setConfigs(opt::InputArgList &Args) { // You cannot choose which one, Rel or Rela, you want to use. Instead each // ABI defines which one you need to use. The following expression expresses // that. - Config->IsRela = M == EM_AARCH64 || M == EM_AMDGPU || M == EM_HEXAGON || - M == EM_PPC || M == EM_PPC64 || M == EM_RISCV || - M == EM_X86_64; + config->isRela = m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON || + m == EM_PPC || m == EM_PPC64 || m == EM_RISCV || + m == EM_X86_64; // If the output uses REL relocations we must store the dynamic relocation // addends to the output sections. We also store addends for RELA relocations // if --apply-dynamic-relocs is used. // We default to not writing the addends when using RELA relocations since // any standard conforming tool can find it in r_addend. - Config->WriteAddends = Args.hasFlag(OPT_apply_dynamic_relocs, + config->writeAddends = args.hasFlag(OPT_apply_dynamic_relocs, OPT_no_apply_dynamic_relocs, false) || - !Config->IsRela; + !config->isRela; - Config->TocOptimize = - Args.hasFlag(OPT_toc_optimize, OPT_no_toc_optimize, M == EM_PPC64); + config->tocOptimize = + args.hasFlag(OPT_toc_optimize, OPT_no_toc_optimize, m == EM_PPC64); } // Returns a value of "-format" option. -static bool isFormatBinary(StringRef S) { - if (S == "binary") +static bool isFormatBinary(StringRef s) { + if (s == "binary") return true; - if (S == "elf" || S == "default") + if (s == "elf" || s == "default") return false; - error("unknown -format value: " + S + + error("unknown -format value: " + s + " (supported formats: elf, default, binary)"); return false; } -void LinkerDriver::createFiles(opt::InputArgList &Args) { +void LinkerDriver::createFiles(opt::InputArgList &args) { // For --{push,pop}-state. - std::vector> Stack; + std::vector> stack; // Iterate over argv to process input files and positional arguments. - for (auto *Arg : Args) { - switch (Arg->getOption().getUnaliasedOption().getID()) { + for (auto *arg : args) { + switch (arg->getOption().getID()) { case OPT_library: - addLibrary(Arg->getValue()); + addLibrary(arg->getValue()); break; case OPT_INPUT: - addFile(Arg->getValue(), /*WithLOption=*/false); + addFile(arg->getValue(), /*withLOption=*/false); break; case OPT_defsym: { - StringRef From; - StringRef To; - std::tie(From, To) = StringRef(Arg->getValue()).split('='); - if (From.empty() || To.empty()) - error("-defsym: syntax error: " + StringRef(Arg->getValue())); + StringRef from; + StringRef to; + std::tie(from, to) = StringRef(arg->getValue()).split('='); + if (from.empty() || to.empty()) + error("-defsym: syntax error: " + StringRef(arg->getValue())); else - readDefsym(From, MemoryBufferRef(To, "-defsym")); + readDefsym(from, MemoryBufferRef(to, "-defsym")); break; } case OPT_script: - if (Optional Path = searchScript(Arg->getValue())) { - if (Optional MB = readFile(*Path)) - readLinkerScript(*MB); + if (Optional path = searchScript(arg->getValue())) { + if (Optional mb = readFile(*path)) + readLinkerScript(*mb); break; } - error(Twine("cannot find linker script ") + Arg->getValue()); + error(Twine("cannot find linker script ") + arg->getValue()); break; case OPT_as_needed: - Config->AsNeeded = true; + config->asNeeded = true; break; case OPT_format: - Config->FormatBinary = isFormatBinary(Arg->getValue()); + config->formatBinary = isFormatBinary(arg->getValue()); break; case OPT_no_as_needed: - Config->AsNeeded = false; + config->asNeeded = false; break; case OPT_Bstatic: - Config->Static = true; + case OPT_omagic: + case OPT_nmagic: + config->isStatic = true; break; case OPT_Bdynamic: - Config->Static = false; + config->isStatic = false; break; case OPT_whole_archive: - InWholeArchive = true; + inWholeArchive = true; break; case OPT_no_whole_archive: - InWholeArchive = false; + inWholeArchive = false; break; case OPT_just_symbols: - if (Optional MB = readFile(Arg->getValue())) { - Files.push_back(createObjectFile(*MB)); - Files.back()->JustSymbols = true; + if (Optional mb = readFile(arg->getValue())) { + files.push_back(createObjectFile(*mb)); + files.back()->justSymbols = true; } break; case OPT_start_group: - if (InputFile::IsInGroup) + if (InputFile::isInGroup) error("nested --start-group"); - InputFile::IsInGroup = true; + InputFile::isInGroup = true; break; case OPT_end_group: - if (!InputFile::IsInGroup) + if (!InputFile::isInGroup) error("stray --end-group"); - InputFile::IsInGroup = false; - ++InputFile::NextGroupId; + InputFile::isInGroup = false; + ++InputFile::nextGroupId; break; case OPT_start_lib: - if (InLib) + if (inLib) error("nested --start-lib"); - if (InputFile::IsInGroup) + if (InputFile::isInGroup) error("may not nest --start-lib in --start-group"); - InLib = true; - InputFile::IsInGroup = true; + inLib = true; + InputFile::isInGroup = true; break; case OPT_end_lib: - if (!InLib) + if (!inLib) error("stray --end-lib"); - InLib = false; - InputFile::IsInGroup = false; - ++InputFile::NextGroupId; + inLib = false; + InputFile::isInGroup = false; + ++InputFile::nextGroupId; break; case OPT_push_state: - Stack.emplace_back(Config->AsNeeded, Config->Static, InWholeArchive); + stack.emplace_back(config->asNeeded, config->isStatic, inWholeArchive); break; case OPT_pop_state: - if (Stack.empty()) { + if (stack.empty()) { error("unbalanced --push-state/--pop-state"); break; } - std::tie(Config->AsNeeded, Config->Static, InWholeArchive) = Stack.back(); - Stack.pop_back(); + std::tie(config->asNeeded, config->isStatic, inWholeArchive) = stack.back(); + stack.pop_back(); break; } } - if (Files.empty() && errorCount() == 0) + if (files.empty() && errorCount() == 0) error("no input files"); } // If -m was not given, infer it from object files. void LinkerDriver::inferMachineType() { - if (Config->EKind != ELFNoneKind) + if (config->ekind != ELFNoneKind) return; - for (InputFile *F : Files) { - if (F->EKind == ELFNoneKind) + for (InputFile *f : files) { + if (f->ekind == ELFNoneKind) continue; - Config->EKind = F->EKind; - Config->EMachine = F->EMachine; - Config->OSABI = F->OSABI; - Config->MipsN32Abi = Config->EMachine == EM_MIPS && isMipsN32Abi(F); + config->ekind = f->ekind; + config->emachine = f->emachine; + config->osabi = f->osabi; + config->mipsN32Abi = config->emachine == EM_MIPS && isMipsN32Abi(f); return; } error("target emulation unknown: -m or at least one .o file required"); @@ -1170,49 +1232,72 @@ void LinkerDriver::inferMachineType() { // Parse -z max-page-size=. The default value is defined by // each target. -static uint64_t getMaxPageSize(opt::InputArgList &Args) { - uint64_t Val = args::getZOptionValue(Args, OPT_z, "max-page-size", - Target->DefaultMaxPageSize); - if (!isPowerOf2_64(Val)) +static uint64_t getMaxPageSize(opt::InputArgList &args) { + uint64_t val = args::getZOptionValue(args, OPT_z, "max-page-size", + target->defaultMaxPageSize); + if (!isPowerOf2_64(val)) error("max-page-size: value isn't a power of 2"); - return Val; + if (config->nmagic || config->omagic) { + if (val != target->defaultMaxPageSize) + warn("-z max-page-size set, but paging disabled by omagic or nmagic"); + return 1; + } + return val; +} + +// Parse -z common-page-size=. The default value is defined by +// each target. +static uint64_t getCommonPageSize(opt::InputArgList &args) { + uint64_t val = args::getZOptionValue(args, OPT_z, "common-page-size", + target->defaultCommonPageSize); + if (!isPowerOf2_64(val)) + error("common-page-size: value isn't a power of 2"); + if (config->nmagic || config->omagic) { + if (val != target->defaultCommonPageSize) + warn("-z common-page-size set, but paging disabled by omagic or nmagic"); + return 1; + } + // commonPageSize can't be larger than maxPageSize. + if (val > config->maxPageSize) + val = config->maxPageSize; + return val; } // Parses -image-base option. -static Optional getImageBase(opt::InputArgList &Args) { - // Because we are using "Config->MaxPageSize" here, this function has to be +static Optional getImageBase(opt::InputArgList &args) { + // Because we are using "Config->maxPageSize" here, this function has to be // called after the variable is initialized. - auto *Arg = Args.getLastArg(OPT_image_base); - if (!Arg) + auto *arg = args.getLastArg(OPT_image_base); + if (!arg) return None; - StringRef S = Arg->getValue(); - uint64_t V; - if (!to_integer(S, V)) { - error("-image-base: number expected, but got " + S); + StringRef s = arg->getValue(); + uint64_t v; + if (!to_integer(s, v)) { + error("-image-base: number expected, but got " + s); return 0; } - if ((V % Config->MaxPageSize) != 0) - warn("-image-base: address isn't multiple of page size: " + S); - return V; + if ((v % config->maxPageSize) != 0) + warn("-image-base: address isn't multiple of page size: " + s); + return v; } // Parses `--exclude-libs=lib,lib,...`. // The library names may be delimited by commas or colons. -static DenseSet getExcludeLibs(opt::InputArgList &Args) { - DenseSet Ret; - for (auto *Arg : Args.filtered(OPT_exclude_libs)) { - StringRef S = Arg->getValue(); +static DenseSet getExcludeLibs(opt::InputArgList &args) { + DenseSet ret; + for (auto *arg : args.filtered(OPT_exclude_libs)) { + StringRef s = arg->getValue(); for (;;) { - size_t Pos = S.find_first_of(",:"); - if (Pos == StringRef::npos) + size_t pos = s.find_first_of(",:"); + if (pos == StringRef::npos) break; - Ret.insert(S.substr(0, Pos)); - S = S.substr(Pos + 1); + ret.insert(s.substr(0, pos)); + s = s.substr(pos + 1); } - Ret.insert(S); + ret.insert(s); } - return Ret; + return ret; } // Handles the -exclude-libs option. If a static library file is specified @@ -1221,139 +1306,247 @@ static DenseSet getExcludeLibs(opt::InputArgList &Args) { // A special library name "ALL" means all archive files. // // This is not a popular option, but some programs such as bionic libc use it. -template -static void excludeLibs(opt::InputArgList &Args) { - DenseSet Libs = getExcludeLibs(Args); - bool All = Libs.count("ALL"); - - auto Visit = [&](InputFile *File) { - if (!File->ArchiveName.empty()) - if (All || Libs.count(path::filename(File->ArchiveName))) - for (Symbol *Sym : File->getSymbols()) - if (!Sym->isLocal() && Sym->File == File) - Sym->VersionId = VER_NDX_LOCAL; +static void excludeLibs(opt::InputArgList &args) { + DenseSet libs = getExcludeLibs(args); + bool all = libs.count("ALL"); + + auto visit = [&](InputFile *file) { + if (!file->archiveName.empty()) + if (all || libs.count(path::filename(file->archiveName))) + for (Symbol *sym : file->getSymbols()) + if (!sym->isLocal() && sym->file == file) + sym->versionId = VER_NDX_LOCAL; }; - for (InputFile *File : ObjectFiles) - Visit(File); + for (InputFile *file : objectFiles) + visit(file); - for (BitcodeFile *File : BitcodeFiles) - Visit(File); + for (BitcodeFile *file : bitcodeFiles) + visit(file); } // Force Sym to be entered in the output. Used for -u or equivalent. -template static void handleUndefined(StringRef Name) { - Symbol *Sym = Symtab->find(Name); - if (!Sym) +static void handleUndefined(Symbol *sym) { + // Since a symbol may not be used inside the program, LTO may + // eliminate it. Mark the symbol as "used" to prevent it. + sym->isUsedInRegularObj = true; + + if (sym->isLazy()) + sym->fetch(); +} + +// As an extention to GNU linkers, lld supports a variant of `-u` +// which accepts wildcard patterns. All symbols that match a given +// pattern are handled as if they were given by `-u`. +static void handleUndefinedGlob(StringRef arg) { + Expected pat = GlobPattern::create(arg); + if (!pat) { + error("--undefined-glob: " + toString(pat.takeError())); return; + } - // Since symbol S may not be used inside the program, LTO may - // eliminate it. Mark the symbol as "used" to prevent it. - Sym->IsUsedInRegularObj = true; + std::vector syms; + symtab->forEachSymbol([&](Symbol *sym) { + // Calling Sym->fetch() from here is not safe because it may + // add new symbols to the symbol table, invalidating the + // current iterator. So we just keep a note. + if (pat->match(sym->getName())) + syms.push_back(sym); + }); - if (Sym->isLazy()) - Symtab->fetchLazy(Sym); + for (Symbol *sym : syms) + handleUndefined(sym); } -template static void handleLibcall(StringRef Name) { - Symbol *Sym = Symtab->find(Name); - if (!Sym || !Sym->isLazy()) +static void handleLibcall(StringRef name) { + Symbol *sym = symtab->find(name); + if (!sym || !sym->isLazy()) return; - MemoryBufferRef MB; - if (auto *LO = dyn_cast(Sym)) - MB = LO->File->MB; + MemoryBufferRef mb; + if (auto *lo = dyn_cast(sym)) + mb = lo->file->mb; else - MB = cast(Sym)->getMemberBuffer(); + mb = cast(sym)->getMemberBuffer(); - if (isBitcode(MB)) - Symtab->fetchLazy(Sym); + if (isBitcode(mb)) + sym->fetch(); +} + +// Replaces common symbols with defined symbols reside in .bss sections. +// This function is called after all symbol names are resolved. As a +// result, the passes after the symbol resolution won't see any +// symbols of type CommonSymbol. +static void replaceCommonSymbols() { + symtab->forEachSymbol([](Symbol *sym) { + auto *s = dyn_cast(sym); + if (!s) + return; + + auto *bss = make("COMMON", s->size, s->alignment); + bss->file = s->file; + bss->markDead(); + inputSections.push_back(bss); + s->replace(Defined{s->file, s->getName(), s->binding, s->stOther, s->type, + /*value=*/0, s->size, bss}); + }); } // If all references to a DSO happen to be weak, the DSO is not added // to DT_NEEDED. If that happens, we need to eliminate shared symbols // created from the DSO. Otherwise, they become dangling references // that point to a non-existent DSO. -template static void demoteSharedSymbols() { - for (Symbol *Sym : Symtab->getSymbols()) { - if (auto *S = dyn_cast(Sym)) { - if (!S->getFile().IsNeeded) { - bool Used = S->Used; - replaceSymbol(S, nullptr, S->getName(), STB_WEAK, S->StOther, - S->Type); - S->Used = Used; - } - } - } +static void demoteSharedSymbols() { + symtab->forEachSymbol([](Symbol *sym) { + auto *s = dyn_cast(sym); + if (!s || s->getFile().isNeeded) + return; + + bool used = s->used; + s->replace(Undefined{nullptr, s->getName(), STB_WEAK, s->stOther, s->type}); + s->used = used; + }); } -// The section referred to by S is considered address-significant. Set the -// KeepUnique flag on the section if appropriate. -static void markAddrsig(Symbol *S) { - if (auto *D = dyn_cast_or_null(S)) - if (D->Section) +// The section referred to by `s` is considered address-significant. Set the +// keepUnique flag on the section if appropriate. +static void markAddrsig(Symbol *s) { + if (auto *d = dyn_cast_or_null(s)) + if (d->section) // We don't need to keep text sections unique under --icf=all even if they // are address-significant. - if (Config->ICF == ICFLevel::Safe || !(D->Section->Flags & SHF_EXECINSTR)) - D->Section->KeepUnique = true; + if (config->icf == ICFLevel::Safe || !(d->section->flags & SHF_EXECINSTR)) + d->section->keepUnique = true; } // Record sections that define symbols mentioned in --keep-unique // and symbols referred to by address-significance tables. These sections are // ineligible for ICF. template -static void findKeepUniqueSections(opt::InputArgList &Args) { - for (auto *Arg : Args.filtered(OPT_keep_unique)) { - StringRef Name = Arg->getValue(); - auto *D = dyn_cast_or_null(Symtab->find(Name)); - if (!D || !D->Section) { - warn("could not find symbol " + Name + " to keep unique"); +static void findKeepUniqueSections(opt::InputArgList &args) { + for (auto *arg : args.filtered(OPT_keep_unique)) { + StringRef name = arg->getValue(); + auto *d = dyn_cast_or_null(symtab->find(name)); + if (!d || !d->section) { + warn("could not find symbol " + name + " to keep unique"); continue; } - D->Section->KeepUnique = true; + d->section->keepUnique = true; } // --icf=all --ignore-data-address-equality means that we can ignore // the dynsym and address-significance tables entirely. - if (Config->ICF == ICFLevel::All && Config->IgnoreDataAddressEquality) + if (config->icf == ICFLevel::All && config->ignoreDataAddressEquality) return; // Symbols in the dynsym could be address-significant in other executables // or DSOs, so we conservatively mark them as address-significant. - for (Symbol *S : Symtab->getSymbols()) - if (S->includeInDynsym()) - markAddrsig(S); + symtab->forEachSymbol([&](Symbol *sym) { + if (sym->includeInDynsym()) + markAddrsig(sym); + }); // Visit the address-significance table in each object file and mark each // referenced symbol as address-significant. - for (InputFile *F : ObjectFiles) { - auto *Obj = cast>(F); - ArrayRef Syms = Obj->getSymbols(); - if (Obj->AddrsigSec) { - ArrayRef Contents = - check(Obj->getObj().getSectionContents(Obj->AddrsigSec)); - const uint8_t *Cur = Contents.begin(); - while (Cur != Contents.end()) { - unsigned Size; - const char *Err; - uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err); - if (Err) - fatal(toString(F) + ": could not decode addrsig section: " + Err); - markAddrsig(Syms[SymIndex]); - Cur += Size; + for (InputFile *f : objectFiles) { + auto *obj = cast>(f); + ArrayRef syms = obj->getSymbols(); + if (obj->addrsigSec) { + ArrayRef contents = + check(obj->getObj().getSectionContents(obj->addrsigSec)); + const uint8_t *cur = contents.begin(); + while (cur != contents.end()) { + unsigned size; + const char *err; + uint64_t symIndex = decodeULEB128(cur, &size, contents.end(), &err); + if (err) + fatal(toString(f) + ": could not decode addrsig section: " + err); + markAddrsig(syms[symIndex]); + cur += size; } } else { // If an object file does not have an address-significance table, // conservatively mark all of its symbols as address-significant. - for (Symbol *S : Syms) - markAddrsig(S); + for (Symbol *s : syms) + markAddrsig(s); + } + } +} + +// This function reads a symbol partition specification section. These sections +// are used to control which partition a symbol is allocated to. See +// https://lld.llvm.org/Partitions.html for more details on partitions. +template +static void readSymbolPartitionSection(InputSectionBase *s) { + // Read the relocation that refers to the partition's entry point symbol. + Symbol *sym; + if (s->areRelocsRela) + sym = &s->getFile()->getRelocTargetSym(s->template relas()[0]); + else + sym = &s->getFile()->getRelocTargetSym(s->template rels()[0]); + if (!isa(sym) || !sym->includeInDynsym()) + return; + + StringRef partName = reinterpret_cast(s->data().data()); + for (Partition &part : partitions) { + if (part.name == partName) { + sym->partition = part.getNumber(); + return; } } + + // Forbid partitions from being used on incompatible targets, and forbid them + // from being used together with various linker features that assume a single + // set of output sections. + if (script->hasSectionsCommand) + error(toString(s->file) + + ": partitions cannot be used with the SECTIONS command"); + if (script->hasPhdrsCommands()) + error(toString(s->file) + + ": partitions cannot be used with the PHDRS command"); + if (!config->sectionStartMap.empty()) + error(toString(s->file) + ": partitions cannot be used with " + "--section-start, -Ttext, -Tdata or -Tbss"); + if (config->emachine == EM_MIPS) + error(toString(s->file) + ": partitions cannot be used on this target"); + + // Impose a limit of no more than 254 partitions. This limit comes from the + // sizes of the Partition fields in InputSectionBase and Symbol, as well as + // the amount of space devoted to the partition number in RankFlags. + if (partitions.size() == 254) + fatal("may not have more than 254 partitions"); + + partitions.emplace_back(); + Partition &newPart = partitions.back(); + newPart.name = partName; + sym->partition = newPart.getNumber(); } -template static Symbol *addUndefined(StringRef Name) { - return Symtab->addUndefined(Name, STB_GLOBAL, STV_DEFAULT, 0, false, - nullptr); +static Symbol *addUndefined(StringRef name) { + return symtab->addSymbol( + Undefined{nullptr, name, STB_GLOBAL, STV_DEFAULT, 0}); +} + +// This function is where all the optimizations of link-time +// optimization takes place. When LTO is in use, some input files are +// not in native object file format but in the LLVM bitcode format. +// This function compiles bitcode files into a few big native files +// using LLVM functions and replaces bitcode symbols with the results. +// Because all bitcode files that the program consists of are passed to +// the compiler at once, it can do a whole-program optimization. +template void LinkerDriver::compileBitcodeFiles() { + // Compile bitcode files and replace bitcode symbols. + lto.reset(new BitcodeCompiler); + for (BitcodeFile *file : bitcodeFiles) + lto->add(*file); + + for (InputFile *file : lto->compile()) { + auto *obj = cast>(file); + obj->parse(/*ignoreComdats=*/true); + for (Symbol *sym : obj->getGlobalSymbols()) + sym->parseSymbolVersion(); + objectFiles.push_back(file); + } } // The --wrap option is a feature to rename symbols so that you can write @@ -1365,9 +1558,9 @@ template static Symbol *addUndefined(StringRef Name) { // // This data structure is instantiated for each -wrap option. struct WrappedSymbol { - Symbol *Sym; - Symbol *Real; - Symbol *Wrap; + Symbol *sym; + Symbol *real; + Symbol *wrap; }; // Handles -wrap option. @@ -1375,34 +1568,33 @@ struct WrappedSymbol { // This function instantiates wrapper symbols. At this point, they seem // like they are not being used at all, so we explicitly set some flags so // that LTO won't eliminate them. -template -static std::vector addWrappedSymbols(opt::InputArgList &Args) { - std::vector V; - DenseSet Seen; +static std::vector addWrappedSymbols(opt::InputArgList &args) { + std::vector v; + DenseSet seen; - for (auto *Arg : Args.filtered(OPT_wrap)) { - StringRef Name = Arg->getValue(); - if (!Seen.insert(Name).second) + for (auto *arg : args.filtered(OPT_wrap)) { + StringRef name = arg->getValue(); + if (!seen.insert(name).second) continue; - Symbol *Sym = Symtab->find(Name); - if (!Sym) + Symbol *sym = symtab->find(name); + if (!sym) continue; - Symbol *Real = addUndefined(Saver.save("__real_" + Name)); - Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name)); - V.push_back({Sym, Real, Wrap}); + Symbol *real = addUndefined(saver.save("__real_" + name)); + Symbol *wrap = addUndefined(saver.save("__wrap_" + name)); + v.push_back({sym, real, wrap}); // We want to tell LTO not to inline symbols to be overwritten // because LTO doesn't know the final symbol contents after renaming. - Real->CanInline = false; - Sym->CanInline = false; + real->canInline = false; + sym->canInline = false; // Tell LTO not to eliminate these symbols. - Sym->IsUsedInRegularObj = true; - Wrap->IsUsedInRegularObj = true; + sym->isUsedInRegularObj = true; + wrap->isUsedInRegularObj = true; } - return V; + return v; } // Do renaming for -wrap by updating pointers to symbols. @@ -1410,27 +1602,63 @@ static std::vector addWrappedSymbols(opt::InputArgList &Args) { // When this function is executed, only InputFiles and symbol table // contain pointers to symbol objects. We visit them to replace pointers, // so that wrapped symbols are swapped as instructed by the command line. -template static void wrapSymbols(ArrayRef Wrapped) { - DenseMap Map; - for (const WrappedSymbol &W : Wrapped) { - Map[W.Sym] = W.Wrap; - Map[W.Real] = W.Sym; +static void wrapSymbols(ArrayRef wrapped) { + DenseMap map; + for (const WrappedSymbol &w : wrapped) { + map[w.sym] = w.wrap; + map[w.real] = w.sym; } // Update pointers in input files. - parallelForEach(ObjectFiles, [&](InputFile *File) { - std::vector &Syms = File->getMutableSymbols(); - for (size_t I = 0, E = Syms.size(); I != E; ++I) - if (Symbol *S = Map.lookup(Syms[I])) - Syms[I] = S; + parallelForEach(objectFiles, [&](InputFile *file) { + MutableArrayRef syms = file->getMutableSymbols(); + for (size_t i = 0, e = syms.size(); i != e; ++i) + if (Symbol *s = map.lookup(syms[i])) + syms[i] = s; }); // Update pointers in the symbol table. - for (const WrappedSymbol &W : Wrapped) - Symtab->wrap(W.Sym, W.Real, W.Wrap); + for (const WrappedSymbol &w : wrapped) + symtab->wrap(w.sym, w.real, w.wrap); } -static const char *LibcallRoutineNames[] = { +// To enable CET (x86's hardware-assited control flow enforcement), each +// source file must be compiled with -fcf-protection. Object files compiled +// with the flag contain feature flags indicating that they are compatible +// with CET. We enable the feature only when all object files are compatible +// with CET. +// +// This function returns the merged feature flags. If 0, we cannot enable CET. +// This is also the case with AARCH64's BTI and PAC which use the similar +// GNU_PROPERTY_AARCH64_FEATURE_1_AND mechanism. +// +// Note that the CET-aware PLT is not implemented yet. We do error +// check only. +template static uint32_t getAndFeatures() { + if (config->emachine != EM_386 && config->emachine != EM_X86_64 && + config->emachine != EM_AARCH64) + return 0; + + uint32_t ret = -1; + for (InputFile *f : objectFiles) { + uint32_t features = cast>(f)->andFeatures; + if (config->forceBTI && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) { + warn(toString(f) + ": --force-bti: file does not have BTI property"); + features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI; + } else if (!features && config->requireCET) + error(toString(f) + ": --require-cet: file is not compatible with CET"); + ret &= features; + } + + // Force enable pointer authentication Plt, we don't warn in this case as + // this does not require support in the object for correctness. + if (config->pacPlt) + ret |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC; + + return ret; +} + +static const char *libcallRoutineNames[] = { #define HANDLE_LIBCALL(code, name) name, #include "llvm/IR/RuntimeLibcalls.def" #undef HANDLE_LIBCALL @@ -1438,53 +1666,48 @@ static const char *LibcallRoutineNames[] = { // Do actual linking. Note that when this function is called, // all linker scripts have already been parsed. -template void LinkerDriver::link(opt::InputArgList &Args) { - Target = getTarget(); - InX::VerSym = nullptr; - InX::VerNeed = nullptr; - - Config->MaxPageSize = getMaxPageSize(Args); - Config->ImageBase = getImageBase(Args); - +template void LinkerDriver::link(opt::InputArgList &args) { // If a -hash-style option was not given, set to a default value, // which varies depending on the target. - if (!Args.hasArg(OPT_hash_style)) { - if (Config->EMachine == EM_MIPS) - Config->SysvHash = true; + if (!args.hasArg(OPT_hash_style)) { + if (config->emachine == EM_MIPS) + config->sysvHash = true; else - Config->SysvHash = Config->GnuHash = true; + config->sysvHash = config->gnuHash = true; } // Default output filename is "a.out" by the Unix tradition. - if (Config->OutputFile.empty()) - Config->OutputFile = "a.out"; + if (config->outputFile.empty()) + config->outputFile = "a.out"; // Fail early if the output file or map file is not writable. If a user has a // long link, e.g. due to a large LTO link, they do not wish to run it and // find that it failed because there was a mistake in their command-line. - if (auto E = tryCreateFile(Config->OutputFile)) - error("cannot open output file " + Config->OutputFile + ": " + E.message()); - if (auto E = tryCreateFile(Config->MapFile)) - error("cannot open map file " + Config->MapFile + ": " + E.message()); + if (auto e = tryCreateFile(config->outputFile)) + error("cannot open output file " + config->outputFile + ": " + e.message()); + if (auto e = tryCreateFile(config->mapFile)) + error("cannot open map file " + config->mapFile + ": " + e.message()); if (errorCount()) return; // Use default entry point name if no name was given via the command // line nor linker scripts. For some reason, MIPS entry point name is // different from others. - Config->WarnMissingEntry = - (!Config->Entry.empty() || (!Config->Shared && !Config->Relocatable)); - if (Config->Entry.empty() && !Config->Relocatable) - Config->Entry = (Config->EMachine == EM_MIPS) ? "__start" : "_start"; + config->warnMissingEntry = + (!config->entry.empty() || (!config->shared && !config->relocatable)); + if (config->entry.empty() && !config->relocatable) + config->entry = (config->emachine == EM_MIPS) ? "__start" : "_start"; // Handle --trace-symbol. - for (auto *Arg : Args.filtered(OPT_trace_symbol)) - Symtab->trace(Arg->getValue()); + for (auto *arg : args.filtered(OPT_trace_symbol)) + symtab->insert(arg->getValue())->traced = true; // Add all files to the symbol table. This will add almost all - // symbols that we need to the symbol table. - for (InputFile *F : Files) - Symtab->addFile(F); + // symbols that we need to the symbol table. This process might + // add files to the link, via autolinking, these files are always + // appended to the Files vector. + for (size_t i = 0; i < files.size(); ++i) + parseFile(files[i]); // Now that we have every file, we can decide if we will need a // dynamic symbol table. @@ -1492,20 +1715,26 @@ template void LinkerDriver::link(opt::InputArgList &Args) { // producing a shared library. // We also need one if any shared libraries are used and for pie executables // (probably because the dynamic linker needs it). - Config->HasDynSymTab = - !SharedFiles.empty() || Config->Pic || Config->ExportDynamic; + config->hasDynSymTab = + !sharedFiles.empty() || config->isPic || config->exportDynamic; // Some symbols (such as __ehdr_start) are defined lazily only when there // are undefined symbols for them, so we add these to trigger that logic. - for (StringRef Name : Script->ReferencedSymbols) - addUndefined(Name); + for (StringRef name : script->referencedSymbols) + addUndefined(name); // Handle the `--undefined ` options. - for (StringRef S : Config->Undefined) - handleUndefined(S); + for (StringRef arg : config->undefined) + if (Symbol *sym = symtab->find(arg)) + handleUndefined(sym); // If an entry symbol is in a static archive, pull out that file now. - handleUndefined(Config->Entry); + if (Symbol *sym = symtab->find(config->entry)) + handleUndefined(sym); + + // Handle the `--undefined-glob ` options. + for (StringRef pat : args::getStrings(args, OPT_undefined_glob)) + handleUndefinedGlob(pat); // If any of our inputs are bitcode files, the LTO code generator may create // references to certain library functions that might not be explicit in the @@ -1524,9 +1753,9 @@ template void LinkerDriver::link(opt::InputArgList &Args) { // to, i.e. if the symbol's definition is in bitcode. Any other required // libcall symbols will be added to the link after LTO when we add the LTO // object file to the link. - if (!BitcodeFiles.empty()) - for (const char *S : LibcallRoutineNames) - handleLibcall(S); + if (!bitcodeFiles.empty()) + for (const char *s : libcallRoutineNames) + handleLibcall(s); // Return if there were name resolution errors. if (errorCount()) @@ -1534,27 +1763,27 @@ template void LinkerDriver::link(opt::InputArgList &Args) { // Now when we read all script files, we want to finalize order of linker // script commands, which can be not yet final because of INSERT commands. - Script->processInsertCommands(); + script->processInsertCommands(); // We want to declare linker script's symbols early, // so that we can version them. // They also might be exported if referenced by DSOs. - Script->declareSymbols(); + script->declareSymbols(); // Handle the -exclude-libs option. - if (Args.hasArg(OPT_exclude_libs)) - excludeLibs(Args); + if (args.hasArg(OPT_exclude_libs)) + excludeLibs(args); - // Create ElfHeader early. We need a dummy section in + // Create elfHeader early. We need a dummy section in // addReservedSymbols to mark the created symbols as not absolute. - Out::ElfHeader = make("", 0, SHF_ALLOC); - Out::ElfHeader->Size = sizeof(typename ELFT::Ehdr); + Out::elfHeader = make("", 0, SHF_ALLOC); + Out::elfHeader->size = sizeof(typename ELFT::Ehdr); // Create wrapped symbols for -wrap option. - std::vector Wrapped = addWrappedSymbols(Args); + std::vector wrapped = addWrappedSymbols(args); // We need to create some reserved symbols such as _end. Create them. - if (!Config->Relocatable) + if (!config->relocatable) addReservedSymbols(); // Apply version scripts. @@ -1562,84 +1791,118 @@ template void LinkerDriver::link(opt::InputArgList &Args) { // For a relocatable output, version scripts don't make sense, and // parsing a symbol version string (e.g. dropping "@ver1" from a symbol // name "foo@ver1") rather do harm, so we don't call this if -r is given. - if (!Config->Relocatable) - Symtab->scanVersionScript(); + if (!config->relocatable) + symtab->scanVersionScript(); // Do link-time optimization if given files are LLVM bitcode files. // This compiles bitcode files into real object files. // // With this the symbol table should be complete. After this, no new names // except a few linker-synthesized ones will be added to the symbol table. - Symtab->addCombinedLTOObject(); + compileBitcodeFiles(); if (errorCount()) return; // If -thinlto-index-only is given, we should create only "index // files" and not object files. Index file creation is already done // in addCombinedLTOObject, so we are done if that's the case. - if (Config->ThinLTOIndexOnly) + if (config->thinLTOIndexOnly) return; // Likewise, --plugin-opt=emit-llvm is an option to make LTO create // an output file in bitcode and exit, so that you can just get a // combined bitcode file. - if (Config->EmitLLVM) + if (config->emitLLVM) return; // Apply symbol renames for -wrap. - if (!Wrapped.empty()) - wrapSymbols(Wrapped); + if (!wrapped.empty()) + wrapSymbols(wrapped); // Now that we have a complete list of input files. // Beyond this point, no new files are added. // Aggregate all input sections into one place. - for (InputFile *F : ObjectFiles) - for (InputSectionBase *S : F->getSections()) - if (S && S != &InputSection::Discarded) - InputSections.push_back(S); - for (BinaryFile *F : BinaryFiles) - for (InputSectionBase *S : F->getSections()) - InputSections.push_back(cast(S)); - - // We do not want to emit debug sections if --strip-all - // or -strip-debug are given. - if (Config->Strip != StripPolicy::None) - llvm::erase_if(InputSections, [](InputSectionBase *S) { - return S->Name.startswith(".debug") || S->Name.startswith(".zdebug"); - }); - - Config->EFlags = Target->calcEFlags(); - - if (Config->EMachine == EM_ARM) { + for (InputFile *f : objectFiles) + for (InputSectionBase *s : f->getSections()) + if (s && s != &InputSection::discarded) + inputSections.push_back(s); + for (BinaryFile *f : binaryFiles) + for (InputSectionBase *s : f->getSections()) + inputSections.push_back(cast(s)); + + llvm::erase_if(inputSections, [](InputSectionBase *s) { + if (s->type == SHT_LLVM_SYMPART) { + readSymbolPartitionSection(s); + return true; + } + + // We do not want to emit debug sections if --strip-all + // or -strip-debug are given. + return config->strip != StripPolicy::None && + (s->name.startswith(".debug") || s->name.startswith(".zdebug")); + }); + + // Now that the number of partitions is fixed, save a pointer to the main + // partition. + mainPart = &partitions[0]; + + // Read .note.gnu.property sections from input object files which + // contain a hint to tweak linker's and loader's behaviors. + config->andFeatures = getAndFeatures(); + + // The Target instance handles target-specific stuff, such as applying + // relocations or writing a PLT section. It also contains target-dependent + // values such as a default image base address. + target = getTarget(); + + config->eflags = target->calcEFlags(); + // maxPageSize (sometimes called abi page size) is the maximum page size that + // the output can be run on. For example if the OS can use 4k or 64k page + // sizes then maxPageSize must be 64k for the output to be useable on both. + // All important alignment decisions must use this value. + config->maxPageSize = getMaxPageSize(args); + // commonPageSize is the most common page size that the output will be run on. + // For example if an OS can use 4k or 64k page sizes and 4k is more common + // than 64k then commonPageSize is set to 4k. commonPageSize can be used for + // optimizations such as DATA_SEGMENT_ALIGN in linker scripts. LLD's use of it + // is limited to writing trap instructions on the last executable segment. + config->commonPageSize = getCommonPageSize(args); + + config->imageBase = getImageBase(args); + + if (config->emachine == EM_ARM) { // FIXME: These warnings can be removed when lld only uses these features // when the input objects have been compiled with an architecture that // supports them. - if (Config->ARMHasBlx == false) + if (config->armHasBlx == false) warn("lld uses blx instruction, no object with architecture supporting " "feature detected"); } // This adds a .comment section containing a version string. We have to add it // before mergeSections because the .comment section is a mergeable section. - if (!Config->Relocatable) - InputSections.push_back(createCommentSection()); + if (!config->relocatable) + inputSections.push_back(createCommentSection()); + + // Replace common symbols with regular symbols. + replaceCommonSymbols(); // Do size optimizations: garbage collection, merging of SHF_MERGE sections // and identical code folding. splitSections(); markLive(); - demoteSharedSymbols(); + demoteSharedSymbols(); mergeSections(); - if (Config->ICF != ICFLevel::None) { - findKeepUniqueSections(Args); + if (config->icf != ICFLevel::None) { + findKeepUniqueSections(args); doIcf(); } // Read the callgraph now that we know what was gced or icfed - if (Config->CallGraphProfileSort) { - if (auto *Arg = Args.getLastArg(OPT_call_graph_ordering_file)) - if (Optional Buffer = readFile(Arg->getValue())) - readCallGraph(*Buffer); + if (config->callGraphProfileSort) { + if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file)) + if (Optional buffer = readFile(arg->getValue())) + readCallGraph(*buffer); readCallGraphsFromObjectFiles(); } diff --git a/ELF/Driver.h b/ELF/Driver.h index 81d7f60..3115e28 100644 --- a/ELF/Driver.h +++ b/ELF/Driver.h @@ -1,15 +1,15 @@ //===- 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 // //===----------------------------------------------------------------------===// #ifndef LLD_ELF_DRIVER_H #define LLD_ELF_DRIVER_H +#include "LTO.h" #include "SymbolTable.h" #include "lld/Common/LLVM.h" #include "lld/Common/Reproduce.h" @@ -22,34 +22,37 @@ namespace lld { namespace elf { -extern class LinkerDriver *Driver; +extern class LinkerDriver *driver; class LinkerDriver { public: - void main(ArrayRef Args); - void addFile(StringRef Path, bool WithLOption); - void addLibrary(StringRef Name); + void main(ArrayRef args); + void addFile(StringRef path, bool withLOption); + void addLibrary(StringRef name); private: - void readConfigs(llvm::opt::InputArgList &Args); - void createFiles(llvm::opt::InputArgList &Args); + void createFiles(llvm::opt::InputArgList &args); void inferMachineType(); - template void link(llvm::opt::InputArgList &Args); + template void link(llvm::opt::InputArgList &args); + template void compileBitcodeFiles(); // True if we are in --whole-archive and --no-whole-archive. - bool InWholeArchive = false; + bool inWholeArchive = false; // True if we are in --start-lib and --end-lib. - bool InLib = false; + bool inLib = false; + + // For LTO. + std::unique_ptr lto; - std::vector Files; + std::vector files; }; // Parses command line options. class ELFOptTable : public llvm::opt::OptTable { public: ELFOptTable(); - llvm::opt::InputArgList parse(ArrayRef Argv); + llvm::opt::InputArgList parse(ArrayRef argv); }; // Create enum with OPT_xxx values for each option in Options.td @@ -61,11 +64,12 @@ enum { }; void printHelp(); -std::string createResponseFile(const llvm::opt::InputArgList &Args); +std::string createResponseFile(const llvm::opt::InputArgList &args); -llvm::Optional findFromSearchPaths(StringRef Path); -llvm::Optional searchScript(StringRef Path); -llvm::Optional searchLibrary(StringRef Path); +llvm::Optional findFromSearchPaths(StringRef path); +llvm::Optional searchScript(StringRef path); +llvm::Optional searchLibraryBaseName(StringRef path); +llvm::Optional searchLibrary(StringRef path); } // namespace elf } // namespace lld diff --git a/ELF/DriverUtils.cpp b/ELF/DriverUtils.cpp index e51d02e..87f0aa2 100644 --- a/ELF/DriverUtils.cpp +++ b/ELF/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 // //===----------------------------------------------------------------------===// // @@ -42,7 +41,7 @@ using namespace lld::elf; #undef PREFIX // Create table mapping all options defined in Options.td -static const opt::OptTable::Info OptInfo[] = { +static const opt::OptTable::Info optInfo[] = { #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \ X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12}, @@ -50,36 +49,36 @@ static const opt::OptTable::Info OptInfo[] = { #undef OPTION }; -ELFOptTable::ELFOptTable() : OptTable(OptInfo) {} +ELFOptTable::ELFOptTable() : OptTable(optInfo) {} // Set color diagnostics according to -color-diagnostics={auto,always,never} // or -no-color-diagnostics flags. -static void handleColorDiagnostics(opt::InputArgList &Args) { - auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq, +static void handleColorDiagnostics(opt::InputArgList &args) { + auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq, OPT_no_color_diagnostics); - if (!Arg) + if (!arg) return; - if (Arg->getOption().getID() == OPT_color_diagnostics) { - errorHandler().ColorDiagnostics = true; - } else if (Arg->getOption().getID() == OPT_no_color_diagnostics) { - errorHandler().ColorDiagnostics = false; + if (arg->getOption().getID() == OPT_color_diagnostics) { + errorHandler().colorDiagnostics = true; + } else if (arg->getOption().getID() == OPT_no_color_diagnostics) { + errorHandler().colorDiagnostics = false; } else { - StringRef S = Arg->getValue(); - if (S == "always") - errorHandler().ColorDiagnostics = true; - else if (S == "never") - errorHandler().ColorDiagnostics = false; - else if (S != "auto") - error("unknown option: --color-diagnostics=" + S); + StringRef s = arg->getValue(); + if (s == "always") + errorHandler().colorDiagnostics = true; + else if (s == "never") + errorHandler().colorDiagnostics = false; + else if (s != "auto") + error("unknown option: --color-diagnostics=" + s); } } -static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) { - if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) { - StringRef S = Arg->getValue(); - if (S != "windows" && S != "posix") - error("invalid response file quoting: " + S); - if (S == "windows") +static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) { + if (auto *arg = args.getLastArg(OPT_rsp_quoting)) { + StringRef s = arg->getValue(); + if (s != "windows" && s != "posix") + error("invalid response file quoting: " + s); + if (s == "windows") return cl::TokenizeWindowsCommandLine; return cl::TokenizeGNUCommandLine; } @@ -97,50 +96,56 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) { // `--plugin-opt ` is converted to `--plugin-opt=`. This is a // bit hacky, but looks like it is still better than handling --plugin-opt // options by hand. -static void concatLTOPluginOptions(SmallVectorImpl &Args) { - SmallVector V; - for (size_t I = 0, E = Args.size(); I != E; ++I) { - StringRef S = Args[I]; - if ((S == "-plugin-opt" || S == "--plugin-opt") && I + 1 != E) { - V.push_back(Saver.save(S + "=" + Args[I + 1]).data()); - ++I; +static void concatLTOPluginOptions(SmallVectorImpl &args) { + SmallVector v; + for (size_t i = 0, e = args.size(); i != e; ++i) { + StringRef s = args[i]; + if ((s == "-plugin-opt" || s == "--plugin-opt") && i + 1 != e) { + v.push_back(saver.save(s + "=" + args[i + 1]).data()); + ++i; } else { - V.push_back(Args[I]); + v.push_back(args[i]); } } - Args = std::move(V); + args = std::move(v); } // Parses a given list of options. -opt::InputArgList ELFOptTable::parse(ArrayRef Argv) { +opt::InputArgList ELFOptTable::parse(ArrayRef argv) { // Make InputArgList from string vectors. - unsigned MissingIndex; - unsigned MissingCount; - SmallVector Vec(Argv.data(), Argv.data() + Argv.size()); + unsigned missingIndex; + unsigned missingCount; + SmallVector vec(argv.data(), argv.data() + argv.size()); // We need to get the quoting style for response files before parsing all // options so we parse here before and ignore all the options but // --rsp-quoting. - opt::InputArgList Args = this->ParseArgs(Vec, MissingIndex, MissingCount); + opt::InputArgList args = this->ParseArgs(vec, missingIndex, missingCount); // Expand response files (arguments in the form of @) // and then parse the argument again. - cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec); - concatLTOPluginOptions(Vec); - Args = this->ParseArgs(Vec, MissingIndex, MissingCount); - - handleColorDiagnostics(Args); - if (MissingCount) - error(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); - - for (auto *Arg : Args.filtered(OPT_UNKNOWN)) - error("unknown argument: " + Arg->getSpelling()); - return Args; + cl::ExpandResponseFiles(saver, getQuotingStyle(args), vec); + concatLTOPluginOptions(vec); + args = this->ParseArgs(vec, missingIndex, missingCount); + + handleColorDiagnostics(args); + if (missingCount) + error(Twine(args.getArgString(missingIndex)) + ": missing argument"); + + for (auto *arg : args.filtered(OPT_UNKNOWN)) { + std::string nearest; + if (findNearest(arg->getAsString(args), nearest) > 1) + error("unknown argument '" + arg->getAsString(args) + "'"); + else + error("unknown argument '" + arg->getAsString(args) + + "', did you mean '" + nearest + "'"); + } + return args; } void elf::printHelp() { ELFOptTable().PrintHelp( - outs(), (Config->ProgName + " [options] file...").str().c_str(), "lld", + outs(), (config->progName + " [options] file...").str().c_str(), "lld", false /*ShowHidden*/, true /*ShowAllAliases*/); outs() << "\n"; @@ -149,30 +154,36 @@ void elf::printHelp() { // in a message for the -help option. If it doesn't match, the scripts // assume that the linker doesn't support very basic features such as // shared libraries. Therefore, we need to print out at least "elf". - outs() << Config->ProgName << ": supported targets: elf\n"; + outs() << config->progName << ": supported targets: elf\n"; +} + +static std::string rewritePath(StringRef s) { + if (fs::exists(s)) + return relativeToRoot(s); + return s; } // Reconstructs command line arguments so that so that you can re-run // the same command with the same inputs. This is for --reproduce. -std::string elf::createResponseFile(const opt::InputArgList &Args) { - SmallString<0> Data; - raw_svector_ostream OS(Data); - OS << "--chroot .\n"; +std::string elf::createResponseFile(const opt::InputArgList &args) { + SmallString<0> data; + raw_svector_ostream os(data); + os << "--chroot .\n"; // Copy the command line to the output while rewriting paths. - for (auto *Arg : Args) { - switch (Arg->getOption().getUnaliasedOption().getID()) { + for (auto *arg : args) { + switch (arg->getOption().getID()) { case OPT_reproduce: break; case OPT_INPUT: - OS << quote(rewritePath(Arg->getValue())) << "\n"; + os << quote(rewritePath(arg->getValue())) << "\n"; break; case OPT_o: // If -o path contains directories, "lld @response.txt" will likely // fail because the archive we are creating doesn't contain empty // directories for the output path (-o doesn't create directories). // Strip directories to prevent the issue. - OS << "-o " << quote(sys::path::filename(Arg->getValue())) << "\n"; + os << "-o " << quote(sys::path::filename(arg->getValue())) << "\n"; break; case OPT_dynamic_list: case OPT_library_path: @@ -181,58 +192,62 @@ std::string elf::createResponseFile(const opt::InputArgList &Args) { case OPT_symbol_ordering_file: case OPT_sysroot: case OPT_version_script: - OS << Arg->getSpelling() << " " << quote(rewritePath(Arg->getValue())) + os << arg->getSpelling() << " " << quote(rewritePath(arg->getValue())) << "\n"; break; default: - OS << toString(*Arg) << "\n"; + os << toString(*arg) << "\n"; } } - return Data.str(); + return data.str(); } // Find a file by concatenating given paths. If a resulting path // starts with "=", the character is replaced with a --sysroot value. -static Optional findFile(StringRef Path1, const Twine &Path2) { - SmallString<128> S; - if (Path1.startswith("=")) - path::append(S, Config->Sysroot, Path1.substr(1), Path2); +static Optional findFile(StringRef path1, const Twine &path2) { + SmallString<128> s; + if (path1.startswith("=")) + path::append(s, config->sysroot, path1.substr(1), path2); else - path::append(S, Path1, Path2); + path::append(s, path1, path2); - if (fs::exists(S)) - return S.str().str(); + if (fs::exists(s)) + return s.str().str(); return None; } -Optional elf::findFromSearchPaths(StringRef Path) { - for (StringRef Dir : Config->SearchPaths) - if (Optional S = findFile(Dir, Path)) - return S; +Optional elf::findFromSearchPaths(StringRef path) { + for (StringRef dir : config->searchPaths) + if (Optional s = findFile(dir, path)) + return s; return None; } -// This is for -lfoo. We'll look for libfoo.so or libfoo.a from +// This is for -l. We'll look for lib.so or lib.a from // search paths. -Optional elf::searchLibrary(StringRef Name) { - if (Name.startswith(":")) - return findFromSearchPaths(Name.substr(1)); - - for (StringRef Dir : Config->SearchPaths) { - if (!Config->Static) - if (Optional S = findFile(Dir, "lib" + Name + ".so")) - return S; - if (Optional S = findFile(Dir, "lib" + Name + ".a")) - return S; +Optional elf::searchLibraryBaseName(StringRef name) { + for (StringRef dir : config->searchPaths) { + if (!config->isStatic) + if (Optional s = findFile(dir, "lib" + name + ".so")) + return s; + if (Optional s = findFile(dir, "lib" + name + ".a")) + return s; } return None; } +// This is for -l. +Optional elf::searchLibrary(StringRef name) { + if (name.startswith(":")) + return findFromSearchPaths(name.substr(1)); + return searchLibraryBaseName (name); +} + // If a linker/version script doesn't exist in the current directory, we also // look for the script in the '-L' search paths. This matches the behaviour of // '-T', --version-script=, and linker script INPUT() command in ld.bfd. -Optional elf::searchScript(StringRef Name) { - if (fs::exists(Name)) - return Name.str(); - return findFromSearchPaths(Name); +Optional elf::searchScript(StringRef name) { + if (fs::exists(name)) + return name.str(); + return findFromSearchPaths(name); } diff --git a/ELF/EhFrame.cpp b/ELF/EhFrame.cpp index 95d444b..b3245dd 100644 --- a/ELF/EhFrame.cpp +++ b/ELF/EhFrame.cpp @@ -1,9 +1,8 @@ //===- EhFrame.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 // //===----------------------------------------------------------------------===// // @@ -37,72 +36,72 @@ using namespace lld::elf; namespace { class EhReader { public: - EhReader(InputSectionBase *S, ArrayRef D) : IS(S), D(D) {} + EhReader(InputSectionBase *s, ArrayRef d) : isec(s), d(d) {} size_t readEhRecordSize(); uint8_t getFdeEncoding(); private: - template void failOn(const P *Loc, const Twine &Msg) { - fatal("corrupted .eh_frame: " + Msg + "\n>>> defined in " + - IS->getObjMsg((const uint8_t *)Loc - IS->data().data())); + template void failOn(const P *loc, const Twine &msg) { + fatal("corrupted .eh_frame: " + msg + "\n>>> defined in " + + isec->getObjMsg((const uint8_t *)loc - isec->data().data())); } uint8_t readByte(); - void skipBytes(size_t Count); + void skipBytes(size_t count); StringRef readString(); void skipLeb128(); void skipAugP(); - InputSectionBase *IS; - ArrayRef D; + InputSectionBase *isec; + ArrayRef d; }; } -size_t elf::readEhRecordSize(InputSectionBase *S, size_t Off) { - return EhReader(S, S->data().slice(Off)).readEhRecordSize(); +size_t elf::readEhRecordSize(InputSectionBase *s, size_t off) { + return EhReader(s, s->data().slice(off)).readEhRecordSize(); } // .eh_frame section is a sequence of records. Each record starts with // a 4 byte length field. This function reads the length. size_t EhReader::readEhRecordSize() { - if (D.size() < 4) - failOn(D.data(), "CIE/FDE too small"); + if (d.size() < 4) + failOn(d.data(), "CIE/FDE too small"); // First 4 bytes of CIE/FDE is the size of the record. // If it is 0xFFFFFFFF, the next 8 bytes contain the size instead, // but we do not support that format yet. - uint64_t V = read32(D.data()); - if (V == UINT32_MAX) - failOn(D.data(), "CIE/FDE too large"); - uint64_t Size = V + 4; - if (Size > D.size()) - failOn(D.data(), "CIE/FDE ends past the end of the section"); - return Size; + uint64_t v = read32(d.data()); + if (v == UINT32_MAX) + failOn(d.data(), "CIE/FDE too large"); + uint64_t size = v + 4; + if (size > d.size()) + failOn(d.data(), "CIE/FDE ends past the end of the section"); + return size; } // Read a byte and advance D by one byte. uint8_t EhReader::readByte() { - if (D.empty()) - failOn(D.data(), "unexpected end of CIE"); - uint8_t B = D.front(); - D = D.slice(1); - return B; + if (d.empty()) + failOn(d.data(), "unexpected end of CIE"); + uint8_t b = d.front(); + d = d.slice(1); + return b; } -void EhReader::skipBytes(size_t Count) { - if (D.size() < Count) - failOn(D.data(), "CIE is too small"); - D = D.slice(Count); +void EhReader::skipBytes(size_t count) { + if (d.size() < count) + failOn(d.data(), "CIE is too small"); + d = d.slice(count); } // Read a null-terminated string. StringRef EhReader::readString() { - const uint8_t *End = std::find(D.begin(), D.end(), '\0'); - if (End == D.end()) - failOn(D.data(), "corrupted CIE (failed to read string)"); - StringRef S = toStringRef(D.slice(0, End - D.begin())); - D = D.slice(S.size() + 1); - return S; + const uint8_t *end = llvm::find(d, '\0'); + if (end == d.end()) + failOn(d.data(), "corrupted CIE (failed to read string)"); + StringRef s = toStringRef(d.slice(0, end - d.begin())); + d = d.slice(s.size() + 1); + return s; } // Skip an integer encoded in the LEB128 format. @@ -110,21 +109,21 @@ StringRef EhReader::readString() { // But we need to be at least able to skip it so that we can read // the field that follows a LEB128 number. void EhReader::skipLeb128() { - const uint8_t *ErrPos = D.data(); - while (!D.empty()) { - uint8_t Val = D.front(); - D = D.slice(1); - if ((Val & 0x80) == 0) + const uint8_t *errPos = d.data(); + while (!d.empty()) { + uint8_t val = d.front(); + d = d.slice(1); + if ((val & 0x80) == 0) return; } - failOn(ErrPos, "corrupted CIE (failed to read LEB128)"); + failOn(errPos, "corrupted CIE (failed to read LEB128)"); } -static size_t getAugPSize(unsigned Enc) { - switch (Enc & 0x0f) { +static size_t getAugPSize(unsigned enc) { + switch (enc & 0x0f) { case DW_EH_PE_absptr: case DW_EH_PE_signed: - return Config->Wordsize; + return config->wordsize; case DW_EH_PE_udata2: case DW_EH_PE_sdata2: return 2; @@ -139,29 +138,29 @@ static size_t getAugPSize(unsigned Enc) { } void EhReader::skipAugP() { - uint8_t Enc = readByte(); - if ((Enc & 0xf0) == DW_EH_PE_aligned) - failOn(D.data() - 1, "DW_EH_PE_aligned encoding is not supported"); - size_t Size = getAugPSize(Enc); - if (Size == 0) - failOn(D.data() - 1, "unknown FDE encoding"); - if (Size >= D.size()) - failOn(D.data() - 1, "corrupted CIE"); - D = D.slice(Size); + uint8_t enc = readByte(); + if ((enc & 0xf0) == DW_EH_PE_aligned) + failOn(d.data() - 1, "DW_EH_PE_aligned encoding is not supported"); + size_t size = getAugPSize(enc); + if (size == 0) + failOn(d.data() - 1, "unknown FDE encoding"); + if (size >= d.size()) + failOn(d.data() - 1, "corrupted CIE"); + d = d.slice(size); } -uint8_t elf::getFdeEncoding(EhSectionPiece *P) { - return EhReader(P->Sec, P->data()).getFdeEncoding(); +uint8_t elf::getFdeEncoding(EhSectionPiece *p) { + return EhReader(p->sec, p->data()).getFdeEncoding(); } uint8_t EhReader::getFdeEncoding() { skipBytes(8); - int Version = readByte(); - if (Version != 1 && Version != 3) - failOn(D.data() - 1, - "FDE version 1 or 3 expected, but got " + Twine(Version)); + int version = readByte(); + if (version != 1 && version != 3) + failOn(d.data() - 1, + "FDE version 1 or 3 expected, but got " + Twine(version)); - StringRef Aug = readString(); + StringRef aug = readString(); // Skip code and data alignment factors. skipLeb128(); @@ -169,7 +168,7 @@ uint8_t EhReader::getFdeEncoding() { // Skip the return address register. In CIE version 1 this is a single // byte. In CIE version 3 this is an unsigned LEB128. - if (Version == 1) + if (version == 1) readByte(); else skipLeb128(); @@ -177,22 +176,22 @@ uint8_t EhReader::getFdeEncoding() { // We only care about an 'R' value, but other records may precede an 'R' // record. Unfortunately records are not in TLV (type-length-value) format, // so we need to teach the linker how to skip records for each type. - for (char C : Aug) { - if (C == 'R') + for (char c : aug) { + if (c == 'R') return readByte(); - if (C == 'z') { + if (c == 'z') { skipLeb128(); continue; } - if (C == 'P') { + if (c == 'P') { skipAugP(); continue; } - if (C == 'L') { + if (c == 'L') { readByte(); continue; } - failOn(Aug.data(), "unknown .eh_frame augmentation string: " + Aug); + failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug); } return DW_EH_PE_absptr; } diff --git a/ELF/EhFrame.h b/ELF/EhFrame.h index 5112891..20dd612 100644 --- a/ELF/EhFrame.h +++ b/ELF/EhFrame.h @@ -1,9 +1,8 @@ //===- EhFrame.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 // //===----------------------------------------------------------------------===// @@ -17,8 +16,8 @@ namespace elf { class InputSectionBase; struct EhSectionPiece; -size_t readEhRecordSize(InputSectionBase *S, size_t Off); -uint8_t getFdeEncoding(EhSectionPiece *P); +size_t readEhRecordSize(InputSectionBase *s, size_t off); +uint8_t getFdeEncoding(EhSectionPiece *p); } // namespace elf } // namespace lld diff --git a/ELF/Filesystem.cpp b/ELF/Filesystem.cpp deleted file mode 100644 index 5cf240e..0000000 --- a/ELF/Filesystem.cpp +++ /dev/null @@ -1,86 +0,0 @@ -//===- Filesystem.cpp -----------------------------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file contains a few utility functions to handle files. -// -//===----------------------------------------------------------------------===// - -#include "Filesystem.h" -#include "Config.h" -#include "lld/Common/Threads.h" -#include "llvm/Config/llvm-config.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/FileSystem.h" -#if LLVM_ON_UNIX -#include -#endif -#include - -using namespace llvm; - -using namespace lld; -using namespace lld::elf; - -// Removes a given file asynchronously. This is a performance hack, -// so remove this when operating systems are improved. -// -// On Linux (and probably on other Unix-like systems), unlink(2) is a -// noticeably slow system call. As of 2016, unlink takes 250 -// milliseconds to remove a 1 GB file on ext4 filesystem on my machine. -// -// To create a new result file, we first remove existing file. So, if -// you repeatedly link a 1 GB program in a regular compile-link-debug -// cycle, every cycle wastes 250 milliseconds only to remove a file. -// Since LLD can link a 1 GB binary in about 5 seconds, that waste -// actually counts. -// -// This function spawns a background thread to remove the file. -// The calling thread returns almost immediately. -void elf::unlinkAsync(StringRef Path) { -// Removing a file is async on windows. -#if defined(_WIN32) - sys::fs::remove(Path); -#else - if (!ThreadsEnabled || !sys::fs::exists(Path) || - !sys::fs::is_regular_file(Path)) - return; - - // We cannot just remove path from a different thread because we are now going - // to create path as a new file. - // Instead we open the file and unlink it on this thread. The unlink is fast - // since the open fd guarantees that it is not removing the last reference. - int FD; - std::error_code EC = sys::fs::openFileForRead(Path, FD); - sys::fs::remove(Path); - - // close and therefore remove TempPath in background. - if (!EC) - std::thread([=] { ::close(FD); }).detach(); -#endif -} - -// Simulate file creation to see if Path is writable. -// -// Determining whether a file is writable or not is amazingly hard, -// and after all the only reliable way of doing that is to actually -// create a file. But we don't want to do that in this function -// because LLD shouldn't update any file if it will end in a failure. -// We also don't want to reimplement heuristics to determine if a -// file is writable. So we'll let FileOutputBuffer do the work. -// -// FileOutputBuffer doesn't touch a desitnation file until commit() -// is called. We use that class without calling commit() to predict -// if the given file is writable. -std::error_code elf::tryCreateFile(StringRef Path) { - if (Path.empty()) - return std::error_code(); - if (Path == "-") - return std::error_code(); - return errorToErrorCode(FileOutputBuffer::create(Path, 1).takeError()); -} diff --git a/ELF/Filesystem.h b/ELF/Filesystem.h deleted file mode 100644 index 987a74a..0000000 --- a/ELF/Filesystem.h +++ /dev/null @@ -1,23 +0,0 @@ -//===- Filesystem.h ---------------------------------------------*- C++ -*-===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLD_ELF_FILESYSTEM_H -#define LLD_ELF_FILESYSTEM_H - -#include "lld/Common/LLVM.h" -#include - -namespace lld { -namespace elf { -void unlinkAsync(StringRef Path); -std::error_code tryCreateFile(StringRef Path); -} // namespace elf -} // namespace lld - -#endif diff --git a/ELF/ICF.cpp b/ELF/ICF.cpp index e917ae7..8b01d06 100644 --- a/ELF/ICF.cpp +++ b/ELF/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 // //===----------------------------------------------------------------------===// // @@ -99,33 +98,33 @@ public: void run(); private: - void segregate(size_t Begin, size_t End, bool Constant); + void segregate(size_t begin, size_t end, bool constant); template - bool constantEq(const InputSection *A, ArrayRef RelsA, - const InputSection *B, ArrayRef RelsB); + bool constantEq(const InputSection *a, ArrayRef relsA, + const InputSection *b, ArrayRef relsB); template - bool variableEq(const InputSection *A, ArrayRef RelsA, - const InputSection *B, ArrayRef RelsB); + bool variableEq(const InputSection *a, ArrayRef relsA, + const InputSection *b, ArrayRef relsB); - bool equalsConstant(const InputSection *A, const InputSection *B); - bool equalsVariable(const InputSection *A, const InputSection *B); + bool equalsConstant(const InputSection *a, const InputSection *b); + bool equalsVariable(const InputSection *a, const InputSection *b); - 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, - llvm::function_ref Fn); + void forEachClassRange(size_t begin, size_t end, + llvm::function_ref fn); - void forEachClass(llvm::function_ref Fn); + void forEachClass(llvm::function_ref fn); - std::vector Sections; + std::vector sections; // We repeat the main loop while `Repeat` is true. - std::atomic Repeat; + std::atomic repeat; // The main loop counter. - int Cnt = 0; + int cnt = 0; // We have two locations for equivalence classes. On the first iteration // of the main loop, Class[0] has a valid value, and Class[1] contains @@ -151,42 +150,42 @@ private: // because we can safely read the next class without worrying about race // conditions. Using the same location makes this algorithm converge // faster because it uses results of the same iteration earlier. - int Current = 0; - int Next = 0; + int current = 0; + int next = 0; }; } // Returns true if section S is subject of ICF. -static bool isEligible(InputSection *S) { - if (!S->Live || S->KeepUnique || !(S->Flags & SHF_ALLOC)) +static bool isEligible(InputSection *s) { + if (!s->isLive() || s->keepUnique || !(s->flags & SHF_ALLOC)) return false; // Don't merge writable sections. .data.rel.ro sections are marked as writable // but are semantically read-only. - if ((S->Flags & SHF_WRITE) && S->Name != ".data.rel.ro" && - !S->Name.startswith(".data.rel.ro.")) + if ((s->flags & SHF_WRITE) && s->name != ".data.rel.ro" && + !s->name.startswith(".data.rel.ro.")) return false; // SHF_LINK_ORDER sections are ICF'd as a unit with their dependent sections, // so we don't consider them for ICF individually. - if (S->Flags & SHF_LINK_ORDER) + if (s->flags & SHF_LINK_ORDER) return false; // Don't merge synthetic sections as their Data member is not valid and empty. // The Data member needs to be valid for ICF as it is used by ICF to determine // the equality of section contents. - if (isa(S)) + if (isa(s)) return false; // .init and .fini contains instructions that must be executed to initialize // and finalize the process. They cannot and should not be merged. - if (S->Name == ".init" || S->Name == ".fini") + if (s->name == ".init" || s->name == ".fini") return false; // A user program may enumerate sections named with a C identifier using // __start_* and __stop_* symbols. We cannot ICF any such sections because // that could change program semantics. - if (isValidCIdentifier(S->Name)) + if (isValidCIdentifier(s->name)) return false; return true; @@ -194,7 +193,7 @@ static bool isEligible(InputSection *S) { // Split an equivalence class into smaller classes. template -void ICF::segregate(size_t Begin, size_t End, bool Constant) { +void ICF::segregate(size_t begin, size_t end, bool constant) { // This loop rearranges sections in [Begin, End) so that all sections // that are equal in terms of equals{Constant,Variable} are contiguous // in [Begin, End). @@ -203,93 +202,93 @@ void ICF::segregate(size_t Begin, size_t End, bool Constant) { // issue in practice because the number of the distinct sections in // each range is usually very small. - while (Begin < End) { + while (begin < end) { // Divide [Begin, End) into two. Let Mid be the start index of the // second group. - auto Bound = - std::stable_partition(Sections.begin() + Begin + 1, - Sections.begin() + End, [&](InputSection *S) { - if (Constant) - return equalsConstant(Sections[Begin], S); - return equalsVariable(Sections[Begin], S); + auto bound = + std::stable_partition(sections.begin() + begin + 1, + sections.begin() + end, [&](InputSection *s) { + if (constant) + return equalsConstant(sections[begin], s); + return equalsVariable(sections[begin], s); }); - size_t Mid = Bound - Sections.begin(); + size_t mid = bound - sections.begin(); // Now we split [Begin, End) into [Begin, Mid) and [Mid, End) by // updating the sections in [Begin, Mid). We use Mid as an equivalence // class ID because every group ends with a unique index. - for (size_t I = Begin; I < Mid; ++I) - Sections[I]->Class[Next] = Mid; + for (size_t i = begin; i < mid; ++i) + sections[i]->eqClass[next] = 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; } } // Compare two lists of relocations. template template -bool ICF::constantEq(const InputSection *SecA, ArrayRef RA, - const InputSection *SecB, ArrayRef RB) { - for (size_t I = 0; I < RA.size(); ++I) { - if (RA[I].r_offset != RB[I].r_offset || - RA[I].getType(Config->IsMips64EL) != RB[I].getType(Config->IsMips64EL)) +bool ICF::constantEq(const InputSection *secA, ArrayRef ra, + const InputSection *secB, ArrayRef rb) { + for (size_t i = 0; i < ra.size(); ++i) { + if (ra[i].r_offset != rb[i].r_offset || + ra[i].getType(config->isMips64EL) != rb[i].getType(config->isMips64EL)) return false; - uint64_t AddA = getAddend(RA[I]); - uint64_t AddB = getAddend(RB[I]); + uint64_t addA = getAddend(ra[i]); + uint64_t addB = getAddend(rb[i]); - Symbol &SA = SecA->template getFile()->getRelocTargetSym(RA[I]); - Symbol &SB = SecB->template getFile()->getRelocTargetSym(RB[I]); - if (&SA == &SB) { - if (AddA == AddB) + Symbol &sa = secA->template getFile()->getRelocTargetSym(ra[i]); + Symbol &sb = secB->template getFile()->getRelocTargetSym(rb[i]); + if (&sa == &sb) { + if (addA == addB) continue; return false; } - auto *DA = dyn_cast(&SA); - auto *DB = dyn_cast(&SB); + auto *da = dyn_cast(&sa); + auto *db = dyn_cast(&sb); // Placeholder symbols generated by linker scripts look the same now but // may have different values later. - if (!DA || !DB || DA->ScriptDefined || DB->ScriptDefined) + if (!da || !db || da->scriptDefined || db->scriptDefined) return false; // Relocations referring to absolute symbols are constant-equal if their // values are equal. - if (!DA->Section && !DB->Section && DA->Value + AddA == DB->Value + AddB) + if (!da->section && !db->section && da->value + addA == db->value + addB) continue; - if (!DA->Section || !DB->Section) + if (!da->section || !db->section) return false; - if (DA->Section->kind() != DB->Section->kind()) + if (da->section->kind() != db->section->kind()) return false; // Relocations referring to InputSections are constant-equal if their // section offsets are equal. - if (isa(DA->Section)) { - if (DA->Value + AddA == DB->Value + AddB) + if (isa(da->section)) { + if (da->value + addA == db->value + addB) continue; return false; } // Relocations referring to MergeInputSections are constant-equal if their // offsets in the output section are equal. - auto *X = dyn_cast(DA->Section); - if (!X) + auto *x = dyn_cast(da->section); + if (!x) return false; - auto *Y = cast(DB->Section); - if (X->getParent() != Y->getParent()) + auto *y = cast(db->section); + if (x->getParent() != y->getParent()) return false; - uint64_t OffsetA = - SA.isSection() ? X->getOffset(AddA) : X->getOffset(DA->Value) + AddA; - uint64_t OffsetB = - SB.isSection() ? Y->getOffset(AddB) : Y->getOffset(DB->Value) + AddB; - if (OffsetA != OffsetB) + uint64_t offsetA = + sa.isSection() ? x->getOffset(addA) : x->getOffset(da->value) + addA; + uint64_t offsetB = + sb.isSection() ? y->getOffset(addB) : y->getOffset(db->value) + addB; + if (offsetA != offsetB) return false; } @@ -299,57 +298,57 @@ bool ICF::constantEq(const InputSection *SecA, ArrayRef RA, // Compare "non-moving" part of two InputSections, namely everything // except relocation targets. template -bool ICF::equalsConstant(const InputSection *A, const InputSection *B) { - if (A->NumRelocations != B->NumRelocations || A->Flags != B->Flags || - A->getSize() != B->getSize() || A->data() != B->data()) +bool ICF::equalsConstant(const InputSection *a, const InputSection *b) { + if (a->numRelocations != b->numRelocations || a->flags != b->flags || + a->getSize() != b->getSize() || a->data() != b->data()) return false; // If two sections have different output sections, we cannot merge them. // FIXME: This doesn't do the right thing in the case where there is a linker // script. We probably need to move output section assignment before ICF to // get the correct behaviour here. - if (getOutputSectionName(A) != getOutputSectionName(B)) + if (getOutputSectionName(a) != getOutputSectionName(b)) return false; - if (A->AreRelocsRela) - return constantEq(A, A->template relas(), B, - B->template relas()); - return constantEq(A, A->template rels(), B, B->template rels()); + if (a->areRelocsRela) + return constantEq(a, a->template relas(), b, + b->template relas()); + return constantEq(a, a->template rels(), b, b->template rels()); } // Compare two lists of relocations. Returns true if all pairs of // relocations point to the same section in terms of ICF. template template -bool ICF::variableEq(const InputSection *SecA, ArrayRef RA, - const InputSection *SecB, ArrayRef RB) { - assert(RA.size() == RB.size()); +bool ICF::variableEq(const InputSection *secA, ArrayRef ra, + const InputSection *secB, ArrayRef rb) { + assert(ra.size() == rb.size()); - for (size_t I = 0; I < RA.size(); ++I) { + for (size_t i = 0; i < ra.size(); ++i) { // The two sections must be identical. - Symbol &SA = SecA->template getFile()->getRelocTargetSym(RA[I]); - Symbol &SB = SecB->template getFile()->getRelocTargetSym(RB[I]); - if (&SA == &SB) + Symbol &sa = secA->template getFile()->getRelocTargetSym(ra[i]); + Symbol &sb = secB->template getFile()->getRelocTargetSym(rb[i]); + if (&sa == &sb) continue; - auto *DA = cast(&SA); - auto *DB = cast(&SB); + auto *da = cast(&sa); + auto *db = cast(&sb); // We already dealt with absolute and non-InputSection symbols in // constantEq, and for InputSections we have already checked everything // except the equivalence class. - if (!DA->Section) + if (!da->section) continue; - auto *X = dyn_cast(DA->Section); - if (!X) + auto *x = dyn_cast(da->section); + if (!x) continue; - auto *Y = cast(DB->Section); + auto *y = cast(db->section); // Ineligible sections are in the special equivalence class 0. // They can never be the same in terms of the equivalence class. - if (X->Class[Current] == 0) + if (x->eqClass[current] == 0) return false; - if (X->Class[Current] != Y->Class[Current]) + if (x->eqClass[current] != y->eqClass[current]) return false; }; @@ -358,19 +357,19 @@ bool ICF::variableEq(const InputSection *SecA, ArrayRef RA, // Compare "moving" part of two InputSections, namely relocation targets. template -bool ICF::equalsVariable(const InputSection *A, const InputSection *B) { - if (A->AreRelocsRela) - return variableEq(A, A->template relas(), B, - B->template relas()); - return variableEq(A, A->template rels(), B, B->template rels()); +bool ICF::equalsVariable(const InputSection *a, const InputSection *b) { + if (a->areRelocsRela) + return variableEq(a, a->template relas(), b, + b->template relas()); + return variableEq(a, a->template rels(), b, b->template rels()); } -template size_t ICF::findBoundary(size_t Begin, size_t End) { - uint32_t Class = Sections[Begin]->Class[Current]; - for (size_t I = Begin + 1; I < End; ++I) - if (Class != Sections[I]->Class[Current]) - return I; - return End; +template size_t ICF::findBoundary(size_t begin, size_t end) { + uint32_t eqClass = sections[begin]->eqClass[current]; + for (size_t i = begin + 1; i < end; ++i) + if (eqClass != sections[i]->eqClass[current]) + return i; + return end; } // Sections in the same equivalence class are contiguous in Sections @@ -379,123 +378,125 @@ template size_t ICF::findBoundary(size_t Begin, size_t End) { // // This function calls Fn on every group within [Begin, End). template -void ICF::forEachClassRange(size_t Begin, size_t End, - llvm::function_ref Fn) { - while (Begin < End) { - size_t Mid = findBoundary(Begin, End); - Fn(Begin, Mid); - Begin = Mid; +void ICF::forEachClassRange(size_t begin, size_t end, + llvm::function_ref fn) { + while (begin < end) { + size_t mid = findBoundary(begin, end); + fn(begin, mid); + begin = mid; } } // Call Fn on each equivalence class. template -void ICF::forEachClass(llvm::function_ref Fn) { +void ICF::forEachClass(llvm::function_ref fn) { // If threading is disabled or the number of sections are // too small to use threading, call Fn sequentially. - if (!ThreadsEnabled || Sections.size() < 1024) { - forEachClassRange(0, Sections.size(), Fn); - ++Cnt; + if (!threadsEnabled || sections.size() < 1024) { + forEachClassRange(0, sections.size(), fn); + ++cnt; return; } - Current = Cnt % 2; - Next = (Cnt + 1) % 2; + current = cnt % 2; + next = (cnt + 1) % 2; // Shard into non-overlapping intervals, and call Fn in parallel. // The sharding must be completed before any calls to Fn are made // so that Fn can modify the Chunks in its shard without causing data // races. - const size_t NumShards = 256; - size_t Step = Sections.size() / NumShards; - size_t Boundaries[NumShards + 1]; - Boundaries[0] = 0; - Boundaries[NumShards] = Sections.size(); - - parallelForEachN(1, NumShards, [&](size_t I) { - Boundaries[I] = findBoundary((I - 1) * Step, Sections.size()); + const size_t numShards = 256; + size_t step = sections.size() / numShards; + size_t boundaries[numShards + 1]; + boundaries[0] = 0; + boundaries[numShards] = sections.size(); + + parallelForEachN(1, numShards, [&](size_t i) { + boundaries[i] = findBoundary((i - 1) * step, sections.size()); }); - parallelForEachN(1, NumShards + 1, [&](size_t I) { - if (Boundaries[I - 1] < Boundaries[I]) - forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn); + parallelForEachN(1, numShards + 1, [&](size_t i) { + if (boundaries[i - 1] < boundaries[i]) + forEachClassRange(boundaries[i - 1], boundaries[i], fn); }); - ++Cnt; + ++cnt; } // Combine the hashes of the sections referenced by the given section into its // hash. template -static void combineRelocHashes(InputSection *IS, ArrayRef Rels) { - uint32_t Hash = IS->Class[1]; - for (RelTy Rel : Rels) { - Symbol &S = IS->template getFile()->getRelocTargetSym(Rel); - if (auto *D = dyn_cast(&S)) - if (auto *RelSec = dyn_cast_or_null(D->Section)) - Hash ^= RelSec->Class[1]; +static void combineRelocHashes(unsigned cnt, InputSection *isec, + ArrayRef rels) { + uint32_t hash = isec->eqClass[cnt % 2]; + for (RelTy rel : rels) { + Symbol &s = isec->template getFile()->getRelocTargetSym(rel); + if (auto *d = dyn_cast(&s)) + if (auto *relSec = dyn_cast_or_null(d->section)) + hash += relSec->eqClass[cnt % 2]; } // Set MSB to 1 to avoid collisions with non-hash IDs. - IS->Class[0] = Hash | (1U << 31); + isec->eqClass[(cnt + 1) % 2] = hash | (1U << 31); } -static void print(const Twine &S) { - if (Config->PrintIcfSections) - message(S); +static void print(const Twine &s) { + if (config->printIcfSections) + message(s); } // The main function of ICF. template void ICF::run() { // Collect sections to merge. - for (InputSectionBase *Sec : InputSections) - if (auto *S = dyn_cast(Sec)) - if (isEligible(S)) - Sections.push_back(S); + for (InputSectionBase *sec : inputSections) + if (auto *s = dyn_cast(sec)) + if (isEligible(s)) + sections.push_back(s); // Initially, we use hash values to partition sections. - parallelForEach(Sections, [&](InputSection *S) { - S->Class[1] = xxHash64(S->data()); + parallelForEach(sections, [&](InputSection *s) { + s->eqClass[0] = xxHash64(s->data()); }); - parallelForEach(Sections, [&](InputSection *S) { - if (S->AreRelocsRela) - combineRelocHashes(S, S->template relas()); - else - combineRelocHashes(S, S->template rels()); - }); + for (unsigned cnt = 0; cnt != 2; ++cnt) { + parallelForEach(sections, [&](InputSection *s) { + if (s->areRelocsRela) + combineRelocHashes(cnt, s, s->template relas()); + else + combineRelocHashes(cnt, s, s->template rels()); + }); + } // From now on, sections in Sections vector are ordered so that sections // in the same equivalence class are consecutive in the vector. - std::stable_sort(Sections.begin(), Sections.end(), - [](InputSection *A, InputSection *B) { - return A->Class[0] < B->Class[0]; - }); + llvm::stable_sort(sections, [](const InputSection *a, const InputSection *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 by the equivalence class. - forEachClassRange(0, Sections.size(), [&](size_t Begin, size_t End) { - if (End - Begin == 1) + forEachClassRange(0, sections.size(), [&](size_t begin, size_t end) { + if (end - begin == 1) return; - print("selected section " + toString(Sections[Begin])); - for (size_t I = Begin + 1; I < End; ++I) { - print(" removing identical section " + toString(Sections[I])); - Sections[Begin]->replace(Sections[I]); + print("selected section " + toString(sections[begin])); + for (size_t i = begin + 1; i < end; ++i) { + print(" removing identical section " + toString(sections[i])); + sections[begin]->replace(sections[i]); // At this point we know sections merged are fully identical and hence // we want to remove duplicate implicit dependencies such as link order // and relocation sections. - for (InputSection *IS : Sections[I]->DependentSections) - IS->Live = false; + for (InputSection *isec : sections[i]->dependentSections) + isec->markDead(); } }); } diff --git a/ELF/ICF.h b/ELF/ICF.h index a6c8636..ed828fc 100644 --- a/ELF/ICF.h +++ b/ELF/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 // //===----------------------------------------------------------------------===// diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp index e4d1dec..98b8828 100644 --- a/ELF/InputFiles.cpp +++ b/ELF/InputFiles.cpp @@ -1,13 +1,13 @@ //===- 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 "Driver.h" #include "InputSection.h" #include "LinkerScript.h" #include "SymbolTable.h" @@ -25,6 +25,7 @@ #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/ARMAttributeParser.h" #include "llvm/Support/ARMBuildAttributes.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/raw_ostream.h" @@ -34,145 +35,272 @@ using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::sys; using namespace llvm::sys::fs; +using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; -bool InputFile::IsInGroup; -uint32_t InputFile::NextGroupId; -std::vector elf::BinaryFiles; -std::vector elf::BitcodeFiles; -std::vector elf::LazyObjFiles; -std::vector elf::ObjectFiles; -std::vector elf::SharedFiles; - -std::unique_ptr elf::Tar; +bool InputFile::isInGroup; +uint32_t InputFile::nextGroupId; +std::vector elf::binaryFiles; +std::vector elf::bitcodeFiles; +std::vector elf::lazyObjFiles; +std::vector elf::objectFiles; +std::vector elf::sharedFiles; + +std::unique_ptr elf::tar; + +static ELFKind getELFKind(MemoryBufferRef mb, StringRef archiveName) { + unsigned char size; + unsigned char endian; + std::tie(size, endian) = getElfArchType(mb.getBuffer()); + + auto report = [&](StringRef msg) { + StringRef filename = mb.getBufferIdentifier(); + if (archiveName.empty()) + fatal(filename + ": " + msg); + else + fatal(archiveName + "(" + filename + "): " + msg); + }; + + if (!mb.getBuffer().startswith(ElfMagic)) + report("not an ELF file"); + if (endian != ELFDATA2LSB && endian != ELFDATA2MSB) + report("corrupted ELF file: invalid data encoding"); + if (size != ELFCLASS32 && size != ELFCLASS64) + report("corrupted ELF file: invalid file class"); + + size_t bufSize = mb.getBuffer().size(); + if ((size == ELFCLASS32 && bufSize < sizeof(Elf32_Ehdr)) || + (size == ELFCLASS64 && bufSize < sizeof(Elf64_Ehdr))) + report("corrupted ELF file: file is too short"); + + if (size == ELFCLASS32) + return (endian == ELFDATA2LSB) ? ELF32LEKind : ELF32BEKind; + return (endian == ELFDATA2LSB) ? ELF64LEKind : ELF64BEKind; +} -InputFile::InputFile(Kind K, MemoryBufferRef M) - : MB(M), GroupId(NextGroupId), FileKind(K) { +InputFile::InputFile(Kind k, MemoryBufferRef m) + : mb(m), groupId(nextGroupId), fileKind(k) { // All files within the same --{start,end}-group get the same group ID. // Otherwise, a new file will get a new group ID. - if (!IsInGroup) - ++NextGroupId; + if (!isInGroup) + ++nextGroupId; } -Optional elf::readFile(StringRef Path) { +Optional elf::readFile(StringRef path) { // The --chroot option changes our virtual root directory. // This is useful when you are dealing with files created by --reproduce. - if (!Config->Chroot.empty() && Path.startswith("/")) - Path = Saver.save(Config->Chroot + Path); + if (!config->chroot.empty() && path.startswith("/")) + path = saver.save(config->chroot + path); - log(Path); + log(path); - auto MBOrErr = MemoryBuffer::getFile(Path, -1, false); - if (auto EC = MBOrErr.getError()) { - error("cannot open " + Path + ": " + EC.message()); + auto mbOrErr = MemoryBuffer::getFile(path, -1, false); + if (auto ec = mbOrErr.getError()) { + error("cannot open " + path + ": " + ec.message()); return None; } - std::unique_ptr &MB = *MBOrErr; - MemoryBufferRef MBRef = MB->getMemBufferRef(); - make>(std::move(MB)); // take MB ownership + std::unique_ptr &mb = *mbOrErr; + MemoryBufferRef mbref = mb->getMemBufferRef(); + make>(std::move(mb)); // take MB ownership + + if (tar) + tar->append(relativeToRoot(path), mbref.getBuffer()); + return mbref; +} + +// All input object files must be for the same architecture +// (e.g. it does not make sense to link x86 object files with +// MIPS object files.) This function checks for that error. +static bool isCompatible(InputFile *file) { + if (!file->isElf() && !isa(file)) + return true; + + if (file->ekind == config->ekind && file->emachine == config->emachine) { + if (config->emachine != EM_MIPS) + return true; + if (isMipsN32Abi(file) == config->mipsN32Abi) + return true; + } + + if (!config->emulation.empty()) { + error(toString(file) + " is incompatible with " + config->emulation); + } else { + InputFile *existing; + if (!objectFiles.empty()) + existing = objectFiles[0]; + else if (!sharedFiles.empty()) + existing = sharedFiles[0]; + else + existing = bitcodeFiles[0]; + + error(toString(file) + " is incompatible with " + toString(existing)); + } + + return false; +} + +template static void doParseFile(InputFile *file) { + if (!isCompatible(file)) + return; + + // Binary file + if (auto *f = dyn_cast(file)) { + binaryFiles.push_back(f); + f->parse(); + return; + } + + // .a file + if (auto *f = dyn_cast(file)) { + f->parse(); + return; + } + + // Lazy object file + if (auto *f = dyn_cast(file)) { + lazyObjFiles.push_back(f); + f->parse(); + return; + } + + if (config->trace) + message(toString(file)); + + // .so file + if (auto *f = dyn_cast(file)) { + f->parse(); + return; + } + + // LLVM bitcode file + if (auto *f = dyn_cast(file)) { + bitcodeFiles.push_back(f); + f->parse(); + return; + } - if (Tar) - Tar->append(relativeToRoot(Path), MBRef.getBuffer()); - return MBRef; + // Regular object file + objectFiles.push_back(file); + cast>(file)->parse(); +} + +// Add symbols in File to the symbol table. +void elf::parseFile(InputFile *file) { + switch (config->ekind) { + case ELF32LEKind: + doParseFile(file); + return; + case ELF32BEKind: + doParseFile(file); + return; + case ELF64LEKind: + doParseFile(file); + return; + case ELF64BEKind: + doParseFile(file); + return; + default: + llvm_unreachable("unknown ELFT"); + } } // Concatenates arguments to construct a string representing an error location. -static std::string createFileLineMsg(StringRef Path, unsigned Line) { - std::string Filename = path::filename(Path); - std::string Lineno = ":" + std::to_string(Line); - if (Filename == Path) - return Filename + Lineno; - return Filename + Lineno + " (" + Path.str() + Lineno + ")"; +static std::string createFileLineMsg(StringRef path, unsigned line) { + std::string filename = path::filename(path); + std::string lineno = ":" + std::to_string(line); + if (filename == path) + return filename + lineno; + return filename + lineno + " (" + path.str() + lineno + ")"; } template -static std::string getSrcMsgAux(ObjFile &File, const Symbol &Sym, - InputSectionBase &Sec, uint64_t Offset) { +static std::string getSrcMsgAux(ObjFile &file, const Symbol &sym, + InputSectionBase &sec, uint64_t offset) { // In DWARF, functions and variables are stored to different places. // First, lookup a function for a given offset. - if (Optional Info = File.getDILineInfo(&Sec, Offset)) - return createFileLineMsg(Info->FileName, Info->Line); + if (Optional info = file.getDILineInfo(&sec, offset)) + return createFileLineMsg(info->FileName, info->Line); // If it failed, lookup again as a variable. - if (Optional> FileLine = - File.getVariableLoc(Sym.getName())) - return createFileLineMsg(FileLine->first, FileLine->second); + if (Optional> fileLine = + file.getVariableLoc(sym.getName())) + return createFileLineMsg(fileLine->first, fileLine->second); - // File.SourceFile contains STT_FILE symbol, and that is a last resort. - return File.SourceFile; + // File.sourceFile contains STT_FILE symbol, and that is a last resort. + return file.sourceFile; } -std::string InputFile::getSrcMsg(const Symbol &Sym, InputSectionBase &Sec, - uint64_t Offset) { +std::string InputFile::getSrcMsg(const Symbol &sym, InputSectionBase &sec, + uint64_t offset) { if (kind() != ObjKind) return ""; - switch (Config->EKind) { + switch (config->ekind) { default: llvm_unreachable("Invalid kind"); case ELF32LEKind: - return getSrcMsgAux(cast>(*this), Sym, Sec, Offset); + return getSrcMsgAux(cast>(*this), sym, sec, offset); case ELF32BEKind: - return getSrcMsgAux(cast>(*this), Sym, Sec, Offset); + return getSrcMsgAux(cast>(*this), sym, sec, offset); case ELF64LEKind: - return getSrcMsgAux(cast>(*this), Sym, Sec, Offset); + return getSrcMsgAux(cast>(*this), sym, sec, offset); case ELF64BEKind: - return getSrcMsgAux(cast>(*this), Sym, Sec, Offset); + return getSrcMsgAux(cast>(*this), sym, sec, offset); } } template void ObjFile::initializeDwarf() { - Dwarf = llvm::make_unique(make_unique>(this)); - for (std::unique_ptr &CU : Dwarf->compile_units()) { - auto Report = [](Error Err) { - handleAllErrors(std::move(Err), - [](ErrorInfoBase &Info) { warn(Info.message()); }); + dwarf = llvm::make_unique(make_unique>(this)); + for (std::unique_ptr &cu : dwarf->compile_units()) { + auto report = [](Error err) { + handleAllErrors(std::move(err), + [](ErrorInfoBase &info) { warn(info.message()); }); }; - Expected ExpectedLT = - Dwarf->getLineTableForUnit(CU.get(), Report); - const DWARFDebugLine::LineTable *LT = nullptr; - if (ExpectedLT) - LT = *ExpectedLT; + Expected expectedLT = + dwarf->getLineTableForUnit(cu.get(), report); + const DWARFDebugLine::LineTable *lt = nullptr; + if (expectedLT) + lt = *expectedLT; else - Report(ExpectedLT.takeError()); - if (!LT) + report(expectedLT.takeError()); + if (!lt) continue; - LineTables.push_back(LT); + lineTables.push_back(lt); - // Loop over variable records and insert them to VariableLoc. - for (const auto &Entry : CU->dies()) { - DWARFDie Die(CU.get(), &Entry); + // Loop over variable records and insert them to variableLoc. + for (const auto &entry : cu->dies()) { + DWARFDie die(cu.get(), &entry); // Skip all tags that are not variables. - if (Die.getTag() != dwarf::DW_TAG_variable) + if (die.getTag() != dwarf::DW_TAG_variable) continue; // Skip if a local variable because we don't need them for generating // error messages. In general, only non-local symbols can fail to be // linked. - if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0)) + if (!dwarf::toUnsigned(die.find(dwarf::DW_AT_external), 0)) continue; // Get the source filename index for the variable. - unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0); - if (!LT->hasFileAtIndex(File)) + unsigned file = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_file), 0); + if (!lt->hasFileAtIndex(file)) continue; // Get the line number on which the variable is declared. - unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0); + unsigned line = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_line), 0); - // Here we want to take the variable name to add it into VariableLoc. + // Here we want to take the variable name to add it into variableLoc. // Variable can have regular and linkage name associated. At first, we try // to get linkage name as it can be different, for example when we have // two variables in different namespaces of the same object. Use common // name otherwise, but handle the case when it also absent in case if the // input object file lacks some debug info. - StringRef Name = - dwarf::toString(Die.find(dwarf::DW_AT_linkage_name), - dwarf::toString(Die.find(dwarf::DW_AT_name), "")); - if (!Name.empty()) - VariableLoc.insert({Name, {LT, File, Line}}); + StringRef name = + dwarf::toString(die.find(dwarf::DW_AT_linkage_name), + dwarf::toString(die.find(dwarf::DW_AT_name), "")); + if (!name.empty()) + variableLoc.insert({name, {lt, file, line}}); } } } @@ -181,112 +309,152 @@ template void ObjFile::initializeDwarf() { // object (variable, array, etc) definition. template Optional> -ObjFile::getVariableLoc(StringRef Name) { - llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); }); +ObjFile::getVariableLoc(StringRef name) { + llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); }); // Return if we have no debug information about data object. - auto It = VariableLoc.find(Name); - if (It == VariableLoc.end()) + auto it = variableLoc.find(name); + if (it == variableLoc.end()) return None; // Take file name string from line table. - std::string FileName; - if (!It->second.LT->getFileNameByIndex( - It->second.File, nullptr, - DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName)) + std::string fileName; + if (!it->second.lt->getFileNameByIndex( + it->second.file, {}, + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, fileName)) return None; - return std::make_pair(FileName, It->second.Line); + return std::make_pair(fileName, it->second.line); } // Returns source line information for a given offset // using DWARF debug info. template -Optional ObjFile::getDILineInfo(InputSectionBase *S, - uint64_t Offset) { - llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); }); +Optional ObjFile::getDILineInfo(InputSectionBase *s, + uint64_t offset) { + llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); }); + + // Detect SectionIndex for specified section. + uint64_t sectionIndex = object::SectionedAddress::UndefSection; + ArrayRef sections = s->file->getSections(); + for (uint64_t curIndex = 0; curIndex < sections.size(); ++curIndex) { + if (s == sections[curIndex]) { + sectionIndex = curIndex; + break; + } + } // Use fake address calcuated by adding section file offset and offset in // section. See comments for ObjectInfo class. - DILineInfo Info; - for (const llvm::DWARFDebugLine::LineTable *LT : LineTables) - if (LT->getFileLineInfoForAddress( - S->getOffsetInFile() + Offset, nullptr, - DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info)) - return Info; + DILineInfo info; + for (const llvm::DWARFDebugLine::LineTable *lt : lineTables) { + if (lt->getFileLineInfoForAddress( + {s->getOffsetInFile() + offset, sectionIndex}, nullptr, + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, info)) + return info; + } return None; } // Returns "", "foo.a(bar.o)" or "baz.o". -std::string lld::toString(const InputFile *F) { - if (!F) +std::string lld::toString(const InputFile *f) { + if (!f) return ""; - if (F->ToStringCache.empty()) { - if (F->ArchiveName.empty()) - F->ToStringCache = F->getName(); + if (f->toStringCache.empty()) { + if (f->archiveName.empty()) + f->toStringCache = f->getName(); else - F->ToStringCache = (F->ArchiveName + "(" + F->getName() + ")").str(); + f->toStringCache = (f->archiveName + "(" + f->getName() + ")").str(); } - return F->ToStringCache; + return f->toStringCache; } -template -ELFFileBase::ELFFileBase(Kind K, MemoryBufferRef MB) : InputFile(K, MB) { - if (ELFT::TargetEndianness == support::little) - EKind = ELFT::Is64Bits ? ELF64LEKind : ELF32LEKind; - else - EKind = ELFT::Is64Bits ? ELF64BEKind : ELF32BEKind; +ELFFileBase::ELFFileBase(Kind k, MemoryBufferRef mb) : InputFile(k, mb) { + ekind = getELFKind(mb, ""); - EMachine = getObj().getHeader()->e_machine; - OSABI = getObj().getHeader()->e_ident[llvm::ELF::EI_OSABI]; + switch (ekind) { + case ELF32LEKind: + init(); + break; + case ELF32BEKind: + init(); + break; + case ELF64LEKind: + init(); + break; + case ELF64BEKind: + init(); + break; + default: + llvm_unreachable("getELFKind"); + } } -template -typename ELFT::SymRange ELFFileBase::getGlobalELFSyms() { - return makeArrayRef(ELFSyms.begin() + FirstGlobal, ELFSyms.end()); +template +static const Elf_Shdr *findSection(ArrayRef sections, uint32_t type) { + for (const Elf_Shdr &sec : sections) + if (sec.sh_type == type) + return &sec; + return nullptr; } -template -uint32_t ELFFileBase::getSectionIndex(const Elf_Sym &Sym) const { - return CHECK(getObj().getSectionIndex(&Sym, ELFSyms, SymtabSHNDX), this); -} +template void ELFFileBase::init() { + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Sym = typename ELFT::Sym; -template -void ELFFileBase::initSymtab(ArrayRef Sections, - const Elf_Shdr *Symtab) { - FirstGlobal = Symtab->sh_info; - ELFSyms = CHECK(getObj().symbols(Symtab), this); - if (FirstGlobal == 0 || FirstGlobal > ELFSyms.size()) + // Initialize trivial attributes. + const ELFFile &obj = getObj(); + emachine = obj.getHeader()->e_machine; + osabi = obj.getHeader()->e_ident[llvm::ELF::EI_OSABI]; + abiVersion = obj.getHeader()->e_ident[llvm::ELF::EI_ABIVERSION]; + + ArrayRef sections = CHECK(obj.sections(), this); + + // Find a symbol table. + bool isDSO = + (identify_magic(mb.getBuffer()) == file_magic::elf_shared_object); + const Elf_Shdr *symtabSec = + findSection(sections, isDSO ? SHT_DYNSYM : SHT_SYMTAB); + + if (!symtabSec) + return; + + // Initialize members corresponding to a symbol table. + firstGlobal = symtabSec->sh_info; + + ArrayRef eSyms = CHECK(obj.symbols(symtabSec), this); + if (firstGlobal == 0 || firstGlobal > eSyms.size()) fatal(toString(this) + ": invalid sh_info in symbol table"); - StringTable = - CHECK(getObj().getStringTableForSymtab(*Symtab, Sections), this); + elfSyms = reinterpret_cast(eSyms.data()); + numELFSyms = eSyms.size(); + stringTable = CHECK(obj.getStringTableForSymtab(*symtabSec, sections), this); } template -ObjFile::ObjFile(MemoryBufferRef M, StringRef ArchiveName) - : ELFFileBase(Base::ObjKind, M) { - this->ArchiveName = ArchiveName; +uint32_t ObjFile::getSectionIndex(const Elf_Sym &sym) const { + return CHECK( + this->getObj().getSectionIndex(&sym, getELFSyms(), shndxTable), + this); } template ArrayRef ObjFile::getLocalSymbols() { - if (this->Symbols.empty()) + if (this->symbols.empty()) return {}; - return makeArrayRef(this->Symbols).slice(1, this->FirstGlobal - 1); + return makeArrayRef(this->symbols).slice(1, this->firstGlobal - 1); } template ArrayRef ObjFile::getGlobalSymbols() { - return makeArrayRef(this->Symbols).slice(this->FirstGlobal); + return makeArrayRef(this->symbols).slice(this->firstGlobal); } -template -void ObjFile::parse(DenseSet &ComdatGroups) { - // Read a section table. JustSymbols is usually false. - if (this->JustSymbols) +template void ObjFile::parse(bool ignoreComdats) { + // Read a section table. justSymbols is usually false. + if (this->justSymbols) initializeJustSymbols(); else - initializeSections(ComdatGroups); + initializeSections(ignoreComdats); // Read a symbol table. initializeSymbols(); @@ -296,17 +464,13 @@ void ObjFile::parse(DenseSet &ComdatGroups) { // They are identified and deduplicated by group name. This function // returns a group name. template -StringRef ObjFile::getShtGroupSignature(ArrayRef Sections, - const Elf_Shdr &Sec) { - // Group signatures are stored as symbol names in object files. - // sh_info contains a symbol index, so we fetch a symbol and read its name. - if (this->ELFSyms.empty()) - this->initSymtab( - Sections, CHECK(object::getSection(Sections, Sec.sh_link), this)); - - const Elf_Sym *Sym = - CHECK(object::getSymbol(this->ELFSyms, Sec.sh_info), this); - StringRef Signature = CHECK(Sym->getName(this->StringTable), this); +StringRef ObjFile::getShtGroupSignature(ArrayRef sections, + const Elf_Shdr &sec) { + typename ELFT::SymRange symbols = this->getELFSyms(); + if (sec.sh_info >= symbols.size()) + fatal(toString(this) + ": invalid symbol index"); + const typename ELFT::Sym &sym = symbols[sec.sh_info]; + StringRef signature = CHECK(sym.getName(this->stringTable), this); // As a special case, if a symbol is a section symbol and has no name, // we use a section name as a signature. @@ -315,23 +479,12 @@ StringRef ObjFile::getShtGroupSignature(ArrayRef Sections, // standard, but GNU gold 1.14 (the newest version as of July 2017) or // older produce such sections as outputs for the -r option, so we need // a bug-compatibility. - if (Signature.empty() && Sym->getType() == STT_SECTION) - return getSectionName(Sec); - return Signature; -} - -template -ArrayRef::Elf_Word> -ObjFile::getShtGroupEntries(const Elf_Shdr &Sec) { - const ELFFile &Obj = this->getObj(); - ArrayRef Entries = - CHECK(Obj.template getSectionContentsAsArray(&Sec), this); - if (Entries.empty() || Entries[0] != GRP_COMDAT) - fatal(toString(this) + ": unsupported SHT_GROUP format"); - return Entries.slice(1); + if (signature.empty() && sym.getType() == STT_SECTION) + return getSectionName(sec); + return signature; } -template bool ObjFile::shouldMerge(const Elf_Shdr &Sec) { +template bool ObjFile::shouldMerge(const Elf_Shdr &sec) { // On a regular link we don't merge sections if -O0 (default is -O1). This // sometimes makes the linker significantly faster, although the output will // be bigger. @@ -344,14 +497,14 @@ template bool ObjFile::shouldMerge(const Elf_Shdr &Sec) { // SHF_MERGE sections based both on their name and sh_entsize, but that seems // to be more trouble than it is worth. Instead, we just use the regular (-O1) // logic for -r. - if (Config->Optimize == 0 && !Config->Relocatable) + if (config->optimize == 0 && !config->relocatable) return false; // A mergeable section with size 0 is useless because they don't have // any data to merge. A mergeable string section with size 0 can be // argued as invalid because it doesn't end with a null character. // We'll avoid a mess by handling them as if they were non-mergeable. - if (Sec.sh_size == 0) + if (sec.sh_size == 0) return false; // Check for sh_entsize. The ELF spec is not clear about the zero @@ -359,17 +512,17 @@ template bool ObjFile::shouldMerge(const Elf_Shdr &Sec) { // the section does not hold a table of fixed-size entries". We know // that Rust 1.13 produces a string mergeable section with a zero // sh_entsize. Here we just accept it rather than being picky about it. - uint64_t EntSize = Sec.sh_entsize; - if (EntSize == 0) + uint64_t entSize = sec.sh_entsize; + if (entSize == 0) return false; - if (Sec.sh_size % EntSize) + if (sec.sh_size % entSize) fatal(toString(this) + ": SHF_MERGE section size must be a multiple of sh_entsize"); - uint64_t Flags = Sec.sh_flags; - if (!(Flags & SHF_MERGE)) + uint64_t flags = sec.sh_flags; + if (!(flags & SHF_MERGE)) return false; - if (Flags & SHF_WRITE) + if (flags & SHF_WRITE) fatal(toString(this) + ": writable SHF_MERGE section is not supported"); return true; @@ -385,118 +538,138 @@ template bool ObjFile::shouldMerge(const Elf_Shdr &Sec) { // When the option is given, we link "just symbols". The section table is // initialized with null pointers. template void ObjFile::initializeJustSymbols() { - ArrayRef ObjSections = CHECK(this->getObj().sections(), this); - this->Sections.resize(ObjSections.size()); + ArrayRef sections = CHECK(this->getObj().sections(), this); + this->sections.resize(sections.size()); +} - for (const Elf_Shdr &Sec : ObjSections) { - if (Sec.sh_type != SHT_SYMTAB) - continue; - this->initSymtab(ObjSections, &Sec); +// An ELF object file may contain a `.deplibs` section. If it exists, the +// section contains a list of library specifiers such as `m` for libm. This +// function resolves a given name by finding the first matching library checking +// the various ways that a library can be specified to LLD. This ELF extension +// is a form of autolinking and is called `dependent libraries`. It is currently +// unique to LLVM and lld. +static void addDependentLibrary(StringRef specifier, const InputFile *f) { + if (!config->dependentLibraries) return; - } + if (fs::exists(specifier)) + driver->addFile(specifier, /*withLOption=*/false); + else if (Optional s = findFromSearchPaths(specifier)) + driver->addFile(*s, /*withLOption=*/true); + else if (Optional s = searchLibraryBaseName(specifier)) + driver->addFile(*s, /*withLOption=*/true); + else + error(toString(f) + + ": unable to find library from dependent library specifier: " + + specifier); } template -void ObjFile::initializeSections( - DenseSet &ComdatGroups) { - const ELFFile &Obj = this->getObj(); - - ArrayRef ObjSections = CHECK(Obj.sections(), this); - uint64_t Size = ObjSections.size(); - this->Sections.resize(Size); - this->SectionStringTable = - CHECK(Obj.getSectionStringTable(ObjSections), this); - - for (size_t I = 0, E = ObjSections.size(); I < E; I++) { - if (this->Sections[I] == &InputSection::Discarded) +void ObjFile::initializeSections(bool ignoreComdats) { + const ELFFile &obj = this->getObj(); + + ArrayRef objSections = CHECK(obj.sections(), this); + uint64_t size = objSections.size(); + this->sections.resize(size); + this->sectionStringTable = + CHECK(obj.getSectionStringTable(objSections), this); + + for (size_t i = 0, e = objSections.size(); i < e; i++) { + if (this->sections[i] == &InputSection::discarded) continue; - const Elf_Shdr &Sec = ObjSections[I]; + const Elf_Shdr &sec = objSections[i]; - if (Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE) - CGProfile = check( - this->getObj().template getSectionContentsAsArray( - &Sec)); + if (sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE) + cgProfile = + check(obj.template getSectionContentsAsArray(&sec)); // SHF_EXCLUDE'ed sections are discarded by the linker. However, // if -r is given, we'll let the final link discard such sections. // This is compatible with GNU. - if ((Sec.sh_flags & SHF_EXCLUDE) && !Config->Relocatable) { - if (Sec.sh_type == SHT_LLVM_ADDRSIG) { + if ((sec.sh_flags & SHF_EXCLUDE) && !config->relocatable) { + if (sec.sh_type == SHT_LLVM_ADDRSIG) { // We ignore the address-significance table if we know that the object // file was created by objcopy or ld -r. This is because these tools // will reorder the symbols in the symbol table, invalidating the data // in the address-significance table, which refers to symbols by index. - if (Sec.sh_link != 0) - this->AddrsigSec = &Sec; - else if (Config->ICF == ICFLevel::Safe) + if (sec.sh_link != 0) + this->addrsigSec = &sec; + else if (config->icf == ICFLevel::Safe) warn(toString(this) + ": --icf=safe is incompatible with object " "files created using objcopy or ld -r"); } - this->Sections[I] = &InputSection::Discarded; + this->sections[i] = &InputSection::discarded; continue; } - switch (Sec.sh_type) { + switch (sec.sh_type) { case SHT_GROUP: { // De-duplicate section groups by their signatures. - StringRef Signature = getShtGroupSignature(ObjSections, Sec); - bool IsNew = ComdatGroups.insert(CachedHashStringRef(Signature)).second; - this->Sections[I] = &InputSection::Discarded; - - // We only support GRP_COMDAT type of group. Get the all entries of the - // section here to let getShtGroupEntries to check the type early for us. - ArrayRef Entries = getShtGroupEntries(Sec); - - // If it is a new section group, we want to keep group members. - // Group leader sections, which contain indices of group members, are - // discarded because they are useless beyond this point. The only - // exception is the -r option because in order to produce re-linkable - // object files, we want to pass through basically everything. - if (IsNew) { - if (Config->Relocatable) - this->Sections[I] = createInputSection(Sec); + StringRef signature = getShtGroupSignature(objSections, sec); + this->sections[i] = &InputSection::discarded; + + + ArrayRef entries = + CHECK(obj.template getSectionContentsAsArray(&sec), this); + if (entries.empty()) + fatal(toString(this) + ": empty SHT_GROUP"); + + // The first word of a SHT_GROUP section contains flags. Currently, + // the standard defines only "GRP_COMDAT" flag for the COMDAT group. + // An group with the empty flag doesn't define anything; such sections + // are just skipped. + if (entries[0] == 0) + continue; + + if (entries[0] != GRP_COMDAT) + fatal(toString(this) + ": unsupported SHT_GROUP format"); + + bool isNew = + ignoreComdats || + symtab->comdatGroups.try_emplace(CachedHashStringRef(signature), this) + .second; + if (isNew) { + if (config->relocatable) + this->sections[i] = createInputSection(sec); continue; } // Otherwise, discard group members. - for (uint32_t SecIndex : Entries) { - if (SecIndex >= Size) + for (uint32_t secIndex : entries.slice(1)) { + if (secIndex >= size) fatal(toString(this) + - ": invalid section index in group: " + Twine(SecIndex)); - this->Sections[SecIndex] = &InputSection::Discarded; + ": invalid section index in group: " + Twine(secIndex)); + this->sections[secIndex] = &InputSection::discarded; } break; } - case SHT_SYMTAB: - this->initSymtab(ObjSections, &Sec); - break; case SHT_SYMTAB_SHNDX: - this->SymtabSHNDX = CHECK(Obj.getSHNDXTable(Sec, ObjSections), this); + shndxTable = CHECK(obj.getSHNDXTable(sec, objSections), this); break; + case SHT_SYMTAB: case SHT_STRTAB: case SHT_NULL: break; default: - this->Sections[I] = createInputSection(Sec); + this->sections[i] = createInputSection(sec); } // .ARM.exidx sections have a reverse dependency on the InputSection they // have a SHF_LINK_ORDER dependency, this is identified by the sh_link. - if (Sec.sh_flags & SHF_LINK_ORDER) { - InputSectionBase *LinkSec = nullptr; - if (Sec.sh_link < this->Sections.size()) - LinkSec = this->Sections[Sec.sh_link]; - if (!LinkSec) + if (sec.sh_flags & SHF_LINK_ORDER) { + InputSectionBase *linkSec = nullptr; + if (sec.sh_link < this->sections.size()) + linkSec = this->sections[sec.sh_link]; + if (!linkSec) fatal(toString(this) + - ": invalid sh_link index: " + Twine(Sec.sh_link)); + ": invalid sh_link index: " + Twine(sec.sh_link)); - InputSection *IS = cast(this->Sections[I]); - LinkSec->DependentSections.push_back(IS); - if (!isa(LinkSec)) - error("a section " + IS->Name + + InputSection *isec = cast(this->sections[i]); + linkSec->dependentSections.push_back(isec); + if (!isa(linkSec)) + error("a section " + isec->name + " with SHF_LINK_ORDER should not refer a non-regular " "section: " + - toString(LinkSec)); + toString(linkSec)); } } } @@ -504,9 +677,9 @@ void ObjFile::initializeSections( // For ARM only, to set the EF_ARM_ABI_FLOAT_SOFT or EF_ARM_ABI_FLOAT_HARD // flag in the ELF Header we need to look at Tag_ABI_VFP_args to find out how // the input objects have been compiled. -static void updateARMVFPArgs(const ARMAttributeParser &Attributes, - const InputFile *F) { - if (!Attributes.hasAttribute(ARMBuildAttrs::ABI_VFP_args)) +static void updateARMVFPArgs(const ARMAttributeParser &attributes, + const InputFile *f) { + if (!attributes.hasAttribute(ARMBuildAttrs::ABI_VFP_args)) // If an ABI tag isn't present then it is implicitly given the value of 0 // which maps to ARMBuildAttrs::BaseAAPCS. However many assembler files, // including some in glibc that don't use FP args (and should have value 3) @@ -514,31 +687,31 @@ static void updateARMVFPArgs(const ARMAttributeParser &Attributes, // as a clash. return; - unsigned VFPArgs = Attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args); - ARMVFPArgKind Arg; - switch (VFPArgs) { + unsigned vfpArgs = attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args); + ARMVFPArgKind arg; + switch (vfpArgs) { case ARMBuildAttrs::BaseAAPCS: - Arg = ARMVFPArgKind::Base; + arg = ARMVFPArgKind::Base; break; case ARMBuildAttrs::HardFPAAPCS: - Arg = ARMVFPArgKind::VFP; + arg = ARMVFPArgKind::VFP; break; case ARMBuildAttrs::ToolChainFPPCS: // Tool chain specific convention that conforms to neither AAPCS variant. - Arg = ARMVFPArgKind::ToolChain; + arg = ARMVFPArgKind::ToolChain; break; case ARMBuildAttrs::CompatibleFPAAPCS: // Object compatible with all conventions. return; default: - error(toString(F) + ": unknown Tag_ABI_VFP_args value: " + Twine(VFPArgs)); + error(toString(f) + ": unknown Tag_ABI_VFP_args value: " + Twine(vfpArgs)); return; } // Follow ld.bfd and error if there is a mix of calling conventions. - if (Config->ARMVFPArgs != Arg && Config->ARMVFPArgs != ARMVFPArgKind::Default) - error(toString(F) + ": incompatible Tag_ABI_VFP_args"); + if (config->armVFPArgs != arg && config->armVFPArgs != ARMVFPArgKind::Default) + error(toString(f) + ": incompatible Tag_ABI_VFP_args"); else - Config->ARMVFPArgs = Arg; + config->armVFPArgs = arg; } // The ARM support in lld makes some use of instructions that are not available @@ -550,11 +723,11 @@ static void updateARMVFPArgs(const ARMAttributeParser &Attributes, // at compile time. We follow the convention that if at least one input object // is compiled with an architecture that supports these features then lld is // permitted to use them. -static void updateSupportedARMFeatures(const ARMAttributeParser &Attributes) { - if (!Attributes.hasAttribute(ARMBuildAttrs::CPU_arch)) +static void updateSupportedARMFeatures(const ARMAttributeParser &attributes) { + if (!attributes.hasAttribute(ARMBuildAttrs::CPU_arch)) return; - auto Arch = Attributes.getAttributeValue(ARMBuildAttrs::CPU_arch); - switch (Arch) { + auto arch = attributes.getAttributeValue(ARMBuildAttrs::CPU_arch); + switch (arch) { case ARMBuildAttrs::Pre_v4: case ARMBuildAttrs::v4: case ARMBuildAttrs::v4T: @@ -566,70 +739,156 @@ static void updateSupportedARMFeatures(const ARMAttributeParser &Attributes) { case ARMBuildAttrs::v6: case ARMBuildAttrs::v6KZ: case ARMBuildAttrs::v6K: - Config->ARMHasBlx = true; + config->armHasBlx = true; // Architectures used in pre-Cortex processors do not support // The J1 = 1 J2 = 1 Thumb branch range extension, with the exception // of Architecture v6T2 (arm1156t2-s and arm1156t2f-s) that do. break; default: // All other Architectures have BLX and extended branch encoding - Config->ARMHasBlx = true; - Config->ARMJ1J2BranchEncoding = true; - if (Arch != ARMBuildAttrs::v6_M && Arch != ARMBuildAttrs::v6S_M) + config->armHasBlx = true; + config->armJ1J2BranchEncoding = true; + if (arch != ARMBuildAttrs::v6_M && arch != ARMBuildAttrs::v6S_M) // All Architectures used in Cortex processors with the exception // of v6-M and v6S-M have the MOVT and MOVW instructions. - Config->ARMHasMovtMovw = true; + config->armHasMovtMovw = true; break; } } +// If a source file is compiled with x86 hardware-assisted call flow control +// enabled, the generated object file contains feature flags indicating that +// fact. This function reads the feature flags and returns it. +// +// Essentially we want to read a single 32-bit value in this function, but this +// function is rather complicated because the value is buried deep inside a +// .note.gnu.property section. +// +// The section consists of one or more NOTE records. Each NOTE record consists +// of zero or more type-length-value fields. We want to find a field of a +// certain type. It seems a bit too much to just store a 32-bit value, perhaps +// the ABI is unnecessarily complicated. +template +static uint32_t readAndFeatures(ObjFile *obj, ArrayRef data) { + using Elf_Nhdr = typename ELFT::Nhdr; + using Elf_Note = typename ELFT::Note; + + uint32_t featuresSet = 0; + while (!data.empty()) { + // Read one NOTE record. + if (data.size() < sizeof(Elf_Nhdr)) + fatal(toString(obj) + ": .note.gnu.property: section too short"); + + auto *nhdr = reinterpret_cast(data.data()); + if (data.size() < nhdr->getSize()) + fatal(toString(obj) + ": .note.gnu.property: section too short"); + + Elf_Note note(*nhdr); + if (nhdr->n_type != NT_GNU_PROPERTY_TYPE_0 || note.getName() != "GNU") { + data = data.slice(nhdr->getSize()); + continue; + } + + uint32_t featureAndType = config->emachine == EM_AARCH64 + ? GNU_PROPERTY_AARCH64_FEATURE_1_AND + : GNU_PROPERTY_X86_FEATURE_1_AND; + + // Read a body of a NOTE record, which consists of type-length-value fields. + ArrayRef desc = note.getDesc(); + while (!desc.empty()) { + if (desc.size() < 8) + fatal(toString(obj) + ": .note.gnu.property: section too short"); + + uint32_t type = read32le(desc.data()); + uint32_t size = read32le(desc.data() + 4); + + if (type == featureAndType) { + // We found a FEATURE_1_AND field. There may be more than one of these + // in a .note.gnu.propery section, for a relocatable object we + // accumulate the bits set. + featuresSet |= read32le(desc.data() + 8); + } + + // On 64-bit, a payload may be followed by a 4-byte padding to make its + // size a multiple of 8. + if (ELFT::Is64Bits) + size = alignTo(size, 8); + + desc = desc.slice(size + 8); // +8 for Type and Size + } + + // Go to next NOTE record to look for more FEATURE_1_AND descriptions. + data = data.slice(nhdr->getSize()); + } + + return featuresSet; +} + template -InputSectionBase *ObjFile::getRelocTarget(const Elf_Shdr &Sec) { - uint32_t Idx = Sec.sh_info; - if (Idx >= this->Sections.size()) - fatal(toString(this) + ": invalid relocated section index: " + Twine(Idx)); - InputSectionBase *Target = this->Sections[Idx]; +InputSectionBase *ObjFile::getRelocTarget(const Elf_Shdr &sec) { + uint32_t idx = sec.sh_info; + if (idx >= this->sections.size()) + fatal(toString(this) + ": invalid relocated section index: " + Twine(idx)); + InputSectionBase *target = this->sections[idx]; // Strictly speaking, a relocation section must be included in the // group of the section it relocates. However, LLVM 3.3 and earlier // would fail to do so, so we gracefully handle that case. - if (Target == &InputSection::Discarded) + if (target == &InputSection::discarded) return nullptr; - if (!Target) + if (!target) fatal(toString(this) + ": unsupported relocation reference"); - return Target; + return target; } // Create a regular InputSection class that has the same contents // as a given section. -static InputSection *toRegularSection(MergeInputSection *Sec) { - return make(Sec->File, Sec->Flags, Sec->Type, Sec->Alignment, - Sec->data(), Sec->Name); +static InputSection *toRegularSection(MergeInputSection *sec) { + return make(sec->file, sec->flags, sec->type, sec->alignment, + sec->data(), sec->name); } template -InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &Sec) { - StringRef Name = getSectionName(Sec); +InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &sec) { + StringRef name = getSectionName(sec); - switch (Sec.sh_type) { + switch (sec.sh_type) { case SHT_ARM_ATTRIBUTES: { - if (Config->EMachine != EM_ARM) + if (config->emachine != EM_ARM) break; - ARMAttributeParser Attributes; - ArrayRef Contents = check(this->getObj().getSectionContents(&Sec)); - Attributes.Parse(Contents, /*isLittle*/ Config->EKind == ELF32LEKind); - updateSupportedARMFeatures(Attributes); - updateARMVFPArgs(Attributes, this); + ARMAttributeParser attributes; + ArrayRef contents = check(this->getObj().getSectionContents(&sec)); + attributes.Parse(contents, /*isLittle*/ config->ekind == ELF32LEKind); + updateSupportedARMFeatures(attributes); + updateARMVFPArgs(attributes, this); // FIXME: Retain the first attribute section we see. The eglibc ARM // dynamic loaders require the presence of an attribute section for dlopen // to work. In a full implementation we would merge all attribute sections. - if (In.ARMAttributes == nullptr) { - In.ARMAttributes = make(*this, Sec, Name); - return In.ARMAttributes; + if (in.armAttributes == nullptr) { + in.armAttributes = make(*this, sec, name); + return in.armAttributes; } - return &InputSection::Discarded; + return &InputSection::discarded; + } + case SHT_LLVM_DEPENDENT_LIBRARIES: { + if (config->relocatable) + break; + ArrayRef data = + CHECK(this->getObj().template getSectionContentsAsArray(&sec), this); + if (!data.empty() && data.back() != '\0') { + error(toString(this) + + ": corrupted dependent libraries section (unterminated string): " + + name); + return &InputSection::discarded; + } + for (const char *d = data.begin(), *e = data.end(); d < e;) { + StringRef s(d); + addDependentLibrary(s, this); + d += s.size() + 1; + } + return &InputSection::discarded; } case SHT_RELA: case SHT_REL: { @@ -638,25 +897,25 @@ InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &Sec) { // and the group is discarded, even though it's a violation of the // spec. We handle that situation gracefully by discarding dangling // relocation sections. - InputSectionBase *Target = getRelocTarget(Sec); - if (!Target) + InputSectionBase *target = getRelocTarget(sec); + if (!target) return nullptr; // This section contains relocation information. // If -r is given, we do not interpret or apply relocation // but just copy relocation sections to output. - if (Config->Relocatable) { - InputSection *RelocSec = make(*this, Sec, Name); + if (config->relocatable) { + InputSection *relocSec = make(*this, sec, name); // We want to add a dependency to target, similar like we do for // -emit-relocs below. This is useful for the case when linker script // contains the "/DISCARD/". It is perhaps uncommon to use a script with // -r, but we faced it in the Linux kernel and have to handle such case // and not to crash. - Target->DependentSections.push_back(RelocSec); - return RelocSec; + target->dependentSections.push_back(relocSec); + return relocSec; } - if (Target->FirstRelocation) + if (target->firstRelocation) fatal(toString(this) + ": multiple relocation sections to one section are not supported"); @@ -665,33 +924,33 @@ InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &Sec) { // because applying relocations at end of linking changes section // contents. So, we simply handle such sections as non-mergeable ones. // Degrading like this is acceptable because section merging is optional. - if (auto *MS = dyn_cast(Target)) { - Target = toRegularSection(MS); - this->Sections[Sec.sh_info] = Target; + if (auto *ms = dyn_cast(target)) { + target = toRegularSection(ms); + this->sections[sec.sh_info] = target; } - if (Sec.sh_type == SHT_RELA) { - ArrayRef Rels = CHECK(this->getObj().relas(&Sec), this); - Target->FirstRelocation = Rels.begin(); - Target->NumRelocations = Rels.size(); - Target->AreRelocsRela = true; + if (sec.sh_type == SHT_RELA) { + ArrayRef rels = CHECK(getObj().relas(&sec), this); + target->firstRelocation = rels.begin(); + target->numRelocations = rels.size(); + target->areRelocsRela = true; } else { - ArrayRef Rels = CHECK(this->getObj().rels(&Sec), this); - Target->FirstRelocation = Rels.begin(); - Target->NumRelocations = Rels.size(); - Target->AreRelocsRela = false; + ArrayRef rels = CHECK(getObj().rels(&sec), this); + target->firstRelocation = rels.begin(); + target->numRelocations = rels.size(); + target->areRelocsRela = false; } - assert(isUInt<31>(Target->NumRelocations)); + assert(isUInt<31>(target->numRelocations)); // Relocation sections processed by the linker are usually removed // from the output, so returning `nullptr` for the normal case. // However, if -emit-relocs is given, we need to leave them in the output. // (Some post link analysis tools need this information.) - if (Config->EmitRelocs) { - InputSection *RelocSec = make(*this, Sec, Name); + if (config->emitRelocs) { + InputSection *relocSec = make(*this, sec, name); // We will not emit relocation section if target was discarded. - Target->DependentSections.push_back(RelocSec); - return RelocSec; + target->dependentSections.push_back(relocSec); + return relocSec; } return nullptr; } @@ -710,28 +969,42 @@ InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &Sec) { // explicitly told to do otherwise (by -z execstack). Because the stack // executable-ness is controlled solely by command line options, // .note.GNU-stack sections are simply ignored. - if (Name == ".note.GNU-stack") - return &InputSection::Discarded; + if (name == ".note.GNU-stack") + return &InputSection::discarded; + + // Object files that use processor features such as Intel Control-Flow + // Enforcement (CET) or AArch64 Branch Target Identification BTI, use a + // .note.gnu.property section containing a bitfield of feature bits like the + // GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag. + // + // Since we merge bitmaps from multiple object files to create a new + // .note.gnu.property containing a single AND'ed bitmap, we discard an input + // file's .note.gnu.property section. + if (name == ".note.gnu.property") { + ArrayRef contents = check(this->getObj().getSectionContents(&sec)); + this->andFeatures = readAndFeatures(this, contents); + return &InputSection::discarded; + } // Split stacks is a feature to support a discontiguous stack, // commonly used in the programming language Go. For the details, // see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled // for split stack will include a .note.GNU-split-stack section. - if (Name == ".note.GNU-split-stack") { - if (Config->Relocatable) { + if (name == ".note.GNU-split-stack") { + if (config->relocatable) { error("cannot mix split-stack and non-split-stack in a relocatable link"); - return &InputSection::Discarded; + return &InputSection::discarded; } - this->SplitStack = true; - return &InputSection::Discarded; + this->splitStack = true; + return &InputSection::discarded; } // An object file cmpiled for split stack, but where some of the // functions were compiled with the no_split_stack_attribute will // include a .note.GNU-no-split-stack section. - if (Name == ".note.GNU-no-split-stack") { - this->SomeNoSplitStack = true; - return &InputSection::Discarded; + if (name == ".note.GNU-no-split-stack") { + this->someNoSplitStack = true; + return &InputSection::discarded; } // The linkonce feature is a sort of proto-comdat. Some glibc i386 object @@ -739,245 +1012,205 @@ InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &Sec) { // sections. Drop those sections to avoid duplicate symbol errors. // FIXME: This is glibc PR20543, we should remove this hack once that has been // fixed for a while. - if (Name.startswith(".gnu.linkonce.")) - return &InputSection::Discarded; + if (name == ".gnu.linkonce.t.__x86.get_pc_thunk.bx" || + name == ".gnu.linkonce.t.__i686.get_pc_thunk.bx") + return &InputSection::discarded; // If we are creating a new .build-id section, strip existing .build-id // sections so that the output won't have more than one .build-id. // This is not usually a problem because input object files normally don't // have .build-id sections, but you can create such files by // "ld.{bfd,gold,lld} -r --build-id", and we want to guard against it. - if (Name == ".note.gnu.build-id" && Config->BuildId != BuildIdKind::None) - return &InputSection::Discarded; + if (name == ".note.gnu.build-id" && config->buildId != BuildIdKind::None) + return &InputSection::discarded; // The linker merges EH (exception handling) frames and creates a // .eh_frame_hdr section for runtime. So we handle them with a special // class. For relocatable outputs, they are just passed through. - if (Name == ".eh_frame" && !Config->Relocatable) - return make(*this, Sec, Name); + if (name == ".eh_frame" && !config->relocatable) + return make(*this, sec, name); - if (shouldMerge(Sec)) - return make(*this, Sec, Name); - return make(*this, Sec, Name); + if (shouldMerge(sec)) + return make(*this, sec, name); + return make(*this, sec, name); } template -StringRef ObjFile::getSectionName(const Elf_Shdr &Sec) { - return CHECK(this->getObj().getSectionName(&Sec, SectionStringTable), this); +StringRef ObjFile::getSectionName(const Elf_Shdr &sec) { + return CHECK(getObj().getSectionName(&sec, sectionStringTable), this); } +// Initialize this->Symbols. this->Symbols is a parallel array as +// its corresponding ELF symbol table. template void ObjFile::initializeSymbols() { - this->Symbols.reserve(this->ELFSyms.size()); - for (const Elf_Sym &Sym : this->ELFSyms) - this->Symbols.push_back(createSymbol(&Sym)); -} - -template Symbol *ObjFile::createSymbol(const Elf_Sym *Sym) { - int Binding = Sym->getBinding(); - - uint32_t SecIdx = this->getSectionIndex(*Sym); - if (SecIdx >= this->Sections.size()) - fatal(toString(this) + ": invalid section index: " + Twine(SecIdx)); - - InputSectionBase *Sec = this->Sections[SecIdx]; - uint8_t StOther = Sym->st_other; - uint8_t Type = Sym->getType(); - uint64_t Value = Sym->st_value; - uint64_t Size = Sym->st_size; - - if (Binding == STB_LOCAL) { - if (Sym->getType() == STT_FILE) - SourceFile = CHECK(Sym->getName(this->StringTable), this); + ArrayRef eSyms = this->getELFSyms(); + this->symbols.resize(eSyms.size()); + + // Our symbol table may have already been partially initialized + // because of LazyObjFile. + for (size_t i = 0, end = eSyms.size(); i != end; ++i) + if (!this->symbols[i] && eSyms[i].getBinding() != STB_LOCAL) + this->symbols[i] = + symtab->insert(CHECK(eSyms[i].getName(this->stringTable), this)); + + // Fill this->Symbols. A symbol is either local or global. + for (size_t i = 0, end = eSyms.size(); i != end; ++i) { + const Elf_Sym &eSym = eSyms[i]; + + // Read symbol attributes. + uint32_t secIdx = getSectionIndex(eSym); + if (secIdx >= this->sections.size()) + fatal(toString(this) + ": invalid section index: " + Twine(secIdx)); + + InputSectionBase *sec = this->sections[secIdx]; + uint8_t binding = eSym.getBinding(); + uint8_t stOther = eSym.st_other; + uint8_t type = eSym.getType(); + uint64_t value = eSym.st_value; + uint64_t size = eSym.st_size; + StringRefZ name = this->stringTable.data() + eSym.st_name; + + // Handle local symbols. Local symbols are not added to the symbol + // table because they are not visible from other object files. We + // allocate symbol instances and add their pointers to Symbols. + if (binding == STB_LOCAL) { + if (eSym.getType() == STT_FILE) + sourceFile = CHECK(eSym.getName(this->stringTable), this); + + if (this->stringTable.size() <= eSym.st_name) + fatal(toString(this) + ": invalid symbol name offset"); + + if (eSym.st_shndx == SHN_UNDEF) + this->symbols[i] = make(this, name, binding, stOther, type); + else if (sec == &InputSection::discarded) + this->symbols[i] = make(this, name, binding, stOther, type, + /*DiscardedSecIdx=*/secIdx); + else + this->symbols[i] = + make(this, name, binding, stOther, type, value, size, sec); + continue; + } - if (this->StringTable.size() <= Sym->st_name) - fatal(toString(this) + ": invalid symbol name offset"); + // Handle global undefined symbols. + if (eSym.st_shndx == SHN_UNDEF) { + this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type}); + continue; + } - StringRefZ Name = this->StringTable.data() + Sym->st_name; - if (Sym->st_shndx == SHN_UNDEF) - return make(this, Name, Binding, StOther, Type); + // Handle global common symbols. + if (eSym.st_shndx == SHN_COMMON) { + if (value == 0 || value >= UINT32_MAX) + fatal(toString(this) + ": common symbol '" + StringRef(name.data) + + "' has invalid alignment: " + Twine(value)); + this->symbols[i]->resolve( + CommonSymbol{this, name, binding, stOther, type, value, size}); + continue; + } - return make(this, Name, Binding, StOther, Type, Value, Size, Sec); - } + // If a defined symbol is in a discarded section, handle it as if it + // were an undefined symbol. Such symbol doesn't comply with the + // standard, but in practice, a .eh_frame often directly refer + // COMDAT member sections, and if a comdat group is discarded, some + // defined symbol in a .eh_frame becomes dangling symbols. + if (sec == &InputSection::discarded) { + this->symbols[i]->resolve( + Undefined{this, name, binding, stOther, type, secIdx}); + continue; + } - StringRef Name = CHECK(Sym->getName(this->StringTable), this); - - switch (Sym->st_shndx) { - case SHN_UNDEF: - return Symtab->addUndefined(Name, Binding, StOther, Type, - /*CanOmitFromDynSym=*/false, this); - case SHN_COMMON: - if (Value == 0 || Value >= UINT32_MAX) - fatal(toString(this) + ": common symbol '" + Name + - "' has invalid alignment: " + Twine(Value)); - return Symtab->addCommon(Name, Size, Value, Binding, StOther, Type, *this); - } + // Handle global defined symbols. + if (binding == STB_GLOBAL || binding == STB_WEAK || + binding == STB_GNU_UNIQUE) { + this->symbols[i]->resolve( + Defined{this, name, binding, stOther, type, value, size, sec}); + continue; + } - switch (Binding) { - default: - fatal(toString(this) + ": unexpected binding: " + Twine(Binding)); - case STB_GLOBAL: - case STB_WEAK: - case STB_GNU_UNIQUE: - if (Sec == &InputSection::Discarded) - return Symtab->addUndefined(Name, Binding, StOther, Type, - /*CanOmitFromDynSym=*/false, this); - return Symtab->addDefined(Name, StOther, Type, Value, Size, Binding, Sec, - this); + fatal(toString(this) + ": unexpected binding: " + Twine((int)binding)); } } -ArchiveFile::ArchiveFile(std::unique_ptr &&File) - : InputFile(ArchiveKind, File->getMemoryBufferRef()), - File(std::move(File)) {} +ArchiveFile::ArchiveFile(std::unique_ptr &&file) + : InputFile(ArchiveKind, file->getMemoryBufferRef()), + file(std::move(file)) {} -template void ArchiveFile::parse() { - for (const Archive::Symbol &Sym : File->symbols()) - Symtab->addLazyArchive(Sym.getName(), *this, Sym); +void ArchiveFile::parse() { + for (const Archive::Symbol &sym : file->symbols()) + symtab->addSymbol(LazyArchive{*this, sym}); } // Returns a buffer pointing to a member file containing a given symbol. -InputFile *ArchiveFile::fetch(const Archive::Symbol &Sym) { - Archive::Child C = - CHECK(Sym.getMember(), toString(this) + +void ArchiveFile::fetch(const Archive::Symbol &sym) { + Archive::Child c = + CHECK(sym.getMember(), toString(this) + ": could not get the member for symbol " + - Sym.getName()); + sym.getName()); - if (!Seen.insert(C.getChildOffset()).second) - return nullptr; + if (!seen.insert(c.getChildOffset()).second) + return; - MemoryBufferRef MB = - CHECK(C.getMemoryBufferRef(), + MemoryBufferRef mb = + CHECK(c.getMemoryBufferRef(), toString(this) + ": could not get the buffer for the member defining symbol " + - Sym.getName()); + sym.getName()); - if (Tar && C.getParent()->isThin()) - Tar->append(relativeToRoot(CHECK(C.getFullName(), this)), MB.getBuffer()); + if (tar && c.getParent()->isThin()) + tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb.getBuffer()); - InputFile *File = createObjectFile( - MB, getName(), C.getParent()->isThin() ? 0 : C.getChildOffset()); - File->GroupId = GroupId; - return File; + InputFile *file = createObjectFile( + mb, getName(), c.getParent()->isThin() ? 0 : c.getChildOffset()); + file->groupId = groupId; + parseFile(file); } -template -SharedFile::SharedFile(MemoryBufferRef M, StringRef DefaultSoName) - : ELFFileBase(Base::SharedKind, M), SoName(DefaultSoName), - IsNeeded(!Config->AsNeeded) {} - -// Partially parse the shared object file so that we can call -// getSoName on this object. -template void SharedFile::parseSoName() { - const Elf_Shdr *DynamicSec = nullptr; - const ELFFile Obj = this->getObj(); - ArrayRef Sections = CHECK(Obj.sections(), this); - - // Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d. - for (const Elf_Shdr &Sec : Sections) { - switch (Sec.sh_type) { - default: - continue; - case SHT_DYNSYM: - this->initSymtab(Sections, &Sec); - break; - case SHT_DYNAMIC: - DynamicSec = &Sec; - break; - case SHT_SYMTAB_SHNDX: - this->SymtabSHNDX = CHECK(Obj.getSHNDXTable(Sec, Sections), this); - break; - case SHT_GNU_versym: - this->VersymSec = &Sec; - break; - case SHT_GNU_verdef: - this->VerdefSec = &Sec; - break; - } - } - - if (this->VersymSec && this->ELFSyms.empty()) - error("SHT_GNU_versym should be associated with symbol table"); - - // Search for a DT_SONAME tag to initialize this->SoName. - if (!DynamicSec) - return; - ArrayRef Arr = - CHECK(Obj.template getSectionContentsAsArray(DynamicSec), this); - for (const Elf_Dyn &Dyn : Arr) { - if (Dyn.d_tag == DT_SONAME) { - uint64_t Val = Dyn.getVal(); - if (Val >= this->StringTable.size()) - fatal(toString(this) + ": invalid DT_SONAME entry"); - SoName = this->StringTable.data() + Val; - return; - } - } -} - -// Parses ".gnu.version" section which is a parallel array for the symbol table. -// If a given file doesn't have ".gnu.version" section, returns VER_NDX_GLOBAL. -template std::vector SharedFile::parseVersyms() { - size_t Size = this->ELFSyms.size() - this->FirstGlobal; - if (!VersymSec) - return std::vector(Size, VER_NDX_GLOBAL); - - const char *Base = this->MB.getBuffer().data(); - const Elf_Versym *Versym = - reinterpret_cast(Base + VersymSec->sh_offset) + - this->FirstGlobal; - - std::vector Ret(Size); - for (size_t I = 0; I < Size; ++I) - Ret[I] = Versym[I].vs_index; - return Ret; -} +unsigned SharedFile::vernauxNum; -// Parse the version definitions in the object file if present. Returns a vector -// whose nth element contains a pointer to the Elf_Verdef for version identifier -// n. Version identifiers that are not definitions map to nullptr. -template -std::vector SharedFile::parseVerdefs() { - if (!VerdefSec) +// Parse the version definitions in the object file if present, and return a +// vector whose nth element contains a pointer to the Elf_Verdef for version +// identifier n. Version identifiers that are not definitions map to nullptr. +template +static std::vector parseVerdefs(const uint8_t *base, + const typename ELFT::Shdr *sec) { + if (!sec) return {}; // We cannot determine the largest verdef identifier without inspecting // every Elf_Verdef, but both bfd and gold assign verdef identifiers // sequentially starting from 1, so we predict that the largest identifier - // will be VerdefCount. - unsigned VerdefCount = VerdefSec->sh_info; - std::vector Verdefs(VerdefCount + 1); + // will be verdefCount. + unsigned verdefCount = sec->sh_info; + std::vector verdefs(verdefCount + 1); // Build the Verdefs array by following the chain of Elf_Verdef objects // from the start of the .gnu.version_d section. - const char *Base = this->MB.getBuffer().data(); - const char *Verdef = Base + VerdefSec->sh_offset; - for (unsigned I = 0; I != VerdefCount; ++I) { - auto *CurVerdef = reinterpret_cast(Verdef); - Verdef += CurVerdef->vd_next; - unsigned VerdefIndex = CurVerdef->vd_ndx; - Verdefs.resize(VerdefIndex + 1); - Verdefs[VerdefIndex] = CurVerdef; + const uint8_t *verdef = base + sec->sh_offset; + for (unsigned i = 0; i != verdefCount; ++i) { + auto *curVerdef = reinterpret_cast(verdef); + verdef += curVerdef->vd_next; + unsigned verdefIndex = curVerdef->vd_ndx; + verdefs.resize(verdefIndex + 1); + verdefs[verdefIndex] = curVerdef; } - - return Verdefs; + return verdefs; } // We do not usually care about alignments of data in shared object // files because the loader takes care of it. However, if we promote a // DSO symbol to point to .bss due to copy relocation, we need to keep // the original alignment requirements. We infer it in this function. -template -uint32_t SharedFile::getAlignment(ArrayRef Sections, - const Elf_Sym &Sym) { - uint64_t Ret = UINT64_MAX; - if (Sym.st_value) - Ret = 1ULL << countTrailingZeros((uint64_t)Sym.st_value); - if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size()) - Ret = std::min(Ret, Sections[Sym.st_shndx].sh_addralign); - return (Ret > UINT32_MAX) ? 0 : Ret; +template +static uint64_t getAlignment(ArrayRef sections, + const typename ELFT::Sym &sym) { + uint64_t ret = UINT64_MAX; + if (sym.st_value) + ret = 1ULL << countTrailingZeros((uint64_t)sym.st_value); + if (0 < sym.st_shndx && sym.st_shndx < sections.size()) + ret = std::min(ret, sections[sym.st_shndx].sh_addralign); + return (ret > UINT32_MAX) ? 0 : ret; } -// Fully parse the shared object file. This must be called after parseSoName(). +// Fully parse the shared object file. // // This function parses symbol versions. If a DSO has version information, // the file has a ".gnu.version_d" section which contains symbol version @@ -992,80 +1225,163 @@ uint32_t SharedFile::getAlignment(ArrayRef Sections, // The file format for symbol versioning is perhaps a bit more complicated // than necessary, but you can easily understand the code if you wrap your // head around the data structure described above. -template void SharedFile::parseRest() { - Verdefs = parseVerdefs(); // parse .gnu.version_d - std::vector Versyms = parseVersyms(); // parse .gnu.version - ArrayRef Sections = CHECK(this->getObj().sections(), this); +template void SharedFile::parse() { + using Elf_Dyn = typename ELFT::Dyn; + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Sym = typename ELFT::Sym; + using Elf_Verdef = typename ELFT::Verdef; + using Elf_Versym = typename ELFT::Versym; + + ArrayRef dynamicTags; + const ELFFile obj = this->getObj(); + ArrayRef sections = CHECK(obj.sections(), this); + + const Elf_Shdr *versymSec = nullptr; + const Elf_Shdr *verdefSec = nullptr; + + // Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d. + for (const Elf_Shdr &sec : sections) { + switch (sec.sh_type) { + default: + continue; + case SHT_DYNAMIC: + dynamicTags = + CHECK(obj.template getSectionContentsAsArray(&sec), this); + break; + case SHT_GNU_versym: + versymSec = &sec; + break; + case SHT_GNU_verdef: + verdefSec = &sec; + break; + } + } + + if (versymSec && numELFSyms == 0) { + error("SHT_GNU_versym should be associated with symbol table"); + return; + } + + // Search for a DT_SONAME tag to initialize this->soName. + for (const Elf_Dyn &dyn : dynamicTags) { + if (dyn.d_tag == DT_NEEDED) { + uint64_t val = dyn.getVal(); + if (val >= this->stringTable.size()) + fatal(toString(this) + ": invalid DT_NEEDED entry"); + dtNeeded.push_back(this->stringTable.data() + val); + } else if (dyn.d_tag == DT_SONAME) { + uint64_t val = dyn.getVal(); + if (val >= this->stringTable.size()) + fatal(toString(this) + ": invalid DT_SONAME entry"); + soName = this->stringTable.data() + val; + } + } + + // DSOs are uniquified not by filename but by soname. + DenseMap::iterator it; + bool wasInserted; + std::tie(it, wasInserted) = symtab->soNames.try_emplace(soName, this); + + // If a DSO appears more than once on the command line with and without + // --as-needed, --no-as-needed takes precedence over --as-needed because a + // user can add an extra DSO with --no-as-needed to force it to be added to + // the dependency list. + it->second->isNeeded |= isNeeded; + if (!wasInserted) + return; + + sharedFiles.push_back(this); + + verdefs = parseVerdefs(obj.base(), verdefSec); + + // Parse ".gnu.version" section which is a parallel array for the symbol + // table. If a given file doesn't have a ".gnu.version" section, we use + // VER_NDX_GLOBAL. + size_t size = numELFSyms - firstGlobal; + std::vector versyms(size, VER_NDX_GLOBAL); + if (versymSec) { + ArrayRef versym = + CHECK(obj.template getSectionContentsAsArray(versymSec), + this) + .slice(firstGlobal); + for (size_t i = 0; i < size; ++i) + versyms[i] = versym[i].vs_index; + } // System libraries can have a lot of symbols with versions. Using a // fixed buffer for computing the versions name (foo@ver) can save a // lot of allocations. - SmallString<0> VersionedNameBuffer; + SmallString<0> versionedNameBuffer; // Add symbols to the symbol table. - ArrayRef Syms = this->getGlobalELFSyms(); - for (size_t I = 0; I < Syms.size(); ++I) { - const Elf_Sym &Sym = Syms[I]; + ArrayRef syms = this->getGlobalELFSyms(); + for (size_t i = 0; i < syms.size(); ++i) { + const Elf_Sym &sym = syms[i]; // ELF spec requires that all local symbols precede weak or global // symbols in each symbol table, and the index of first non-local symbol // is stored to sh_info. If a local symbol appears after some non-local // symbol, that's a violation of the spec. - StringRef Name = CHECK(Sym.getName(this->StringTable), this); - if (Sym.getBinding() == STB_LOCAL) { - warn("found local symbol '" + Name + + StringRef name = CHECK(sym.getName(this->stringTable), this); + if (sym.getBinding() == STB_LOCAL) { + warn("found local symbol '" + name + "' in global part of symbol table in file " + toString(this)); continue; } - if (Sym.isUndefined()) { - Symbol *S = Symtab->addUndefined(Name, Sym.getBinding(), - Sym.st_other, Sym.getType(), - /*CanOmitFromDynSym=*/false, this); - S->ExportDynamic = true; + if (sym.isUndefined()) { + Symbol *s = symtab->addSymbol( + Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()}); + s->exportDynamic = true; continue; } // MIPS BFD linker puts _gp_disp symbol into DSO files and incorrectly // assigns VER_NDX_LOCAL to this section global symbol. Here is a // workaround for this bug. - uint32_t Idx = Versyms[I] & ~VERSYM_HIDDEN; - if (Config->EMachine == EM_MIPS && Idx == VER_NDX_LOCAL && - Name == "_gp_disp") + uint32_t idx = versyms[i] & ~VERSYM_HIDDEN; + if (config->emachine == EM_MIPS && idx == VER_NDX_LOCAL && + name == "_gp_disp") continue; - uint64_t Alignment = getAlignment(Sections, Sym); - if (!(Versyms[I] & VERSYM_HIDDEN)) - Symtab->addShared(Name, *this, Sym, Alignment, Idx); + uint32_t alignment = getAlignment(sections, sym); + if (!(versyms[i] & VERSYM_HIDDEN)) { + symtab->addSymbol(SharedSymbol{*this, name, sym.getBinding(), + sym.st_other, sym.getType(), sym.st_value, + sym.st_size, alignment, idx}); + } // Also add the symbol with the versioned name to handle undefined symbols // with explicit versions. - if (Idx == VER_NDX_GLOBAL) + if (idx == VER_NDX_GLOBAL) continue; - if (Idx >= Verdefs.size() || Idx == VER_NDX_LOCAL) { - error("corrupt input file: version definition index " + Twine(Idx) + - " for symbol " + Name + " is out of bounds\n>>> defined in " + + if (idx >= verdefs.size() || idx == VER_NDX_LOCAL) { + error("corrupt input file: version definition index " + Twine(idx) + + " for symbol " + name + " is out of bounds\n>>> defined in " + toString(this)); continue; } - StringRef VerName = - this->StringTable.data() + Verdefs[Idx]->getAux()->vda_name; - VersionedNameBuffer.clear(); - Name = (Name + "@" + VerName).toStringRef(VersionedNameBuffer); - Symtab->addShared(Saver.save(Name), *this, Sym, Alignment, Idx); + StringRef verName = + this->stringTable.data() + + reinterpret_cast(verdefs[idx])->getAux()->vda_name; + versionedNameBuffer.clear(); + name = (name + "@" + verName).toStringRef(versionedNameBuffer); + symtab->addSymbol(SharedSymbol{*this, saver.save(name), sym.getBinding(), + sym.st_other, sym.getType(), sym.st_value, + sym.st_size, alignment, idx}); } } -static ELFKind getBitcodeELFKind(const Triple &T) { - if (T.isLittleEndian()) - return T.isArch64Bit() ? ELF64LEKind : ELF32LEKind; - return T.isArch64Bit() ? ELF64BEKind : ELF32BEKind; +static ELFKind getBitcodeELFKind(const Triple &t) { + if (t.isLittleEndian()) + return t.isArch64Bit() ? ELF64LEKind : ELF32LEKind; + return t.isArch64Bit() ? ELF64BEKind : ELF32BEKind; } -static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) { - switch (T.getArch()) { +static uint8_t getBitcodeMachineKind(StringRef path, const Triple &t) { + switch (t.getArch()) { case Triple::aarch64: return EM_AARCH64; case Triple::amdgcn: @@ -1088,25 +1404,28 @@ static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) { case Triple::ppc64: case Triple::ppc64le: return EM_PPC64; + case Triple::riscv32: + case Triple::riscv64: + return EM_RISCV; case Triple::x86: - return T.isOSIAMCU() ? EM_IAMCU : EM_386; + return t.isOSIAMCU() ? EM_IAMCU : EM_386; case Triple::x86_64: return EM_X86_64; default: - error(Path + ": could not infer e_machine from bitcode target triple " + - T.str()); + error(path + ": could not infer e_machine from bitcode target triple " + + t.str()); return EM_NONE; } } -BitcodeFile::BitcodeFile(MemoryBufferRef MB, StringRef ArchiveName, - uint64_t OffsetInArchive) - : InputFile(BitcodeKind, MB) { - this->ArchiveName = ArchiveName; +BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive) + : InputFile(BitcodeKind, mb) { + this->archiveName = archiveName; - std::string Path = MB.getBufferIdentifier().str(); - if (Config->ThinLTOIndexOnly) - Path = replaceThinLTOSuffix(MB.getBufferIdentifier()); + 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 @@ -1114,20 +1433,21 @@ BitcodeFile::BitcodeFile(MemoryBufferRef MB, StringRef ArchiveName, // 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)))); + StringRef name = archiveName.empty() + ? saver.save(path) + : saver.save(archiveName + "(" + path + " at " + + utostr(offsetInArchive) + ")"); + MemoryBufferRef mbref(mb.getBuffer(), name); - Obj = CHECK(lto::InputFile::create(MBRef), this); + obj = CHECK(lto::InputFile::create(mbref), this); - Triple T(Obj->getTargetTriple()); - EKind = getBitcodeELFKind(T); - EMachine = getBitcodeMachineKind(MB.getBufferIdentifier(), T); + Triple t(obj->getTargetTriple()); + ekind = getBitcodeELFKind(t); + emachine = getBitcodeMachineKind(mb.getBufferIdentifier(), t); } -static uint8_t mapVisibility(GlobalValue::VisibilityTypes GvVisibility) { - switch (GvVisibility) { +static uint8_t mapVisibility(GlobalValue::VisibilityTypes gvVisibility) { + switch (gvVisibility) { case GlobalValue::DefaultVisibility: return STV_DEFAULT; case GlobalValue::HiddenVisibility: @@ -1139,209 +1459,187 @@ static uint8_t mapVisibility(GlobalValue::VisibilityTypes GvVisibility) { } template -static Symbol *createBitcodeSymbol(const std::vector &KeptComdats, - const lto::InputFile::Symbol &ObjSym, - BitcodeFile &F) { - StringRef Name = Saver.save(ObjSym.getName()); - uint32_t Binding = ObjSym.isWeak() ? STB_WEAK : STB_GLOBAL; - - uint8_t Type = ObjSym.isTLS() ? STT_TLS : STT_NOTYPE; - uint8_t Visibility = mapVisibility(ObjSym.getVisibility()); - bool CanOmitFromDynSym = ObjSym.canBeOmittedFromSymbolTable(); - - int C = ObjSym.getComdatIndex(); - if (C != -1 && !KeptComdats[C]) - return Symtab->addUndefined(Name, Binding, Visibility, Type, - CanOmitFromDynSym, &F); - - if (ObjSym.isUndefined()) - return Symtab->addUndefined(Name, Binding, Visibility, Type, - CanOmitFromDynSym, &F); - - if (ObjSym.isCommon()) - return Symtab->addCommon(Name, ObjSym.getCommonSize(), - ObjSym.getCommonAlignment(), Binding, Visibility, - STT_OBJECT, F); - - return Symtab->addBitcode(Name, Binding, Visibility, Type, CanOmitFromDynSym, - F); -} +static Symbol *createBitcodeSymbol(const std::vector &keptComdats, + const lto::InputFile::Symbol &objSym, + BitcodeFile &f) { + StringRef name = saver.save(objSym.getName()); + uint8_t binding = objSym.isWeak() ? STB_WEAK : STB_GLOBAL; + uint8_t type = objSym.isTLS() ? STT_TLS : STT_NOTYPE; + uint8_t visibility = mapVisibility(objSym.getVisibility()); + bool canOmitFromDynSym = objSym.canBeOmittedFromSymbolTable(); + + int c = objSym.getComdatIndex(); + if (objSym.isUndefined() || (c != -1 && !keptComdats[c])) { + Undefined New(&f, name, binding, visibility, type); + if (canOmitFromDynSym) + New.exportDynamic = false; + return symtab->addSymbol(New); + } -template -void BitcodeFile::parse(DenseSet &ComdatGroups) { - std::vector KeptComdats; - for (StringRef S : Obj->getComdatTable()) - KeptComdats.push_back(ComdatGroups.insert(CachedHashStringRef(S)).second); + if (objSym.isCommon()) + return symtab->addSymbol( + CommonSymbol{&f, name, binding, visibility, STT_OBJECT, + objSym.getCommonAlignment(), objSym.getCommonSize()}); - for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) - Symbols.push_back(createBitcodeSymbol(KeptComdats, ObjSym, *this)); + Defined New(&f, name, binding, visibility, type, 0, 0, nullptr); + if (canOmitFromDynSym) + New.exportDynamic = false; + return symtab->addSymbol(New); } -static ELFKind getELFKind(MemoryBufferRef MB) { - unsigned char Size; - unsigned char Endian; - std::tie(Size, Endian) = getElfArchType(MB.getBuffer()); - - if (Endian != ELFDATA2LSB && Endian != ELFDATA2MSB) - fatal(MB.getBufferIdentifier() + ": invalid data encoding"); - if (Size != ELFCLASS32 && Size != ELFCLASS64) - fatal(MB.getBufferIdentifier() + ": invalid file class"); +template void BitcodeFile::parse() { + std::vector keptComdats; + for (StringRef s : obj->getComdatTable()) + keptComdats.push_back( + symtab->comdatGroups.try_emplace(CachedHashStringRef(s), this).second); - size_t BufSize = MB.getBuffer().size(); - if ((Size == ELFCLASS32 && BufSize < sizeof(Elf32_Ehdr)) || - (Size == ELFCLASS64 && BufSize < sizeof(Elf64_Ehdr))) - fatal(MB.getBufferIdentifier() + ": file is too short"); + for (const lto::InputFile::Symbol &objSym : obj->symbols()) + symbols.push_back(createBitcodeSymbol(keptComdats, objSym, *this)); - if (Size == ELFCLASS32) - return (Endian == ELFDATA2LSB) ? ELF32LEKind : ELF32BEKind; - return (Endian == ELFDATA2LSB) ? ELF64LEKind : ELF64BEKind; + for (auto l : obj->getDependentLibraries()) + addDependentLibrary(l, this); } void BinaryFile::parse() { - ArrayRef Data = arrayRefFromStringRef(MB.getBuffer()); - auto *Section = make(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, - 8, Data, ".data"); - Sections.push_back(Section); + ArrayRef data = arrayRefFromStringRef(mb.getBuffer()); + auto *section = make(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, + 8, data, ".data"); + sections.push_back(section); // For each input file foo that is embedded to a result as a binary // blob, we define _binary_foo_{start,end,size} symbols, so that // user programs can access blobs by name. Non-alphanumeric // characters in a filename are replaced with underscore. - std::string S = "_binary_" + MB.getBufferIdentifier().str(); - for (size_t I = 0; I < S.size(); ++I) - if (!isAlnum(S[I])) - S[I] = '_'; - - Symtab->addDefined(Saver.save(S + "_start"), STV_DEFAULT, STT_OBJECT, 0, 0, - STB_GLOBAL, Section, nullptr); - Symtab->addDefined(Saver.save(S + "_end"), STV_DEFAULT, STT_OBJECT, - Data.size(), 0, STB_GLOBAL, Section, nullptr); - Symtab->addDefined(Saver.save(S + "_size"), STV_DEFAULT, STT_OBJECT, - Data.size(), 0, STB_GLOBAL, nullptr, nullptr); + std::string s = "_binary_" + mb.getBufferIdentifier().str(); + for (size_t i = 0; i < s.size(); ++i) + if (!isAlnum(s[i])) + s[i] = '_'; + + symtab->addSymbol(Defined{nullptr, saver.save(s + "_start"), STB_GLOBAL, + STV_DEFAULT, STT_OBJECT, 0, 0, section}); + symtab->addSymbol(Defined{nullptr, saver.save(s + "_end"), STB_GLOBAL, + STV_DEFAULT, STT_OBJECT, data.size(), 0, section}); + symtab->addSymbol(Defined{nullptr, saver.save(s + "_size"), STB_GLOBAL, + STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr}); } -InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName, - uint64_t OffsetInArchive) { - if (isBitcode(MB)) - return make(MB, ArchiveName, OffsetInArchive); +InputFile *elf::createObjectFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive) { + if (isBitcode(mb)) + return make(mb, archiveName, offsetInArchive); - switch (getELFKind(MB)) { + switch (getELFKind(mb, archiveName)) { case ELF32LEKind: - return make>(MB, ArchiveName); + return make>(mb, archiveName); case ELF32BEKind: - return make>(MB, ArchiveName); + return make>(mb, archiveName); case ELF64LEKind: - return make>(MB, ArchiveName); + return make>(mb, archiveName); case ELF64BEKind: - return make>(MB, ArchiveName); + return make>(mb, archiveName); default: llvm_unreachable("getELFKind"); } } -InputFile *elf::createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName) { - switch (getELFKind(MB)) { - case ELF32LEKind: - return make>(MB, DefaultSoName); - case ELF32BEKind: - return make>(MB, DefaultSoName); - case ELF64LEKind: - return make>(MB, DefaultSoName); - case ELF64BEKind: - return make>(MB, DefaultSoName); - default: - llvm_unreachable("getELFKind"); - } -} +void LazyObjFile::fetch() { + if (mb.getBuffer().empty()) + return; -MemoryBufferRef LazyObjFile::getBuffer() { - if (AddedToLink) - return MemoryBufferRef(); - AddedToLink = true; - return MB; -} + InputFile *file = createObjectFile(mb, archiveName, offsetInArchive); + file->groupId = groupId; -InputFile *LazyObjFile::fetch() { - MemoryBufferRef MBRef = getBuffer(); - if (MBRef.getBuffer().empty()) - return nullptr; + mb = {}; + + // Copy symbol vector so that the new InputFile doesn't have to + // insert the same defined symbols to the symbol table again. + file->symbols = std::move(symbols); - InputFile *File = createObjectFile(MBRef, ArchiveName, OffsetInArchive); - File->GroupId = GroupId; - return File; + parseFile(file); } template void LazyObjFile::parse() { + using Elf_Sym = typename ELFT::Sym; + // A lazy object file wraps either a bitcode file or an ELF file. - if (isBitcode(this->MB)) { - std::unique_ptr Obj = - CHECK(lto::InputFile::create(this->MB), this); - for (const lto::InputFile::Symbol &Sym : Obj->symbols()) - if (!Sym.isUndefined()) - Symtab->addLazyObject(Saver.save(Sym.getName()), *this); + if (isBitcode(this->mb)) { + std::unique_ptr obj = + CHECK(lto::InputFile::create(this->mb), this); + for (const lto::InputFile::Symbol &sym : obj->symbols()) { + if (sym.isUndefined()) + continue; + symtab->addSymbol(LazyObject{*this, saver.save(sym.getName())}); + } return; } - if (getELFKind(this->MB) != Config->EKind) { - error("incompatible file: " + this->MB.getBufferIdentifier()); + if (getELFKind(this->mb, archiveName) != config->ekind) { + error("incompatible file: " + this->mb.getBufferIdentifier()); return; } - ELFFile Obj = check(ELFFile::create(MB.getBuffer())); - ArrayRef Sections = CHECK(Obj.sections(), this); + // Find a symbol table. + ELFFile obj = check(ELFFile::create(mb.getBuffer())); + ArrayRef sections = CHECK(obj.sections(), this); - for (const typename ELFT::Shdr &Sec : Sections) { - if (Sec.sh_type != SHT_SYMTAB) + for (const typename ELFT::Shdr &sec : sections) { + if (sec.sh_type != SHT_SYMTAB) continue; - typename ELFT::SymRange Syms = CHECK(Obj.symbols(&Sec), this); - uint32_t FirstGlobal = Sec.sh_info; - StringRef StringTable = - CHECK(Obj.getStringTableForSymtab(Sec, Sections), this); + // A symbol table is found. + ArrayRef eSyms = CHECK(obj.symbols(&sec), this); + uint32_t firstGlobal = sec.sh_info; + StringRef strtab = CHECK(obj.getStringTableForSymtab(sec, sections), this); + this->symbols.resize(eSyms.size()); + + // Get existing symbols or insert placeholder symbols. + for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) + if (eSyms[i].st_shndx != SHN_UNDEF) + this->symbols[i] = symtab->insert(CHECK(eSyms[i].getName(strtab), this)); + + // Replace existing symbols with LazyObject symbols. + // + // resolve() may trigger this->fetch() if an existing symbol is an + // undefined symbol. If that happens, this LazyObjFile has served + // its purpose, and we can exit from the loop early. + for (Symbol *sym : this->symbols) { + if (!sym) + continue; + sym->resolve(LazyObject{*this, sym->getName()}); - for (const typename ELFT::Sym &Sym : Syms.slice(FirstGlobal)) - if (Sym.st_shndx != SHN_UNDEF) - Symtab->addLazyObject(CHECK(Sym.getName(StringTable), this), - *this); + // MemoryBuffer is emptied if this file is instantiated as ObjFile. + if (mb.getBuffer().empty()) + return; + } return; } } -std::string elf::replaceThinLTOSuffix(StringRef Path) { - StringRef Suffix = Config->ThinLTOObjectSuffixReplace.first; - StringRef Repl = Config->ThinLTOObjectSuffixReplace.second; +std::string elf::replaceThinLTOSuffix(StringRef path) { + StringRef suffix = config->thinLTOObjectSuffixReplace.first; + StringRef repl = config->thinLTOObjectSuffixReplace.second; - if (Path.consume_back(Suffix)) - return (Path + Repl).str(); - return Path; + if (path.consume_back(suffix)) + return (path + repl).str(); + return path; } -template void ArchiveFile::parse(); -template void ArchiveFile::parse(); -template void ArchiveFile::parse(); -template void ArchiveFile::parse(); - -template void BitcodeFile::parse(DenseSet &); -template void BitcodeFile::parse(DenseSet &); -template void BitcodeFile::parse(DenseSet &); -template void BitcodeFile::parse(DenseSet &); +template void BitcodeFile::parse(); +template void BitcodeFile::parse(); +template void BitcodeFile::parse(); +template void BitcodeFile::parse(); template void LazyObjFile::parse(); template void LazyObjFile::parse(); template void LazyObjFile::parse(); template void LazyObjFile::parse(); -template class elf::ELFFileBase; -template class elf::ELFFileBase; -template class elf::ELFFileBase; -template class elf::ELFFileBase; - template class elf::ObjFile; template class elf::ObjFile; template class elf::ObjFile; template class elf::ObjFile; -template class elf::SharedFile; -template class elf::SharedFile; -template class elf::SharedFile; -template class elf::SharedFile; +template void SharedFile::parse(); +template void SharedFile::parse(); +template void SharedFile::parse(); +template void SharedFile::parse(); diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h index 5094ddd..5ccc3d4 100644 --- a/ELF/InputFiles.h +++ b/ELF/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 // //===----------------------------------------------------------------------===// @@ -40,7 +39,7 @@ class InputSectionBase; } // Returns "", "foo.a(bar.o)" or "baz.o". -std::string toString(const elf::InputFile *F); +std::string toString(const elf::InputFile *f); namespace elf { @@ -50,10 +49,13 @@ class Symbol; // If -reproduce option is given, all input files are written // to this tar archive. -extern std::unique_ptr Tar; +extern std::unique_ptr tar; // Opens a given file. -llvm::Optional readFile(StringRef Path); +llvm::Optional readFile(StringRef path); + +// Add symbols in File to the symbol table. +void parseFile(InputFile *file); // The root class of input files. class InputFile { @@ -67,192 +69,230 @@ public: BinaryKind, }; - Kind kind() const { return FileKind; } + Kind kind() const { return fileKind; } bool isElf() const { - Kind K = kind(); - return K == ObjKind || K == SharedKind; + Kind k = kind(); + return k == ObjKind || k == SharedKind; } - StringRef getName() const { return MB.getBufferIdentifier(); } - MemoryBufferRef MB; + StringRef getName() const { return mb.getBufferIdentifier(); } + MemoryBufferRef mb; // Returns sections. It is a runtime error to call this function // on files that don't have the notion of sections. ArrayRef getSections() const { - assert(FileKind == ObjKind || FileKind == BinaryKind); - return Sections; + assert(fileKind == ObjKind || fileKind == BinaryKind); + return sections; } // Returns object file symbols. It is a runtime error to call this // function on files of other types. ArrayRef getSymbols() { return getMutableSymbols(); } - std::vector &getMutableSymbols() { - assert(FileKind == BinaryKind || FileKind == ObjKind || - FileKind == BitcodeKind); - return Symbols; + MutableArrayRef getMutableSymbols() { + assert(fileKind == BinaryKind || fileKind == ObjKind || + fileKind == BitcodeKind); + return symbols; } // Filename of .a which contained this file. If this file was // not in an archive file, it is the empty string. We use this // string for creating error messages. - std::string ArchiveName; + std::string archiveName; // If this is an architecture-specific file, the following members // have ELF type (i.e. ELF{32,64}{LE,BE}) and target machine type. - ELFKind EKind = ELFNoneKind; - uint16_t EMachine = llvm::ELF::EM_NONE; - uint8_t OSABI = 0; + ELFKind ekind = ELFNoneKind; + uint16_t emachine = llvm::ELF::EM_NONE; + uint8_t osabi = 0; + uint8_t abiVersion = 0; // Cache for toString(). Only toString() should use this member. - mutable std::string ToStringCache; + mutable std::string toStringCache; - std::string getSrcMsg(const Symbol &Sym, InputSectionBase &Sec, - uint64_t Offset); + std::string getSrcMsg(const Symbol &sym, InputSectionBase &sec, + uint64_t offset); // True if this is an argument for --just-symbols. Usually false. - bool JustSymbols = false; - - // GroupId is used for --warn-backrefs which is an optional error + bool justSymbols = false; + + // outSecOff of .got2 in the current file. This is used by PPC32 -fPIC/-fPIE + // to compute offsets in PLT call stubs. + uint32_t ppc32Got2OutSecOff = 0; + + // On PPC64 we need to keep track of which files contain small code model + // relocations that access the .toc section. To minimize the chance of a + // relocation overflow, files that do contain said relocations should have + // their .toc sections sorted closer to the .got section than files that do + // not contain any small code model relocations. Thats because the toc-pointer + // is defined to point at .got + 0x8000 and the instructions used with small + // code model relocations support immediates in the range [-0x8000, 0x7FFC], + // making the addressable range relative to the toc pointer + // [.got, .got + 0xFFFC]. + bool ppc64SmallCodeModelTocRelocs = false; + + // groupId is used for --warn-backrefs which is an optional error // checking feature. All files within the same --{start,end}-group or // --{start,end}-lib get the same group ID. Otherwise, each file gets a new // group ID. For more info, see checkDependency() in SymbolTable.cpp. - uint32_t GroupId; - static bool IsInGroup; - static uint32_t NextGroupId; + uint32_t groupId; + static bool isInGroup; + static uint32_t nextGroupId; // Index of MIPS GOT built for this file. - llvm::Optional MipsGotIndex; + llvm::Optional mipsGotIndex; + + std::vector symbols; protected: - InputFile(Kind K, MemoryBufferRef M); - std::vector Sections; - std::vector Symbols; + InputFile(Kind k, MemoryBufferRef m); + std::vector sections; private: - const Kind FileKind; + const Kind fileKind; }; -template class ELFFileBase : public InputFile { +class ELFFileBase : public InputFile { public: - typedef typename ELFT::Shdr Elf_Shdr; - typedef typename ELFT::Sym Elf_Sym; - typedef typename ELFT::Word Elf_Word; - typedef typename ELFT::SymRange Elf_Sym_Range; + ELFFileBase(Kind k, MemoryBufferRef m); + static bool classof(const InputFile *f) { return f->isElf(); } - ELFFileBase(Kind K, MemoryBufferRef M); - static bool classof(const InputFile *F) { return F->isElf(); } - - llvm::object::ELFFile getObj() const { - return check(llvm::object::ELFFile::create(MB.getBuffer())); + template llvm::object::ELFFile getObj() const { + return check(llvm::object::ELFFile::create(mb.getBuffer())); } - StringRef getStringTable() const { return StringTable; } - - uint32_t getSectionIndex(const Elf_Sym &Sym) const; + StringRef getStringTable() const { return stringTable; } - Elf_Sym_Range getGlobalELFSyms(); - Elf_Sym_Range getELFSyms() const { return ELFSyms; } + template typename ELFT::SymRange getELFSyms() const { + return typename ELFT::SymRange( + reinterpret_cast(elfSyms), numELFSyms); + } + template typename ELFT::SymRange getGlobalELFSyms() const { + return getELFSyms().slice(firstGlobal); + } protected: - ArrayRef ELFSyms; - uint32_t FirstGlobal = 0; - ArrayRef SymtabSHNDX; - StringRef StringTable; - void initSymtab(ArrayRef Sections, const Elf_Shdr *Symtab); + // Initializes this class's member variables. + template void init(); + + const void *elfSyms = nullptr; + size_t numELFSyms = 0; + uint32_t firstGlobal = 0; + StringRef stringTable; }; // .o file. -template class ObjFile : public ELFFileBase { - typedef ELFFileBase Base; - typedef typename ELFT::Rel Elf_Rel; - typedef typename ELFT::Rela Elf_Rela; - typedef typename ELFT::Sym Elf_Sym; - typedef typename ELFT::Shdr Elf_Shdr; - typedef typename ELFT::Word Elf_Word; - typedef typename ELFT::CGProfile Elf_CGProfile; - - StringRef getShtGroupSignature(ArrayRef Sections, - const Elf_Shdr &Sec); - ArrayRef getShtGroupEntries(const Elf_Shdr &Sec); +template class ObjFile : public ELFFileBase { + using Elf_Rel = typename ELFT::Rel; + using Elf_Rela = typename ELFT::Rela; + using Elf_Sym = typename ELFT::Sym; + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Word = typename ELFT::Word; + using Elf_CGProfile = typename ELFT::CGProfile; public: - static bool classof(const InputFile *F) { return F->kind() == Base::ObjKind; } + static bool classof(const InputFile *f) { return f->kind() == ObjKind; } + + llvm::object::ELFFile getObj() const { + return this->ELFFileBase::getObj(); + } ArrayRef getLocalSymbols(); ArrayRef getGlobalSymbols(); - ObjFile(MemoryBufferRef M, StringRef ArchiveName); - void parse(llvm::DenseSet &ComdatGroups); + ObjFile(MemoryBufferRef m, StringRef archiveName) : ELFFileBase(ObjKind, m) { + this->archiveName = archiveName; + } + + void parse(bool ignoreComdats = false); - Symbol &getSymbol(uint32_t SymbolIndex) const { - if (SymbolIndex >= this->Symbols.size()) + StringRef getShtGroupSignature(ArrayRef sections, + const Elf_Shdr &sec); + + Symbol &getSymbol(uint32_t symbolIndex) const { + if (symbolIndex >= this->symbols.size()) fatal(toString(this) + ": invalid symbol index"); - return *this->Symbols[SymbolIndex]; + return *this->symbols[symbolIndex]; } - template Symbol &getRelocTargetSym(const RelT &Rel) const { - uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL); - return getSymbol(SymIndex); + uint32_t getSectionIndex(const Elf_Sym &sym) const; + + template Symbol &getRelocTargetSym(const RelT &rel) const { + uint32_t symIndex = rel.getSymbol(config->isMips64EL); + return getSymbol(symIndex); } llvm::Optional getDILineInfo(InputSectionBase *, uint64_t); - llvm::Optional> getVariableLoc(StringRef Name); + llvm::Optional> getVariableLoc(StringRef name); // MIPS GP0 value defined by this file. This value represents the gp value // used to create the relocatable object and required to support // R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations. - uint32_t MipsGp0 = 0; + uint32_t mipsGp0 = 0; + + uint32_t andFeatures = 0; // Name of source file obtained from STT_FILE symbol value, // or empty string if there is no such symbol in object file // symbol table. - StringRef SourceFile; + StringRef sourceFile; // True if the file defines functions compiled with // -fsplit-stack. Usually false. - bool SplitStack = false; + bool splitStack = false; // True if the file defines functions compiled with -fsplit-stack, // but had one or more functions with the no_split_stack attribute. - bool SomeNoSplitStack = false; + bool someNoSplitStack = false; // Pointer to this input file's .llvm_addrsig section, if it has one. - const Elf_Shdr *AddrsigSec = nullptr; + const Elf_Shdr *addrsigSec = nullptr; // SHT_LLVM_CALL_GRAPH_PROFILE table - ArrayRef CGProfile; + ArrayRef cgProfile; private: - void - initializeSections(llvm::DenseSet &ComdatGroups); + void initializeSections(bool ignoreComdats); void initializeSymbols(); void initializeJustSymbols(); void initializeDwarf(); - InputSectionBase *getRelocTarget(const Elf_Shdr &Sec); - InputSectionBase *createInputSection(const Elf_Shdr &Sec); - StringRef getSectionName(const Elf_Shdr &Sec); - - bool shouldMerge(const Elf_Shdr &Sec); - Symbol *createSymbol(const Elf_Sym *Sym); + InputSectionBase *getRelocTarget(const Elf_Shdr &sec); + InputSectionBase *createInputSection(const Elf_Shdr &sec); + StringRef getSectionName(const Elf_Shdr &sec); + + bool shouldMerge(const Elf_Shdr &sec); + + // Each ELF symbol contains a section index which the symbol belongs to. + // However, because the number of bits dedicated for that is limited, a + // symbol can directly point to a section only when the section index is + // equal to or smaller than 65280. + // + // If an object file contains more than 65280 sections, the file must + // contain .symtab_shndx section. The section contains an array of + // 32-bit integers whose size is the same as the number of symbols. + // Nth symbol's section index is in the Nth entry of .symtab_shndx. + // + // The following variable contains the contents of .symtab_shndx. + // If the section does not exist (which is common), the array is empty. + ArrayRef shndxTable; // .shstrtab contents. - StringRef SectionStringTable; + StringRef sectionStringTable; // Debugging information to retrieve source file and line for error // reporting. Linker may find reasonable number of errors in a // single object file, so we cache debugging information in order to // parse it only once for each object file we link. - std::unique_ptr Dwarf; - std::vector LineTables; + std::unique_ptr dwarf; + std::vector lineTables; struct VarLoc { - const llvm::DWARFDebugLine::LineTable *LT; - unsigned File; - unsigned Line; + const llvm::DWARFDebugLine::LineTable *lt; + unsigned file; + unsigned line; }; - llvm::DenseMap VariableLoc; - llvm::once_flag InitDwarfLine; + llvm::DenseMap variableLoc; + llvm::once_flag initDwarfLine; }; // LazyObjFile is analogous to ArchiveFile in the sense that @@ -264,118 +304,100 @@ private: // archive file semantics. class LazyObjFile : public InputFile { public: - LazyObjFile(MemoryBufferRef M, StringRef ArchiveName, - uint64_t OffsetInArchive) - : InputFile(LazyObjKind, M), OffsetInArchive(OffsetInArchive) { - this->ArchiveName = ArchiveName; + LazyObjFile(MemoryBufferRef m, StringRef archiveName, + uint64_t offsetInArchive) + : InputFile(LazyObjKind, m), offsetInArchive(offsetInArchive) { + this->archiveName = archiveName; } - static bool classof(const InputFile *F) { return F->kind() == LazyObjKind; } + static bool classof(const InputFile *f) { return f->kind() == LazyObjKind; } template void parse(); - MemoryBufferRef getBuffer(); - InputFile *fetch(); - bool AddedToLink = false; + void fetch(); private: - uint64_t OffsetInArchive; + uint64_t offsetInArchive; }; // An ArchiveFile object represents a .a file. class ArchiveFile : public InputFile { public: - explicit ArchiveFile(std::unique_ptr &&File); - static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; } - template void parse(); + explicit ArchiveFile(std::unique_ptr &&file); + static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; } + void parse(); // Pulls out an object file that contains a definition for Sym and // returns it. If the same file was instantiated before, this - // function returns a nullptr (so we don't instantiate the same file + // function does nothing (so we don't instantiate the same file // more than once.) - InputFile *fetch(const Archive::Symbol &Sym); + void fetch(const Archive::Symbol &sym); private: - std::unique_ptr File; - llvm::DenseSet Seen; + std::unique_ptr file; + llvm::DenseSet seen; }; class BitcodeFile : public InputFile { public: - BitcodeFile(MemoryBufferRef M, StringRef ArchiveName, - uint64_t OffsetInArchive); - static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; } - template - void parse(llvm::DenseSet &ComdatGroups); - std::unique_ptr Obj; + BitcodeFile(MemoryBufferRef m, StringRef archiveName, + uint64_t offsetInArchive); + static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } + template void parse(); + std::unique_ptr obj; }; // .so file. -template class SharedFile : public ELFFileBase { - typedef ELFFileBase Base; - typedef typename ELFT::Dyn Elf_Dyn; - typedef typename ELFT::Shdr Elf_Shdr; - typedef typename ELFT::Sym Elf_Sym; - typedef typename ELFT::SymRange Elf_Sym_Range; - typedef typename ELFT::Verdef Elf_Verdef; - typedef typename ELFT::Versym Elf_Versym; - - const Elf_Shdr *VersymSec = nullptr; - const Elf_Shdr *VerdefSec = nullptr; - +class SharedFile : public ELFFileBase { public: - std::vector Verdefs; - std::string SoName; + SharedFile(MemoryBufferRef m, StringRef defaultSoName) + : ELFFileBase(SharedKind, m), soName(defaultSoName), + isNeeded(!config->asNeeded) {} - static bool classof(const InputFile *F) { - return F->kind() == Base::SharedKind; - } + // This is actually a vector of Elf_Verdef pointers. + std::vector verdefs; - SharedFile(MemoryBufferRef M, StringRef DefaultSoName); + // If the output file needs Elf_Verneed data structures for this file, this is + // a vector of Elf_Vernaux version identifiers that map onto the entries in + // Verdefs, otherwise it is empty. + std::vector vernauxs; - void parseSoName(); - void parseRest(); - uint32_t getAlignment(ArrayRef Sections, const Elf_Sym &Sym); - std::vector parseVerdefs(); - std::vector parseVersyms(); + static unsigned vernauxNum; - struct NeededVer { - // The string table offset of the version name in the output file. - size_t StrTab; + std::vector dtNeeded; + std::string soName; - // The version identifier for this version name. - uint16_t Index; - }; + static bool classof(const InputFile *f) { return f->kind() == SharedKind; } + + template void parse(); - // Mapping from Elf_Verdef data structures to information about Elf_Vernaux - // data structures in the output file. - std::map VerdefMap; + // Used for --no-allow-shlib-undefined. + bool allNeededIsKnown; // Used for --as-needed - bool IsNeeded; + bool isNeeded; }; class BinaryFile : public InputFile { public: - explicit BinaryFile(MemoryBufferRef M) : InputFile(BinaryKind, M) {} - static bool classof(const InputFile *F) { return F->kind() == BinaryKind; } + explicit BinaryFile(MemoryBufferRef m) : InputFile(BinaryKind, m) {} + static bool classof(const InputFile *f) { return f->kind() == BinaryKind; } void parse(); }; -InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName = "", - uint64_t OffsetInArchive = 0); -InputFile *createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName); +InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName = "", + uint64_t offsetInArchive = 0); -inline bool isBitcode(MemoryBufferRef MB) { - return identify_magic(MB.getBuffer()) == llvm::file_magic::bitcode; +inline bool isBitcode(MemoryBufferRef mb) { + return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode; } -std::string replaceThinLTOSuffix(StringRef Path); +std::string replaceThinLTOSuffix(StringRef path); -extern std::vector BinaryFiles; -extern std::vector BitcodeFiles; -extern std::vector LazyObjFiles; -extern std::vector ObjectFiles; -extern std::vector SharedFiles; +extern std::vector binaryFiles; +extern std::vector bitcodeFiles; +extern std::vector lazyObjFiles; +extern std::vector objectFiles; +extern std::vector sharedFiles; } // namespace elf } // namespace lld diff --git a/ELF/InputSection.cpp b/ELF/InputSection.cpp index 839bff7..a024ac3 100644 --- a/ELF/InputSection.cpp +++ b/ELF/InputSection.cpp @@ -1,9 +1,8 @@ //===- InputSection.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 // //===----------------------------------------------------------------------===// @@ -41,52 +40,52 @@ using namespace llvm::sys; using namespace lld; using namespace lld::elf; -std::vector elf::InputSections; +std::vector elf::inputSections; // Returns a string to construct an error message. -std::string lld::toString(const InputSectionBase *Sec) { - return (toString(Sec->File) + ":(" + Sec->Name + ")").str(); +std::string lld::toString(const InputSectionBase *sec) { + return (toString(sec->file) + ":(" + sec->name + ")").str(); } template -static ArrayRef getSectionContents(ObjFile &File, - const typename ELFT::Shdr &Hdr) { - if (Hdr.sh_type == SHT_NOBITS) - return makeArrayRef(nullptr, Hdr.sh_size); - return check(File.getObj().getSectionContents(&Hdr)); +static ArrayRef getSectionContents(ObjFile &file, + const typename ELFT::Shdr &hdr) { + if (hdr.sh_type == SHT_NOBITS) + return makeArrayRef(nullptr, hdr.sh_size); + return check(file.getObj().getSectionContents(&hdr)); } -InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags, - uint32_t Type, uint64_t Entsize, - uint32_t Link, uint32_t Info, - uint32_t Alignment, ArrayRef Data, - StringRef Name, Kind SectionKind) - : SectionBase(SectionKind, Name, Flags, Entsize, Alignment, Type, Info, - Link), - File(File), RawData(Data) { +InputSectionBase::InputSectionBase(InputFile *file, uint64_t flags, + uint32_t type, uint64_t entsize, + uint32_t link, uint32_t info, + uint32_t alignment, ArrayRef data, + StringRef name, Kind sectionKind) + : SectionBase(sectionKind, name, flags, entsize, alignment, type, info, + link), + file(file), rawData(data) { // In order to reduce memory allocation, we assume that mergeable // sections are smaller than 4 GiB, which is not an unreasonable // assumption as of 2017. - if (SectionKind == SectionBase::Merge && RawData.size() > UINT32_MAX) + if (sectionKind == SectionBase::Merge && rawData.size() > UINT32_MAX) error(toString(this) + ": section too large"); - NumRelocations = 0; - AreRelocsRela = false; + numRelocations = 0; + areRelocsRela = false; // The ELF spec states that a value of 0 means the section has // no alignment constraits. - uint32_t V = std::max(Alignment, 1); - if (!isPowerOf2_64(V)) - fatal(toString(File) + ": section sh_addralign is not a power of 2"); - this->Alignment = V; + uint32_t v = std::max(alignment, 1); + if (!isPowerOf2_64(v)) + fatal(toString(this) + ": sh_addralign is not a power of 2"); + this->alignment = v; // In ELF, each section can be compressed by zlib, and if compressed, // section name may be mangled by appending "z" (e.g. ".zdebug_info"). // If that's the case, demangle section name so that we can handle a // section as if it weren't compressed. - if ((Flags & SHF_COMPRESSED) || Name.startswith(".zdebug")) { + if ((flags & SHF_COMPRESSED) || name.startswith(".zdebug")) { if (!zlib::isAvailable()) - error(toString(File) + ": contains a compressed section, " + + error(toString(file) + ": contains a compressed section, " + "but zlib is not available"); parseCompressedHeader(); } @@ -95,11 +94,11 @@ InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags, // Drop SHF_GROUP bit unless we are producing a re-linkable object file. // SHF_GROUP is a marker that a section belongs to some comdat group. // That flag doesn't make sense in an executable. -static uint64_t getFlags(uint64_t Flags) { - Flags &= ~(uint64_t)SHF_INFO_LINK; - if (!Config->Relocatable) - Flags &= ~(uint64_t)SHF_GROUP; - return Flags; +static uint64_t getFlags(uint64_t flags) { + flags &= ~(uint64_t)SHF_INFO_LINK; + if (!config->relocatable) + flags &= ~(uint64_t)SHF_GROUP; + return flags; } // GNU assembler 2.24 and LLVM 4.0.0's MC (the newest release as of @@ -112,205 +111,212 @@ static uint64_t getFlags(uint64_t Flags) { // // This function forces SHT_{INIT,FINI}_ARRAY so that we can handle // incorrect inputs as if they were correct from the beginning. -static uint64_t getType(uint64_t Type, StringRef Name) { - if (Type == SHT_PROGBITS && Name.startswith(".init_array.")) +static uint64_t getType(uint64_t type, StringRef name) { + if (type == SHT_PROGBITS && name.startswith(".init_array.")) return SHT_INIT_ARRAY; - if (Type == SHT_PROGBITS && Name.startswith(".fini_array.")) + if (type == SHT_PROGBITS && name.startswith(".fini_array.")) return SHT_FINI_ARRAY; - return Type; + return type; } template -InputSectionBase::InputSectionBase(ObjFile &File, - const typename ELFT::Shdr &Hdr, - StringRef Name, Kind SectionKind) - : InputSectionBase(&File, getFlags(Hdr.sh_flags), - getType(Hdr.sh_type, Name), Hdr.sh_entsize, Hdr.sh_link, - Hdr.sh_info, Hdr.sh_addralign, - getSectionContents(File, Hdr), Name, SectionKind) { +InputSectionBase::InputSectionBase(ObjFile &file, + const typename ELFT::Shdr &hdr, + StringRef name, Kind sectionKind) + : InputSectionBase(&file, getFlags(hdr.sh_flags), + getType(hdr.sh_type, name), hdr.sh_entsize, hdr.sh_link, + hdr.sh_info, hdr.sh_addralign, + getSectionContents(file, hdr), name, sectionKind) { // We reject object files having insanely large alignments even though // they are allowed by the spec. I think 4GB is a reasonable limitation. // We might want to relax this in the future. - if (Hdr.sh_addralign > UINT32_MAX) - fatal(toString(&File) + ": section sh_addralign is too large"); + if (hdr.sh_addralign > UINT32_MAX) + fatal(toString(&file) + ": section sh_addralign is too large"); } size_t InputSectionBase::getSize() const { - if (auto *S = dyn_cast(this)) - return S->getSize(); - if (UncompressedSize >= 0) - return UncompressedSize; - return RawData.size(); + if (auto *s = dyn_cast(this)) + return s->getSize(); + if (uncompressedSize >= 0) + return uncompressedSize; + return rawData.size(); } void InputSectionBase::uncompress() const { - size_t Size = UncompressedSize; - UncompressedBuf.reset(new char[Size]); + size_t size = uncompressedSize; + char *uncompressedBuf; + { + static std::mutex mu; + std::lock_guard lock(mu); + uncompressedBuf = bAlloc.Allocate(size); + } - if (Error E = - zlib::uncompress(toStringRef(RawData), UncompressedBuf.get(), Size)) + if (Error e = zlib::uncompress(toStringRef(rawData), uncompressedBuf, size)) fatal(toString(this) + - ": uncompress failed: " + llvm::toString(std::move(E))); - RawData = makeArrayRef((uint8_t *)UncompressedBuf.get(), Size); + ": uncompress failed: " + llvm::toString(std::move(e))); + rawData = makeArrayRef((uint8_t *)uncompressedBuf, size); + uncompressedSize = -1; } uint64_t InputSectionBase::getOffsetInFile() const { - const uint8_t *FileStart = (const uint8_t *)File->MB.getBufferStart(); - const uint8_t *SecStart = data().begin(); - return SecStart - FileStart; + const uint8_t *fileStart = (const uint8_t *)file->mb.getBufferStart(); + const uint8_t *secStart = data().begin(); + return secStart - fileStart; } -uint64_t SectionBase::getOffset(uint64_t Offset) const { +uint64_t SectionBase::getOffset(uint64_t offset) const { switch (kind()) { case Output: { - auto *OS = cast(this); + auto *os = cast(this); // For output sections we treat offset -1 as the end of the section. - return Offset == uint64_t(-1) ? OS->Size : Offset; + return offset == uint64_t(-1) ? os->size : offset; } case Regular: case Synthetic: - return cast(this)->getOffset(Offset); + return cast(this)->getOffset(offset); case EHFrame: // The file crtbeginT.o has relocations pointing to the start of an empty // .eh_frame that is known to be the first in the link. It does that to // identify the start of the output .eh_frame. - return Offset; + return offset; case Merge: - const MergeInputSection *MS = cast(this); - if (InputSection *IS = MS->getParent()) - return IS->getOffset(MS->getParentOffset(Offset)); - return MS->getParentOffset(Offset); + const MergeInputSection *ms = cast(this); + if (InputSection *isec = ms->getParent()) + return isec->getOffset(ms->getParentOffset(offset)); + return ms->getParentOffset(offset); } llvm_unreachable("invalid section kind"); } -uint64_t SectionBase::getVA(uint64_t Offset) const { - const OutputSection *Out = getOutputSection(); - return (Out ? Out->Addr : 0) + getOffset(Offset); +uint64_t SectionBase::getVA(uint64_t offset) const { + const OutputSection *out = getOutputSection(); + return (out ? out->addr : 0) + getOffset(offset); } OutputSection *SectionBase::getOutputSection() { - InputSection *Sec; - if (auto *IS = dyn_cast(this)) - Sec = IS; - else if (auto *MS = dyn_cast(this)) - Sec = MS->getParent(); - else if (auto *EH = dyn_cast(this)) - Sec = EH->getParent(); + InputSection *sec; + if (auto *isec = dyn_cast(this)) + sec = isec; + else if (auto *ms = dyn_cast(this)) + sec = ms->getParent(); + else if (auto *eh = dyn_cast(this)) + sec = eh->getParent(); else return cast(this); - return Sec ? Sec->getParent() : nullptr; + return sec ? sec->getParent() : nullptr; } -// When a section is compressed, `RawData` consists with a header followed +// When a section is compressed, `rawData` consists with a header followed // by zlib-compressed data. This function parses a header to initialize -// `UncompressedSize` member and remove the header from `RawData`. +// `uncompressedSize` member and remove the header from `rawData`. void InputSectionBase::parseCompressedHeader() { - typedef typename ELF64LE::Chdr Chdr64; - typedef typename ELF32LE::Chdr Chdr32; + using Chdr64 = typename ELF64LE::Chdr; + using Chdr32 = typename ELF32LE::Chdr; // Old-style header - if (Name.startswith(".zdebug")) { - if (!toStringRef(RawData).startswith("ZLIB")) { + if (name.startswith(".zdebug")) { + if (!toStringRef(rawData).startswith("ZLIB")) { error(toString(this) + ": corrupted compressed section header"); return; } - RawData = RawData.slice(4); + rawData = rawData.slice(4); - if (RawData.size() < 8) { + if (rawData.size() < 8) { error(toString(this) + ": corrupted compressed section header"); return; } - UncompressedSize = read64be(RawData.data()); - RawData = RawData.slice(8); + uncompressedSize = read64be(rawData.data()); + rawData = rawData.slice(8); // Restore the original section name. // (e.g. ".zdebug_info" -> ".debug_info") - Name = Saver.save("." + Name.substr(2)); + name = saver.save("." + name.substr(2)); return; } - assert(Flags & SHF_COMPRESSED); - Flags &= ~(uint64_t)SHF_COMPRESSED; + assert(flags & SHF_COMPRESSED); + flags &= ~(uint64_t)SHF_COMPRESSED; // New-style 64-bit header - if (Config->Is64) { - if (RawData.size() < sizeof(Chdr64)) { + if (config->is64) { + if (rawData.size() < sizeof(Chdr64)) { error(toString(this) + ": corrupted compressed section"); return; } - auto *Hdr = reinterpret_cast(RawData.data()); - if (Hdr->ch_type != ELFCOMPRESS_ZLIB) { + auto *hdr = reinterpret_cast(rawData.data()); + if (hdr->ch_type != ELFCOMPRESS_ZLIB) { error(toString(this) + ": unsupported compression type"); return; } - UncompressedSize = Hdr->ch_size; - RawData = RawData.slice(sizeof(*Hdr)); + uncompressedSize = hdr->ch_size; + alignment = std::max(hdr->ch_addralign, 1); + rawData = rawData.slice(sizeof(*hdr)); return; } // New-style 32-bit header - if (RawData.size() < sizeof(Chdr32)) { + if (rawData.size() < sizeof(Chdr32)) { error(toString(this) + ": corrupted compressed section"); return; } - auto *Hdr = reinterpret_cast(RawData.data()); - if (Hdr->ch_type != ELFCOMPRESS_ZLIB) { + auto *hdr = reinterpret_cast(rawData.data()); + if (hdr->ch_type != ELFCOMPRESS_ZLIB) { error(toString(this) + ": unsupported compression type"); return; } - UncompressedSize = Hdr->ch_size; - RawData = RawData.slice(sizeof(*Hdr)); + uncompressedSize = hdr->ch_size; + alignment = std::max(hdr->ch_addralign, 1); + rawData = rawData.slice(sizeof(*hdr)); } InputSection *InputSectionBase::getLinkOrderDep() const { - assert(Link); - assert(Flags & SHF_LINK_ORDER); - return cast(File->getSections()[Link]); + assert(link); + assert(flags & SHF_LINK_ORDER); + return cast(file->getSections()[link]); } // Find a function symbol that encloses a given location. template -Defined *InputSectionBase::getEnclosingFunction(uint64_t Offset) { - for (Symbol *B : File->getSymbols()) - if (Defined *D = dyn_cast(B)) - if (D->Section == this && D->Type == STT_FUNC && D->Value <= Offset && - Offset < D->Value + D->Size) - return D; +Defined *InputSectionBase::getEnclosingFunction(uint64_t offset) { + for (Symbol *b : file->getSymbols()) + if (Defined *d = dyn_cast(b)) + if (d->section == this && d->type == STT_FUNC && d->value <= offset && + offset < d->value + d->size) + return d; return nullptr; } // Returns a source location string. Used to construct an error message. template -std::string InputSectionBase::getLocation(uint64_t Offset) { - std::string SecAndOffset = (Name + "+0x" + utohexstr(Offset)).str(); +std::string InputSectionBase::getLocation(uint64_t offset) { + std::string secAndOffset = (name + "+0x" + utohexstr(offset)).str(); // We don't have file for synthetic sections. if (getFile() == nullptr) - return (Config->OutputFile + ":(" + SecAndOffset + ")") + return (config->outputFile + ":(" + secAndOffset + ")") .str(); // First check if we can get desired values from debugging information. - if (Optional Info = getFile()->getDILineInfo(this, Offset)) - return Info->FileName + ":" + std::to_string(Info->Line) + ":(" + - SecAndOffset + ")"; + if (Optional info = getFile()->getDILineInfo(this, offset)) + return info->FileName + ":" + std::to_string(info->Line) + ":(" + + secAndOffset + ")"; - // File->SourceFile contains STT_FILE symbol that contains a + // File->sourceFile contains STT_FILE symbol that contains a // source file name. If it's missing, we use an object file name. - std::string SrcFile = getFile()->SourceFile; - if (SrcFile.empty()) - SrcFile = toString(File); + std::string srcFile = getFile()->sourceFile; + if (srcFile.empty()) + srcFile = toString(file); - if (Defined *D = getEnclosingFunction(Offset)) - return SrcFile + ":(function " + toString(*D) + ": " + SecAndOffset + ")"; + if (Defined *d = getEnclosingFunction(offset)) + return srcFile + ":(function " + toString(*d) + ": " + secAndOffset + ")"; // If there's no symbol, print out the offset in the section. - return (SrcFile + ":(" + SecAndOffset + ")"); + return (srcFile + ":(" + secAndOffset + ")"); } // This function is intended to be used for constructing an error message. @@ -319,8 +325,8 @@ std::string InputSectionBase::getLocation(uint64_t Offset) { // foo.c:42 (/home/alice/possibly/very/long/path/foo.c:42) // // Returns an empty string if there's no way to get line info. -std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) { - return File->getSrcMsg(Sym, *this, Offset); +std::string InputSectionBase::getSrcMsg(const Symbol &sym, uint64_t offset) { + return file->getSrcMsg(sym, *this, offset); } // Returns a filename string along with an optional section name. This @@ -332,95 +338,96 @@ std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) { // or // // path/to/foo.o:(function bar) in archive path/to/bar.a -std::string InputSectionBase::getObjMsg(uint64_t Off) { - std::string Filename = File->getName(); +std::string InputSectionBase::getObjMsg(uint64_t off) { + std::string filename = file->getName(); - std::string Archive; - if (!File->ArchiveName.empty()) - Archive = " in archive " + File->ArchiveName; + std::string archive; + if (!file->archiveName.empty()) + archive = " in archive " + file->archiveName; // Find a symbol that encloses a given location. - for (Symbol *B : File->getSymbols()) - if (auto *D = dyn_cast(B)) - if (D->Section == this && D->Value <= Off && Off < D->Value + D->Size) - return Filename + ":(" + toString(*D) + ")" + Archive; + for (Symbol *b : file->getSymbols()) + if (auto *d = dyn_cast(b)) + if (d->section == this && d->value <= off && off < d->value + d->size) + return filename + ":(" + toString(*d) + ")" + archive; // If there's no symbol, print out the offset in the section. - return (Filename + ":(" + Name + "+0x" + utohexstr(Off) + ")" + Archive) + return (filename + ":(" + name + "+0x" + utohexstr(off) + ")" + archive) .str(); } -InputSection InputSection::Discarded(nullptr, 0, 0, 0, ArrayRef(), ""); +InputSection InputSection::discarded(nullptr, 0, 0, 0, ArrayRef(), ""); -InputSection::InputSection(InputFile *F, uint64_t Flags, uint32_t Type, - uint32_t Alignment, ArrayRef Data, - StringRef Name, Kind K) - : InputSectionBase(F, Flags, Type, - /*Entsize*/ 0, /*Link*/ 0, /*Info*/ 0, Alignment, Data, - Name, K) {} +InputSection::InputSection(InputFile *f, uint64_t flags, uint32_t type, + uint32_t alignment, ArrayRef data, + StringRef name, Kind k) + : InputSectionBase(f, flags, type, + /*Entsize*/ 0, /*Link*/ 0, /*Info*/ 0, alignment, data, + name, k) {} template -InputSection::InputSection(ObjFile &F, const typename ELFT::Shdr &Header, - StringRef Name) - : InputSectionBase(F, Header, Name, InputSectionBase::Regular) {} +InputSection::InputSection(ObjFile &f, const typename ELFT::Shdr &header, + StringRef name) + : InputSectionBase(f, header, name, InputSectionBase::Regular) {} -bool InputSection::classof(const SectionBase *S) { - return S->kind() == SectionBase::Regular || - S->kind() == SectionBase::Synthetic; +bool InputSection::classof(const SectionBase *s) { + return s->kind() == SectionBase::Regular || + s->kind() == SectionBase::Synthetic; } OutputSection *InputSection::getParent() const { - return cast_or_null(Parent); + return cast_or_null(parent); } // Copy SHT_GROUP section contents. Used only for the -r option. -template void InputSection::copyShtGroup(uint8_t *Buf) { +template void InputSection::copyShtGroup(uint8_t *buf) { // ELFT::Word is the 32-bit integral type in the target endianness. - typedef typename ELFT::Word u32; - ArrayRef From = getDataAs(); - auto *To = reinterpret_cast(Buf); + using u32 = typename ELFT::Word; + ArrayRef from = getDataAs(); + auto *to = reinterpret_cast(buf); // The first entry is not a section number but a flag. - *To++ = From[0]; + *to++ = from[0]; // Adjust section numbers because section numbers in an input object // files are different in the output. - ArrayRef Sections = File->getSections(); - for (uint32_t Idx : From.slice(1)) - *To++ = Sections[Idx]->getOutputSection()->SectionIndex; + ArrayRef sections = file->getSections(); + for (uint32_t idx : from.slice(1)) + *to++ = sections[idx]->getOutputSection()->sectionIndex; } InputSectionBase *InputSection::getRelocatedSection() const { - if (!File || (Type != SHT_RELA && Type != SHT_REL)) + if (!file || (type != SHT_RELA && type != SHT_REL)) return nullptr; - ArrayRef Sections = File->getSections(); - return Sections[Info]; + ArrayRef sections = file->getSections(); + return sections[info]; } // This is used for -r and --emit-relocs. We can't use memcpy to copy // relocations because we need to update symbol table offset and section index // for each relocation. So we copy relocations one by one. template -void InputSection::copyRelocations(uint8_t *Buf, ArrayRef Rels) { - InputSectionBase *Sec = getRelocatedSection(); +void InputSection::copyRelocations(uint8_t *buf, ArrayRef rels) { + InputSectionBase *sec = getRelocatedSection(); - for (const RelTy &Rel : Rels) { - RelType Type = Rel.getType(Config->IsMips64EL); - Symbol &Sym = getFile()->getRelocTargetSym(Rel); + for (const RelTy &rel : rels) { + RelType type = rel.getType(config->isMips64EL); + const ObjFile *file = getFile(); + Symbol &sym = file->getRelocTargetSym(rel); - auto *P = reinterpret_cast(Buf); - Buf += sizeof(RelTy); + auto *p = reinterpret_cast(buf); + buf += sizeof(RelTy); if (RelTy::IsRela) - P->r_addend = getAddend(Rel); + p->r_addend = getAddend(rel); // Output section VA is zero for -r, so r_offset is an offset within the // section, but for --emit-relocs it is an virtual address. - P->r_offset = Sec->getVA(Rel.r_offset); - P->setSymbolAndType(In.SymTab->getSymbolIndex(&Sym), Type, - Config->IsMips64EL); + p->r_offset = sec->getVA(rel.r_offset); + p->setSymbolAndType(in.symTab->getSymbolIndex(&sym), type, + config->isMips64EL); - if (Sym.Type == STT_SECTION) { + if (sym.type == STT_SECTION) { // We combine multiple section symbols into only one per // section. This means we have to update the addend. That is // trivial for Elf_Rela, but for Elf_Rel we have to write to the @@ -429,25 +436,38 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef Rels) { // .eh_frame is horribly special and can reference discarded sections. To // avoid having to parse and recreate .eh_frame, we just replace any // relocation in it pointing to discarded sections with R_*_NONE, which - // hopefully creates a frame that is ignored at runtime. - auto *D = dyn_cast(&Sym); - if (!D) { - error("STT_SECTION symbol should be defined"); + // hopefully creates a frame that is ignored at runtime. Also, don't warn + // on .gcc_except_table and debug sections. + // + // See the comment in maybeReportUndefined for PPC64 .toc . + auto *d = dyn_cast(&sym); + if (!d) { + if (!sec->name.startswith(".debug") && + !sec->name.startswith(".zdebug") && sec->name != ".eh_frame" && + sec->name != ".gcc_except_table" && sec->name != ".toc") { + uint32_t secIdx = cast(sym).discardedSecIdx; + Elf_Shdr_Impl sec = + CHECK(file->getObj().sections(), file)[secIdx]; + warn("relocation refers to a discarded section: " + + CHECK(file->getObj().getSectionName(&sec), file) + + "\n>>> referenced by " + getObjMsg(p->r_offset)); + } + p->setSymbolAndType(0, 0, false); continue; } - SectionBase *Section = D->Section->Repl; - if (!Section->Live) { - P->setSymbolAndType(0, 0, false); + SectionBase *section = d->section->repl; + if (!section->isLive()) { + p->setSymbolAndType(0, 0, false); continue; } - int64_t Addend = getAddend(Rel); - const uint8_t *BufLoc = Sec->data().begin() + Rel.r_offset; + int64_t addend = getAddend(rel); + const uint8_t *bufLoc = sec->data().begin() + rel.r_offset; if (!RelTy::IsRela) - Addend = Target->getImplicitAddend(BufLoc, Type); + addend = target->getImplicitAddend(bufLoc, type); - if (Config->EMachine == EM_MIPS && Config->Relocatable && - Target->getRelExpr(Type, Sym, BufLoc) == R_MIPS_GOTREL) { + if (config->emachine == EM_MIPS && config->relocatable && + target->getRelExpr(type, sym, bufLoc) == R_MIPS_GOTREL) { // Some MIPS relocations depend on "gp" value. By default, // this value has 0x7ff0 offset from a .got section. But // relocatable files produced by a complier or a linker @@ -459,13 +479,13 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef Rels) { // individual "gp" values used by each input object file. // As a workaround we add the "gp" value to the relocation // addend and save it back to the file. - Addend += Sec->getFile()->MipsGp0; + addend += sec->getFile()->mipsGp0; } if (RelTy::IsRela) - P->r_addend = Sym.getVA(Addend) - Section->getOutputSection()->Addr; - else if (Config->Relocatable) - Sec->Relocations.push_back({R_ABS, Type, Rel.r_offset, Addend, &Sym}); + p->r_addend = sym.getVA(addend) - section->getOutputSection()->addr; + else if (config->relocatable && type != target->noneRel) + sec->relocations.push_back({R_ABS, type, rel.r_offset, addend, &sym}); } } } @@ -475,13 +495,13 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef Rels) { // this context is the address of the place P. A further special case is that // branch relocations to an undefined weak reference resolve to the next // instruction. -static uint32_t getARMUndefinedRelativeWeakVA(RelType Type, uint32_t A, - uint32_t P) { - switch (Type) { +static uint32_t getARMUndefinedRelativeWeakVA(RelType type, uint32_t a, + uint32_t p) { + switch (type) { // Unresolved branch relocations to weak references resolve to next // instruction, this will be either 2 or 4 bytes on from P. case R_ARM_THM_JUMP11: - return P + 2 + A; + return p + 2 + a; case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_PC24: @@ -489,10 +509,10 @@ static uint32_t getARMUndefinedRelativeWeakVA(RelType Type, uint32_t A, case R_ARM_PREL31: case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: - return P + 4 + A; + return p + 4 + a; case R_ARM_THM_CALL: // We don't want an interworking BLX to ARM - return P + 5 + A; + return p + 5 + a; // Unresolved non branch pc-relative relocations // R_ARM_TARGET2 which can be resolved relatively is not present as it never // targets a weak-reference. @@ -501,29 +521,29 @@ static uint32_t getARMUndefinedRelativeWeakVA(RelType Type, uint32_t A, case R_ARM_REL32: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: - return P + A; + return p + a; } llvm_unreachable("ARM pc-relative relocation expected\n"); } // The comment above getARMUndefinedRelativeWeakVA applies to this function. -static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type, uint64_t A, - uint64_t P) { - switch (Type) { +static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t type, uint64_t a, + uint64_t p) { + switch (type) { // Unresolved branch relocations to weak references resolve to next // instruction, this is 4 bytes on from P. case R_AARCH64_CALL26: case R_AARCH64_CONDBR19: case R_AARCH64_JUMP26: case R_AARCH64_TSTBR14: - return P + 4 + A; + return p + 4 + a; // Unresolved non branch pc-relative relocations case R_AARCH64_PREL16: case R_AARCH64_PREL32: case R_AARCH64_PREL64: case R_AARCH64_ADR_PREL_LO21: case R_AARCH64_LD_PREL_LO19: - return P + A; + return p + a; } llvm_unreachable("AArch64 pc-relative relocation expected\n"); } @@ -535,11 +555,11 @@ static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type, uint64_t A, // The procedure call standard only defines a Read Write Position Independent // RWPI variant so in practice we should expect the static base to be the base // of the RW segment. -static uint64_t getARMStaticBase(const Symbol &Sym) { - OutputSection *OS = Sym.getOutputSection(); - if (!OS || !OS->PtLoad || !OS->PtLoad->FirstSec) - fatal("SBREL relocation to " + Sym.getName() + " without static base"); - return OS->PtLoad->FirstSec->Addr; +static uint64_t getARMStaticBase(const Symbol &sym) { + OutputSection *os = sym.getOutputSection(); + if (!os || !os->ptLoad || !os->ptLoad->firstSec) + fatal("SBREL relocation to " + sym.getName() + " without static base"); + return os->ptLoad->firstSec->addr; } // For R_RISCV_PC_INDIRECT (R_RISCV_PCREL_LO12_{I,S}), the symbol actually @@ -548,101 +568,115 @@ static uint64_t getARMStaticBase(const Symbol &Sym) { // // This function returns the R_RISCV_PCREL_HI20 relocation from // R_RISCV_PCREL_LO12's symbol and addend. -static Relocation *getRISCVPCRelHi20(const Symbol *Sym, uint64_t Addend) { - const Defined *D = cast(Sym); - InputSection *IS = cast(D->Section); +static Relocation *getRISCVPCRelHi20(const Symbol *sym, uint64_t addend) { + const Defined *d = cast(sym); + if (!d->section) { + error("R_RISCV_PCREL_LO12 relocation points to an absolute symbol: " + + sym->getName()); + return nullptr; + } + InputSection *isec = cast(d->section); - if (Addend != 0) + if (addend != 0) warn("Non-zero addend in R_RISCV_PCREL_LO12 relocation to " + - IS->getObjMsg(D->Value) + " is ignored"); + isec->getObjMsg(d->value) + " is ignored"); // Relocations are sorted by offset, so we can use std::equal_range to do // binary search. - auto Range = std::equal_range(IS->Relocations.begin(), IS->Relocations.end(), - D->Value, RelocationOffsetComparator{}); - for (auto It = std::get<0>(Range); It != std::get<1>(Range); ++It) - if (isRelExprOneOf(It->Expr)) - return &*It; - - error("R_RISCV_PCREL_LO12 relocation points to " + IS->getObjMsg(D->Value) + + Relocation r; + r.offset = d->value; + auto range = + std::equal_range(isec->relocations.begin(), isec->relocations.end(), r, + [](const Relocation &lhs, const Relocation &rhs) { + return lhs.offset < rhs.offset; + }); + + for (auto it = range.first; it != range.second; ++it) + if (it->type == R_RISCV_PCREL_HI20 || it->type == R_RISCV_GOT_HI20 || + it->type == R_RISCV_TLS_GD_HI20 || it->type == R_RISCV_TLS_GOT_HI20) + return &*it; + + error("R_RISCV_PCREL_LO12 relocation points to " + isec->getObjMsg(d->value) + " without an associated R_RISCV_PCREL_HI20 relocation"); return nullptr; } // A TLS symbol's virtual address is relative to the TLS segment. Add a // target-specific adjustment to produce a thread-pointer-relative offset. -static int64_t getTlsTpOffset() { - switch (Config->EMachine) { +static int64_t getTlsTpOffset(const Symbol &s) { + // On targets that support TLSDESC, _TLS_MODULE_BASE_@tpoff = 0. + if (&s == ElfSym::tlsModuleBase) + return 0; + + switch (config->emachine) { case EM_ARM: case EM_AARCH64: // Variant 1. The thread pointer points to a TCB with a fixed 2-word size, // followed by a variable amount of alignment padding, followed by the TLS // segment. - // - // NB: While the ARM/AArch64 ABI formally has a 2-word TCB size, lld - // effectively increases the TCB size to 8 words for Android compatibility. - // It accomplishes this by increasing the segment's alignment. - return alignTo(Config->Wordsize * 2, Out::TlsPhdr->p_align); + return s.getVA(0) + alignTo(config->wordsize * 2, Out::tlsPhdr->p_align); case EM_386: case EM_X86_64: // Variant 2. The TLS segment is located just before the thread pointer. - return -Out::TlsPhdr->p_memsz; + return s.getVA(0) - alignTo(Out::tlsPhdr->p_memsz, Out::tlsPhdr->p_align); + case EM_PPC: case EM_PPC64: // The thread pointer points to a fixed offset from the start of the // executable's TLS segment. An offset of 0x7000 allows a signed 16-bit // offset to reach 0x1000 of TCB/thread-library data and 0xf000 of the // program's TLS segment. - return -0x7000; + return s.getVA(0) - 0x7000; + case EM_RISCV: + return s.getVA(0); default: llvm_unreachable("unhandled Config->EMachine"); } } -static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, - uint64_t P, const Symbol &Sym, RelExpr Expr) { - switch (Expr) { - case R_INVALID: - return 0; +static uint64_t getRelocTargetVA(const InputFile *file, RelType type, int64_t a, + uint64_t p, const Symbol &sym, RelExpr expr) { + switch (expr) { case R_ABS: + case R_DTPREL: case R_RELAX_TLS_LD_TO_LE_ABS: case R_RELAX_GOT_PC_NOPIC: - return Sym.getVA(A); + case R_RISCV_ADD: + return sym.getVA(a); case R_ADDEND: - return A; + return a; case R_ARM_SBREL: - return Sym.getVA(A) - getARMStaticBase(Sym); + return sym.getVA(a) - getARMStaticBase(sym); case R_GOT: - case R_GOT_PLT: case R_RELAX_TLS_GD_TO_IE_ABS: - return Sym.getGotVA() + A; + return sym.getGotVA() + a; case R_GOTONLY_PC: - return In.Got->getVA() + A - P; - case R_GOTONLY_PC_FROM_END: - return In.Got->getVA() + A - P + In.Got->getSize(); + return in.got->getVA() + a - p; + case R_GOTPLTONLY_PC: + return in.gotPlt->getVA() + a - p; case R_GOTREL: - return Sym.getVA(A) - In.Got->getVA(); - case R_GOTREL_FROM_END: - return Sym.getVA(A) - In.Got->getVA() - In.Got->getSize(); - case R_GOT_FROM_END: - case R_RELAX_TLS_GD_TO_IE_END: - return Sym.getGotOffset() + A - In.Got->getSize(); + case R_PPC64_RELAX_TOC: + return sym.getVA(a) - in.got->getVA(); + case R_GOTPLTREL: + return sym.getVA(a) - in.gotPlt->getVA(); + case R_GOTPLT: + case R_RELAX_TLS_GD_TO_IE_GOTPLT: + return sym.getGotVA() + a - in.gotPlt->getVA(); case R_TLSLD_GOT_OFF: case R_GOT_OFF: case R_RELAX_TLS_GD_TO_IE_GOT_OFF: - return Sym.getGotOffset() + A; + return sym.getGotOffset() + a; case R_AARCH64_GOT_PAGE_PC: - case R_AARCH64_GOT_PAGE_PC_PLT: case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC: - return getAArch64Page(Sym.getGotVA() + A) - getAArch64Page(P); + return getAArch64Page(sym.getGotVA() + a) - getAArch64Page(p); case R_GOT_PC: case R_RELAX_TLS_GD_TO_IE: - return Sym.getGotVA() + A - P; + return sym.getGotVA() + a - p; case R_HEXAGON_GOT: - return Sym.getGotVA() - In.GotPlt->getVA(); + return sym.getGotVA() - in.gotPlt->getVA(); case R_MIPS_GOTREL: - return Sym.getVA(A) - In.MipsGot->getGp(File); + return sym.getVA(a) - in.mipsGot->getGp(file); case R_MIPS_GOT_GP: - return In.MipsGot->getGp(File) + A; + return in.mipsGot->getGp(file) + a; case R_MIPS_GOT_GP_PC: { // R_MIPS_LO16 expression has R_MIPS_GOT_GP_PC type iif the target // is _gp_disp symbol. In that case we should use the following @@ -651,73 +685,76 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, // microMIPS variants of these relocations use slightly different // expressions: AHL + GP - P + 3 for %lo() and AHL + GP - P - 1 for %hi() // to correctly handle less-sugnificant bit of the microMIPS symbol. - uint64_t V = In.MipsGot->getGp(File) + A - P; - if (Type == R_MIPS_LO16 || Type == R_MICROMIPS_LO16) - V += 4; - if (Type == R_MICROMIPS_LO16 || Type == R_MICROMIPS_HI16) - V -= 1; - return V; + uint64_t v = in.mipsGot->getGp(file) + a - p; + if (type == R_MIPS_LO16 || type == R_MICROMIPS_LO16) + v += 4; + if (type == R_MICROMIPS_LO16 || type == R_MICROMIPS_HI16) + v -= 1; + return v; } case R_MIPS_GOT_LOCAL_PAGE: // If relocation against MIPS local symbol requires GOT entry, this entry // should be initialized by 'page address'. This address is high 16-bits // of sum the symbol's value and the addend. - return In.MipsGot->getVA() + In.MipsGot->getPageEntryOffset(File, Sym, A) - - In.MipsGot->getGp(File); + return in.mipsGot->getVA() + in.mipsGot->getPageEntryOffset(file, sym, a) - + in.mipsGot->getGp(file); case R_MIPS_GOT_OFF: case R_MIPS_GOT_OFF32: // In case of MIPS if a GOT relocation has non-zero addend this addend // should be applied to the GOT entry content not to the GOT entry offset. // That is why we use separate expression type. - return In.MipsGot->getVA() + In.MipsGot->getSymEntryOffset(File, Sym, A) - - In.MipsGot->getGp(File); + return in.mipsGot->getVA() + in.mipsGot->getSymEntryOffset(file, sym, a) - + in.mipsGot->getGp(file); case R_MIPS_TLSGD: - return In.MipsGot->getVA() + In.MipsGot->getGlobalDynOffset(File, Sym) - - In.MipsGot->getGp(File); + return in.mipsGot->getVA() + in.mipsGot->getGlobalDynOffset(file, sym) - + in.mipsGot->getGp(file); case R_MIPS_TLSLD: - return In.MipsGot->getVA() + In.MipsGot->getTlsIndexOffset(File) - - In.MipsGot->getGp(File); + return in.mipsGot->getVA() + in.mipsGot->getTlsIndexOffset(file) - + in.mipsGot->getGp(file); case R_AARCH64_PAGE_PC: { - uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getVA(A); - return getAArch64Page(Val) - getAArch64Page(P); - } - case R_AARCH64_PLT_PAGE_PC: { - uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getPltVA() + A; - return getAArch64Page(Val) - getAArch64Page(P); + uint64_t val = sym.isUndefWeak() ? p + a : sym.getVA(a); + return getAArch64Page(val) - getAArch64Page(p); } case R_RISCV_PC_INDIRECT: { - if (const Relocation *HiRel = getRISCVPCRelHi20(&Sym, A)) - return getRelocTargetVA(File, HiRel->Type, HiRel->Addend, Sym.getVA(), - *HiRel->Sym, HiRel->Expr); + if (const Relocation *hiRel = getRISCVPCRelHi20(&sym, a)) + return getRelocTargetVA(file, hiRel->type, hiRel->addend, sym.getVA(), + *hiRel->sym, hiRel->expr); return 0; } case R_PC: { - uint64_t Dest; - if (Sym.isUndefWeak()) { + uint64_t dest; + if (sym.isUndefWeak()) { // On ARM and AArch64 a branch to an undefined weak resolves to the // next instruction, otherwise the place. - if (Config->EMachine == EM_ARM) - Dest = getARMUndefinedRelativeWeakVA(Type, A, P); - else if (Config->EMachine == EM_AARCH64) - Dest = getAArch64UndefinedRelativeWeakVA(Type, A, P); + if (config->emachine == EM_ARM) + dest = getARMUndefinedRelativeWeakVA(type, a, p); + else if (config->emachine == EM_AARCH64) + dest = getAArch64UndefinedRelativeWeakVA(type, a, p); + else if (config->emachine == EM_PPC) + dest = p; else - Dest = Sym.getVA(A); + dest = sym.getVA(a); } else { - Dest = Sym.getVA(A); + dest = sym.getVA(a); } - return Dest - P; + return dest - p; } case R_PLT: - return Sym.getPltVA() + A; + return sym.getPltVA() + a; case R_PLT_PC: - case R_PPC_CALL_PLT: - return Sym.getPltVA() + A - P; - case R_PPC_CALL: { - uint64_t SymVA = Sym.getVA(A); + case R_PPC64_CALL_PLT: + return sym.getPltVA() + a - p; + case R_PPC32_PLTREL: + // R_PPC_PLTREL24 uses the addend (usually 0 or 0x8000) to indicate r30 + // stores _GLOBAL_OFFSET_TABLE_ or .got2+0x8000. The addend is ignored for + // target VA compuation. + return sym.getPltVA() - p; + case R_PPC64_CALL: { + uint64_t symVA = sym.getVA(a); // If we have an undefined weak symbol, we might get here with a symbol // address of zero. That could overflow, but the code must be unreachable, // so don't bother doing anything at all. - if (!SymVA) + if (!symVA) return 0; // PPC64 V2 ABI describes two entry points to a function. The global entry @@ -726,46 +763,49 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, // the callee. For local calls the caller and callee share the same // TOC base and so the TOC pointer initialization code should be skipped by // branching to the local entry point. - return SymVA - P + getPPC64GlobalEntryToLocalEntryOffset(Sym.StOther); + return symVA - p + getPPC64GlobalEntryToLocalEntryOffset(sym.stOther); } - case R_PPC_TOC: - return getPPC64TocBase() + A; + case R_PPC64_TOCBASE: + return getPPC64TocBase() + a; case R_RELAX_GOT_PC: - return Sym.getVA(A) - P; + return sym.getVA(a) - p; case R_RELAX_TLS_GD_TO_LE: case R_RELAX_TLS_IE_TO_LE: case R_RELAX_TLS_LD_TO_LE: case R_TLS: - // A weak undefined TLS symbol resolves to the base of the TLS - // block, i.e. gets a value of zero. If we pass --gc-sections to - // lld and .tbss is not referenced, it gets reclaimed and we don't - // create a TLS program header. Therefore, we resolve this - // statically to zero. - if (Sym.isTls() && Sym.isUndefWeak()) - return 0; - return Sym.getVA(A) + getTlsTpOffset(); + // It is not very clear what to return if the symbol is undefined. With + // --noinhibit-exec, even a non-weak undefined reference may reach here. + // Just return A, which matches R_ABS, and the behavior of some dynamic + // loaders. + if (sym.isUndefined()) + return a; + return getTlsTpOffset(sym) + a; case R_RELAX_TLS_GD_TO_LE_NEG: case R_NEG_TLS: - return Out::TlsPhdr->p_memsz - Sym.getVA(A); + if (sym.isUndefined()) + return a; + return -getTlsTpOffset(sym) + a; case R_SIZE: - return Sym.getSize() + A; + return sym.getSize() + a; case R_TLSDESC: - return In.Got->getGlobalDynAddr(Sym) + A; + return in.got->getGlobalDynAddr(sym) + a; + case R_TLSDESC_PC: + return in.got->getGlobalDynAddr(sym) + a - p; case R_AARCH64_TLSDESC_PAGE: - return getAArch64Page(In.Got->getGlobalDynAddr(Sym) + A) - - getAArch64Page(P); + return getAArch64Page(in.got->getGlobalDynAddr(sym) + a) - + getAArch64Page(p); case R_TLSGD_GOT: - return In.Got->getGlobalDynOffset(Sym) + A; - case R_TLSGD_GOT_FROM_END: - return In.Got->getGlobalDynOffset(Sym) + A - In.Got->getSize(); + return in.got->getGlobalDynOffset(sym) + a; + case R_TLSGD_GOTPLT: + return in.got->getVA() + in.got->getGlobalDynOffset(sym) + a - in.gotPlt->getVA(); case R_TLSGD_PC: - return In.Got->getGlobalDynAddr(Sym) + A - P; - case R_TLSLD_GOT_FROM_END: - return In.Got->getTlsIndexOff() + A - In.Got->getSize(); + return in.got->getGlobalDynAddr(sym) + a - p; + case R_TLSLD_GOTPLT: + return in.got->getVA() + in.got->getTlsIndexOff() + a - in.gotPlt->getVA(); case R_TLSLD_GOT: - return In.Got->getTlsIndexOff() + A; + return in.got->getTlsIndexOff() + a; case R_TLSLD_PC: - return In.Got->getTlsIndexVA() + A - P; + return in.got->getTlsIndexVA() + a - p; default: llvm_unreachable("invalid expression"); } @@ -779,36 +819,36 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, // So, we handle relocations for non-alloc sections directly in this // function as a performance optimization. template -void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef Rels) { - const unsigned Bits = sizeof(typename ELFT::uint) * 8; +void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef rels) { + const unsigned bits = sizeof(typename ELFT::uint) * 8; - for (const RelTy &Rel : Rels) { - RelType Type = Rel.getType(Config->IsMips64EL); + for (const RelTy &rel : rels) { + RelType type = rel.getType(config->isMips64EL); // GCC 8.0 or earlier have a bug that they emit R_386_GOTPC relocations // against _GLOBAL_OFFSET_TABLE_ for .debug_info. The bug has been fixed // in 2017 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82630), but we // need to keep this bug-compatible code for a while. - if (Config->EMachine == EM_386 && Type == R_386_GOTPC) + if (config->emachine == EM_386 && type == R_386_GOTPC) continue; - uint64_t Offset = getOffset(Rel.r_offset); - uint8_t *BufLoc = Buf + Offset; - int64_t Addend = getAddend(Rel); + uint64_t offset = getOffset(rel.r_offset); + uint8_t *bufLoc = buf + offset; + int64_t addend = getAddend(rel); if (!RelTy::IsRela) - Addend += Target->getImplicitAddend(BufLoc, Type); + addend += target->getImplicitAddend(bufLoc, type); - Symbol &Sym = getFile()->getRelocTargetSym(Rel); - RelExpr Expr = Target->getRelExpr(Type, Sym, BufLoc); - if (Expr == R_NONE) + Symbol &sym = getFile()->getRelocTargetSym(rel); + RelExpr expr = target->getRelExpr(type, sym, bufLoc); + if (expr == R_NONE) continue; - if (Expr != R_ABS) { - std::string Msg = getLocation(Offset) + - ": has non-ABS relocation " + toString(Type) + - " against symbol '" + toString(Sym) + "'"; - if (Expr != R_PC) { - error(Msg); + if (expr != R_ABS && expr != R_DTPREL && expr != R_RISCV_ADD) { + std::string msg = getLocation(offset) + + ": has non-ABS relocation " + toString(type) + + " against symbol '" + toString(sym) + "'"; + if (expr != R_PC) { + error(msg); return; } @@ -819,16 +859,16 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef Rels) { // relocations without any errors and relocate them as if they were at // address 0. For bug-compatibilty, we accept them with warnings. We // know Steel Bank Common Lisp as of 2018 have this bug. - warn(Msg); - Target->relocateOne(BufLoc, Type, - SignExtend64(Sym.getVA(Addend - Offset))); + warn(msg); + target->relocateOne(bufLoc, type, + SignExtend64(sym.getVA(addend - offset))); continue; } - if (Sym.isTls() && !Out::TlsPhdr) - Target->relocateOne(BufLoc, Type, 0); + if (sym.isTls() && !Out::tlsPhdr) + target->relocateOne(bufLoc, type, 0); else - Target->relocateOne(BufLoc, Type, SignExtend64(Sym.getVA(Addend))); + target->relocateOne(bufLoc, type, SignExtend64(sym.getVA(addend))); } } @@ -837,96 +877,100 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef Rels) { // relocations aimed to update addends. They are handled in relocateAlloc() // for allocatable sections, and this function does the same for // non-allocatable sections, such as sections with debug information. -static void relocateNonAllocForRelocatable(InputSection *Sec, uint8_t *Buf) { - const unsigned Bits = Config->Is64 ? 64 : 32; +static void relocateNonAllocForRelocatable(InputSection *sec, uint8_t *buf) { + const unsigned bits = config->is64 ? 64 : 32; - for (const Relocation &Rel : Sec->Relocations) { + for (const Relocation &rel : sec->relocations) { // InputSection::copyRelocations() adds only R_ABS relocations. - assert(Rel.Expr == R_ABS); - uint8_t *BufLoc = Buf + Rel.Offset + Sec->OutSecOff; - uint64_t TargetVA = SignExtend64(Rel.Sym->getVA(Rel.Addend), Bits); - Target->relocateOne(BufLoc, Rel.Type, TargetVA); + assert(rel.expr == R_ABS); + uint8_t *bufLoc = buf + rel.offset + sec->outSecOff; + uint64_t targetVA = SignExtend64(rel.sym->getVA(rel.addend), bits); + target->relocateOne(bufLoc, rel.type, targetVA); } } template -void InputSectionBase::relocate(uint8_t *Buf, uint8_t *BufEnd) { - if (Flags & SHF_EXECINSTR) - adjustSplitStackFunctionPrologues(Buf, BufEnd); +void InputSectionBase::relocate(uint8_t *buf, uint8_t *bufEnd) { + if (flags & SHF_EXECINSTR) + adjustSplitStackFunctionPrologues(buf, bufEnd); - if (Flags & SHF_ALLOC) { - relocateAlloc(Buf, BufEnd); + if (flags & SHF_ALLOC) { + relocateAlloc(buf, bufEnd); return; } - auto *Sec = cast(this); - if (Config->Relocatable) - relocateNonAllocForRelocatable(Sec, Buf); - else if (Sec->AreRelocsRela) - Sec->relocateNonAlloc(Buf, Sec->template relas()); + auto *sec = cast(this); + if (config->relocatable) + relocateNonAllocForRelocatable(sec, buf); + else if (sec->areRelocsRela) + sec->relocateNonAlloc(buf, sec->template relas()); else - Sec->relocateNonAlloc(Buf, Sec->template rels()); + sec->relocateNonAlloc(buf, sec->template rels()); } -void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) { - assert(Flags & SHF_ALLOC); - const unsigned Bits = Config->Wordsize * 8; +void InputSectionBase::relocateAlloc(uint8_t *buf, uint8_t *bufEnd) { + assert(flags & SHF_ALLOC); + const unsigned bits = config->wordsize * 8; - for (const Relocation &Rel : Relocations) { - uint64_t Offset = Rel.Offset; - if (auto *Sec = dyn_cast(this)) - Offset += Sec->OutSecOff; - uint8_t *BufLoc = Buf + Offset; - RelType Type = Rel.Type; + for (const Relocation &rel : relocations) { + uint64_t offset = rel.offset; + if (auto *sec = dyn_cast(this)) + offset += sec->outSecOff; + uint8_t *bufLoc = buf + offset; + RelType type = rel.type; - uint64_t AddrLoc = getOutputSection()->Addr + Offset; - RelExpr Expr = Rel.Expr; - uint64_t TargetVA = SignExtend64( - getRelocTargetVA(File, Type, Rel.Addend, AddrLoc, *Rel.Sym, Expr), - Bits); + uint64_t addrLoc = getOutputSection()->addr + offset; + RelExpr expr = rel.expr; + uint64_t targetVA = SignExtend64( + getRelocTargetVA(file, type, rel.addend, addrLoc, *rel.sym, expr), + bits); - switch (Expr) { + switch (expr) { case R_RELAX_GOT_PC: case R_RELAX_GOT_PC_NOPIC: - Target->relaxGot(BufLoc, TargetVA); + target->relaxGot(bufLoc, type, targetVA); + break; + case R_PPC64_RELAX_TOC: + if (!tryRelaxPPC64TocIndirection(type, rel, bufLoc)) + target->relocateOne(bufLoc, type, targetVA); break; case R_RELAX_TLS_IE_TO_LE: - Target->relaxTlsIeToLe(BufLoc, Type, TargetVA); + target->relaxTlsIeToLe(bufLoc, type, targetVA); break; case R_RELAX_TLS_LD_TO_LE: case R_RELAX_TLS_LD_TO_LE_ABS: - Target->relaxTlsLdToLe(BufLoc, Type, TargetVA); + target->relaxTlsLdToLe(bufLoc, type, targetVA); break; case R_RELAX_TLS_GD_TO_LE: case R_RELAX_TLS_GD_TO_LE_NEG: - Target->relaxTlsGdToLe(BufLoc, Type, TargetVA); + target->relaxTlsGdToLe(bufLoc, type, targetVA); break; case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC: case R_RELAX_TLS_GD_TO_IE: case R_RELAX_TLS_GD_TO_IE_ABS: case R_RELAX_TLS_GD_TO_IE_GOT_OFF: - case R_RELAX_TLS_GD_TO_IE_END: - Target->relaxTlsGdToIe(BufLoc, Type, TargetVA); + case R_RELAX_TLS_GD_TO_IE_GOTPLT: + target->relaxTlsGdToIe(bufLoc, type, targetVA); break; - case R_PPC_CALL: + case R_PPC64_CALL: // If this is a call to __tls_get_addr, it may be part of a TLS // sequence that has been relaxed and turned into a nop. In this // case, we don't want to handle it as a call. - if (read32(BufLoc) == 0x60000000) // nop + if (read32(bufLoc) == 0x60000000) // nop break; // Patch a nop (0x60000000) to a ld. - if (Rel.Sym->NeedsTocRestore) { - if (BufLoc + 8 > BufEnd || read32(BufLoc + 4) != 0x60000000) { - error(getErrorLocation(BufLoc) + "call lacks nop, can't restore toc"); + if (rel.sym->needsTocRestore) { + if (bufLoc + 8 > bufEnd || read32(bufLoc + 4) != 0x60000000) { + error(getErrorLocation(bufLoc) + "call lacks nop, can't restore toc"); break; } - write32(BufLoc + 4, 0xe8410018); // ld %r2, 24(%r1) + write32(bufLoc + 4, 0xe8410018); // ld %r2, 24(%r1) } - Target->relocateOne(BufLoc, Type, TargetVA); + target->relocateOne(bufLoc, type, targetVA); break; default: - Target->relocateOne(BufLoc, Type, TargetVA); + target->relocateOne(bufLoc, type, targetVA); break; } } @@ -935,44 +979,44 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) { // For each function-defining prologue, find any calls to __morestack, // and replace them with calls to __morestack_non_split. static void switchMorestackCallsToMorestackNonSplit( - DenseSet &Prologues, std::vector &MorestackCalls) { + DenseSet &prologues, std::vector &morestackCalls) { // If the target adjusted a function's prologue, all calls to // __morestack inside that function should be switched to // __morestack_non_split. - Symbol *MoreStackNonSplit = Symtab->find("__morestack_non_split"); - if (!MoreStackNonSplit) { + Symbol *moreStackNonSplit = symtab->find("__morestack_non_split"); + if (!moreStackNonSplit) { error("Mixing split-stack objects requires a definition of " "__morestack_non_split"); return; } // Sort both collections to compare addresses efficiently. - llvm::sort(MorestackCalls, [](const Relocation *L, const Relocation *R) { - return L->Offset < R->Offset; + llvm::sort(morestackCalls, [](const Relocation *l, const Relocation *r) { + return l->offset < r->offset; }); - std::vector Functions(Prologues.begin(), Prologues.end()); - llvm::sort(Functions, [](const Defined *L, const Defined *R) { - return L->Value < R->Value; + std::vector functions(prologues.begin(), prologues.end()); + llvm::sort(functions, [](const Defined *l, const Defined *r) { + return l->value < r->value; }); - auto It = MorestackCalls.begin(); - for (Defined *F : Functions) { + auto it = morestackCalls.begin(); + for (Defined *f : functions) { // Find the first call to __morestack within the function. - while (It != MorestackCalls.end() && (*It)->Offset < F->Value) - ++It; + while (it != morestackCalls.end() && (*it)->offset < f->value) + ++it; // Adjust all calls inside the function. - while (It != MorestackCalls.end() && (*It)->Offset < F->Value + F->Size) { - (*It)->Sym = MoreStackNonSplit; - ++It; + while (it != morestackCalls.end() && (*it)->offset < f->value + f->size) { + (*it)->sym = moreStackNonSplit; + ++it; } } } -static bool enclosingPrologueAttempted(uint64_t Offset, - const DenseSet &Prologues) { - for (Defined *F : Prologues) - if (F->Value <= Offset && Offset < F->Value + F->Size) +static bool enclosingPrologueAttempted(uint64_t offset, + const DenseSet &prologues) { + for (Defined *f : prologues) + if (f->value <= offset && offset < f->value + f->size) return true; return false; } @@ -982,30 +1026,30 @@ static bool enclosingPrologueAttempted(uint64_t Offset, // adjusted to ensure that the called function will have enough stack // available. Find those functions, and adjust their prologues. template -void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf, - uint8_t *End) { - if (!getFile()->SplitStack) +void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *buf, + uint8_t *end) { + if (!getFile()->splitStack) return; - DenseSet Prologues; - std::vector MorestackCalls; + DenseSet prologues; + std::vector morestackCalls; - for (Relocation &Rel : Relocations) { + for (Relocation &rel : relocations) { // Local symbols can't possibly be cross-calls, and should have been // resolved long before this line. - if (Rel.Sym->isLocal()) + if (rel.sym->isLocal()) continue; // Ignore calls into the split-stack api. - if (Rel.Sym->getName().startswith("__morestack")) { - if (Rel.Sym->getName().equals("__morestack")) - MorestackCalls.push_back(&Rel); + if (rel.sym->getName().startswith("__morestack")) { + if (rel.sym->getName().equals("__morestack")) + morestackCalls.push_back(&rel); continue; } // A relocation to non-function isn't relevant. Sometimes // __morestack is not marked as a function, so this check comes // after the name check. - if (Rel.Sym->Type != STT_FUNC) + if (rel.sym->type != STT_FUNC) continue; // If the callee's-file was compiled with split stack, nothing to do. In @@ -1013,106 +1057,117 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf, // being produced". So an "undefined" symbol might be provided by a shared // library. It is not possible to tell how such symbols were compiled, so be // conservative. - if (Defined *D = dyn_cast(Rel.Sym)) - if (InputSection *IS = cast_or_null(D->Section)) - if (!IS || !IS->getFile() || IS->getFile()->SplitStack) + if (Defined *d = dyn_cast(rel.sym)) + if (InputSection *isec = cast_or_null(d->section)) + if (!isec || !isec->getFile() || isec->getFile()->splitStack) continue; - if (enclosingPrologueAttempted(Rel.Offset, Prologues)) + if (enclosingPrologueAttempted(rel.offset, prologues)) continue; - if (Defined *F = getEnclosingFunction(Rel.Offset)) { - Prologues.insert(F); - if (Target->adjustPrologueForCrossSplitStack(Buf + getOffset(F->Value), - End, F->StOther)) + if (Defined *f = getEnclosingFunction(rel.offset)) { + prologues.insert(f); + if (target->adjustPrologueForCrossSplitStack(buf + getOffset(f->value), + end, f->stOther)) continue; - if (!getFile()->SomeNoSplitStack) - error(lld::toString(this) + ": " + F->getName() + - " (with -fsplit-stack) calls " + Rel.Sym->getName() + + if (!getFile()->someNoSplitStack) + error(lld::toString(this) + ": " + f->getName() + + " (with -fsplit-stack) calls " + rel.sym->getName() + " (without -fsplit-stack), but couldn't adjust its prologue"); } } - if (Target->NeedsMoreStackNonSplit) - switchMorestackCallsToMorestackNonSplit(Prologues, MorestackCalls); + if (target->needsMoreStackNonSplit) + switchMorestackCallsToMorestackNonSplit(prologues, morestackCalls); } -template void InputSection::writeTo(uint8_t *Buf) { - if (Type == SHT_NOBITS) +template void InputSection::writeTo(uint8_t *buf) { + if (type == SHT_NOBITS) return; - if (auto *S = dyn_cast(this)) { - S->writeTo(Buf + OutSecOff); + if (auto *s = dyn_cast(this)) { + s->writeTo(buf + outSecOff); return; } // If -r or --emit-relocs is given, then an InputSection // may be a relocation section. - if (Type == SHT_RELA) { - copyRelocations(Buf + OutSecOff, getDataAs()); + if (type == SHT_RELA) { + copyRelocations(buf + outSecOff, getDataAs()); return; } - if (Type == SHT_REL) { - copyRelocations(Buf + OutSecOff, getDataAs()); + if (type == SHT_REL) { + copyRelocations(buf + outSecOff, getDataAs()); return; } // If -r is given, we may have a SHT_GROUP section. - if (Type == SHT_GROUP) { - copyShtGroup(Buf + OutSecOff); + if (type == SHT_GROUP) { + copyShtGroup(buf + outSecOff); return; } // If this is a compressed section, uncompress section contents directly // to the buffer. - if (UncompressedSize >= 0 && !UncompressedBuf) { - size_t Size = UncompressedSize; - if (Error E = zlib::uncompress(toStringRef(RawData), - (char *)(Buf + OutSecOff), Size)) + if (uncompressedSize >= 0) { + size_t size = uncompressedSize; + if (Error e = zlib::uncompress(toStringRef(rawData), + (char *)(buf + outSecOff), size)) fatal(toString(this) + - ": uncompress failed: " + llvm::toString(std::move(E))); - uint8_t *BufEnd = Buf + OutSecOff + Size; - relocate(Buf, BufEnd); + ": uncompress failed: " + llvm::toString(std::move(e))); + uint8_t *bufEnd = buf + outSecOff + size; + relocate(buf, bufEnd); return; } // Copy section contents from source object file to output file // and then apply relocations. - memcpy(Buf + OutSecOff, data().data(), data().size()); - uint8_t *BufEnd = Buf + OutSecOff + data().size(); - relocate(Buf, BufEnd); + memcpy(buf + outSecOff, data().data(), data().size()); + uint8_t *bufEnd = buf + outSecOff + data().size(); + relocate(buf, bufEnd); } -void InputSection::replace(InputSection *Other) { - Alignment = std::max(Alignment, Other->Alignment); - Other->Repl = Repl; - Other->Live = false; +void InputSection::replace(InputSection *other) { + alignment = std::max(alignment, other->alignment); + + // When a section is replaced with another section that was allocated to + // another partition, the replacement section (and its associated sections) + // need to be placed in the main partition so that both partitions will be + // able to access it. + if (partition != other->partition) { + partition = 1; + for (InputSection *isec : dependentSections) + isec->partition = 1; + } + + other->repl = repl; + other->markDead(); } template -EhInputSection::EhInputSection(ObjFile &F, - const typename ELFT::Shdr &Header, - StringRef Name) - : InputSectionBase(F, Header, Name, InputSectionBase::EHFrame) {} +EhInputSection::EhInputSection(ObjFile &f, + const typename ELFT::Shdr &header, + StringRef name) + : InputSectionBase(f, header, name, InputSectionBase::EHFrame) {} SyntheticSection *EhInputSection::getParent() const { - return cast_or_null(Parent); + return cast_or_null(parent); } // Returns the index of the first relocation that points to a region between // Begin and Begin+Size. template -static unsigned getReloc(IntTy Begin, IntTy Size, const ArrayRef &Rels, - unsigned &RelocI) { +static unsigned getReloc(IntTy begin, IntTy size, const ArrayRef &rels, + unsigned &relocI) { // Start search from RelocI for fast access. That works because the // relocations are sorted in .eh_frame. - for (unsigned N = Rels.size(); RelocI < N; ++RelocI) { - const RelTy &Rel = Rels[RelocI]; - if (Rel.r_offset < Begin) + for (unsigned n = rels.size(); relocI < n; ++relocI) { + const RelTy &rel = rels[relocI]; + if (rel.r_offset < begin) continue; - if (Rel.r_offset < Begin + Size) - return RelocI; + if (rel.r_offset < begin + size) + return relocI; return -1; } return -1; @@ -1121,84 +1176,84 @@ static unsigned getReloc(IntTy Begin, IntTy Size, const ArrayRef &Rels, // .eh_frame is a sequence of CIE or FDE records. // This function splits an input section into records and returns them. template void EhInputSection::split() { - if (AreRelocsRela) + if (areRelocsRela) split(relas()); else split(rels()); } template -void EhInputSection::split(ArrayRef Rels) { - unsigned RelI = 0; - for (size_t Off = 0, End = data().size(); Off != End;) { - size_t Size = readEhRecordSize(this, Off); - Pieces.emplace_back(Off, this, Size, getReloc(Off, Size, Rels, RelI)); +void EhInputSection::split(ArrayRef rels) { + unsigned relI = 0; + for (size_t off = 0, end = data().size(); off != end;) { + size_t size = readEhRecordSize(this, off); + pieces.emplace_back(off, this, size, getReloc(off, size, rels, relI)); // The empty record is the end marker. - if (Size == 4) + if (size == 4) break; - Off += Size; + off += size; } } -static size_t findNull(StringRef S, size_t EntSize) { +static size_t findNull(StringRef s, size_t entSize) { // Optimize the common case. - if (EntSize == 1) - return S.find(0); + if (entSize == 1) + return s.find(0); - for (unsigned I = 0, N = S.size(); I != N; I += EntSize) { - const char *B = S.begin() + I; - if (std::all_of(B, B + EntSize, [](char C) { return C == 0; })) - return I; + for (unsigned i = 0, n = s.size(); i != n; i += entSize) { + const char *b = s.begin() + i; + if (std::all_of(b, b + entSize, [](char c) { return c == 0; })) + return i; } return StringRef::npos; } SyntheticSection *MergeInputSection::getParent() const { - return cast_or_null(Parent); + return cast_or_null(parent); } // Split SHF_STRINGS section. Such section is a sequence of // null-terminated strings. -void MergeInputSection::splitStrings(ArrayRef Data, size_t EntSize) { - size_t Off = 0; - bool IsAlloc = Flags & SHF_ALLOC; - StringRef S = toStringRef(Data); - - while (!S.empty()) { - size_t End = findNull(S, EntSize); - if (End == StringRef::npos) +void MergeInputSection::splitStrings(ArrayRef data, size_t entSize) { + size_t off = 0; + bool isAlloc = flags & SHF_ALLOC; + StringRef s = toStringRef(data); + + while (!s.empty()) { + size_t end = findNull(s, entSize); + if (end == StringRef::npos) fatal(toString(this) + ": string is not null terminated"); - size_t Size = End + EntSize; + size_t size = end + entSize; - Pieces.emplace_back(Off, xxHash64(S.substr(0, Size)), !IsAlloc); - S = S.substr(Size); - Off += Size; + pieces.emplace_back(off, xxHash64(s.substr(0, size)), !isAlloc); + s = s.substr(size); + off += size; } } // Split non-SHF_STRINGS section. Such section is a sequence of // fixed size records. -void MergeInputSection::splitNonStrings(ArrayRef Data, - size_t EntSize) { - size_t Size = Data.size(); - assert((Size % EntSize) == 0); - bool IsAlloc = Flags & SHF_ALLOC; - - for (size_t I = 0; I != Size; I += EntSize) - Pieces.emplace_back(I, xxHash64(Data.slice(I, EntSize)), !IsAlloc); +void MergeInputSection::splitNonStrings(ArrayRef data, + size_t entSize) { + size_t size = data.size(); + assert((size % entSize) == 0); + bool isAlloc = flags & SHF_ALLOC; + + for (size_t i = 0; i != size; i += entSize) + pieces.emplace_back(i, xxHash64(data.slice(i, entSize)), !isAlloc); } template -MergeInputSection::MergeInputSection(ObjFile &F, - const typename ELFT::Shdr &Header, - StringRef Name) - : InputSectionBase(F, Header, Name, InputSectionBase::Merge) {} +MergeInputSection::MergeInputSection(ObjFile &f, + const typename ELFT::Shdr &header, + StringRef name) + : InputSectionBase(f, header, name, InputSectionBase::Merge) {} -MergeInputSection::MergeInputSection(uint64_t Flags, uint32_t Type, - uint64_t Entsize, ArrayRef Data, - StringRef Name) - : InputSectionBase(nullptr, Flags, Type, Entsize, /*Link*/ 0, /*Info*/ 0, - /*Alignment*/ Entsize, Data, Name, SectionBase::Merge) {} +MergeInputSection::MergeInputSection(uint64_t flags, uint32_t type, + uint64_t entsize, ArrayRef data, + StringRef name) + : InputSectionBase(nullptr, flags, type, entsize, /*Link*/ 0, /*Info*/ 0, + /*Alignment*/ entsize, data, name, SectionBase::Merge) {} // This function is called after we obtain a complete list of input sections // that need to be linked. This is responsible to split section contents @@ -1207,37 +1262,35 @@ MergeInputSection::MergeInputSection(uint64_t Flags, uint32_t Type, // Note that this function is called from parallelForEach. This must be // thread-safe (i.e. no memory allocation from the pools). void MergeInputSection::splitIntoPieces() { - assert(Pieces.empty()); + assert(pieces.empty()); - if (Flags & SHF_STRINGS) - splitStrings(data(), Entsize); + if (flags & SHF_STRINGS) + splitStrings(data(), entsize); else - splitNonStrings(data(), Entsize); + splitNonStrings(data(), entsize); } -SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) { - if (this->data().size() <= Offset) +SectionPiece *MergeInputSection::getSectionPiece(uint64_t offset) { + if (this->data().size() <= offset) fatal(toString(this) + ": offset is outside the section"); // If Offset is not at beginning of a section piece, it is not in the map. // In that case we need to do a binary search of the original section piece vector. - auto It2 = - llvm::upper_bound(Pieces, Offset, [](uint64_t Offset, SectionPiece P) { - return Offset < P.InputOff; - }); - return &It2[-1]; + auto it = partition_point( + pieces, [=](SectionPiece p) { return p.inputOff <= offset; }); + return &it[-1]; } // Returns the offset in an output section for a given input offset. // Because contents of a mergeable section is not contiguous in output, // it is not just an addition to a base output offset. -uint64_t MergeInputSection::getParentOffset(uint64_t Offset) const { +uint64_t MergeInputSection::getParentOffset(uint64_t offset) const { // If Offset is not at beginning of a section piece, it is not in the map. // In that case we need to search from the original section piece vector. - const SectionPiece &Piece = - *(const_cast(this)->getSectionPiece (Offset)); - uint64_t Addend = Offset - Piece.InputOff; - return Piece.OutputOff + Addend; + const SectionPiece &piece = + *(const_cast(this)->getSectionPiece (offset)); + uint64_t addend = offset - piece.inputOff; + return piece.outputOff + addend; } template InputSection::InputSection(ObjFile &, const ELF32LE::Shdr &, diff --git a/ELF/InputSection.h b/ELF/InputSection.h index 34f411e..3a97407 100644 --- a/ELF/InputSection.h +++ b/ELF/InputSection.h @@ -1,9 +1,8 @@ //===- InputSection.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 // //===----------------------------------------------------------------------===// @@ -26,11 +25,14 @@ class Symbol; struct SectionPiece; class Defined; +struct Partition; class SyntheticSection; class MergeSyntheticSection; template class ObjFile; class OutputSection; +extern std::vector partitions; + // This is the base class of all sections that lld handles. Some are sections in // input files, some are sections in the produced output file and some exist // just as a convenience for implementing special ways of combining some @@ -39,38 +41,53 @@ class SectionBase { public: enum Kind { Regular, EHFrame, Merge, Synthetic, Output }; - Kind kind() const { return (Kind)SectionKind; } + Kind kind() const { return (Kind)sectionKind; } - StringRef Name; + StringRef name; // This pointer points to the "real" instance of this instance. // Usually Repl == this. However, if ICF merges two sections, // Repl pointer of one section points to another section. So, // if you need to get a pointer to this instance, do not use // this but instead this->Repl. - SectionBase *Repl; + SectionBase *repl; - unsigned SectionKind : 3; + unsigned sectionKind : 3; - // The next two bit fields are only used by InputSectionBase, but we + // The next three bit fields are only used by InputSectionBase, but we // put them here so the struct packs better. - // The garbage collector sets sections' Live bits. - // If GC is disabled, all sections are considered live by default. - unsigned Live : 1; + // True if this section has already been placed to a linker script + // output section. This is needed because, in a linker script, you + // can refer to the same section more than once. For example, in + // the following linker script, + // + // .foo : { *(.text) } + // .bar : { *(.text) } + // + // .foo takes all .text sections, and .bar becomes empty. To achieve + // this, we need to memorize whether a section has been placed or + // not for each input section. + unsigned assigned : 1; - unsigned Bss : 1; + unsigned bss : 1; // Set for sections that should not be folded by ICF. - unsigned KeepUnique : 1; + unsigned keepUnique : 1; + + // The 1-indexed partition that this section is assigned to by the garbage + // collector, or 0 if this section is dead. Normally there is only one + // partition, so this will either be 0 or 1. + uint8_t partition; + elf::Partition &getPartition() const; // These corresponds to the fields in Elf_Shdr. - uint32_t Alignment; - uint64_t Flags; - uint64_t Entsize; - uint32_t Type; - uint32_t Link; - uint32_t Info; + uint32_t alignment; + uint64_t flags; + uint64_t entsize; + uint32_t type; + uint32_t link; + uint32_t info; OutputSection *getOutputSection(); const OutputSection *getOutputSection() const { @@ -79,90 +96,81 @@ public: // Translate an offset in the input section to an offset in the output // section. - uint64_t getOffset(uint64_t Offset) const; + uint64_t getOffset(uint64_t offset) const; - uint64_t getVA(uint64_t Offset = 0) const; + uint64_t getVA(uint64_t offset = 0) const; + + bool isLive() const { return partition != 0; } + void markLive() { partition = 1; } + void markDead() { partition = 0; } protected: - SectionBase(Kind SectionKind, StringRef Name, uint64_t Flags, - uint64_t Entsize, uint64_t Alignment, uint32_t Type, - uint32_t Info, uint32_t Link) - : Name(Name), Repl(this), SectionKind(SectionKind), Live(false), - Bss(false), KeepUnique(false), Alignment(Alignment), Flags(Flags), - Entsize(Entsize), Type(Type), Link(Link), Info(Info) {} + SectionBase(Kind sectionKind, StringRef name, uint64_t flags, + uint64_t entsize, uint64_t alignment, uint32_t type, + uint32_t info, uint32_t link) + : name(name), repl(this), sectionKind(sectionKind), assigned(false), + bss(false), keepUnique(false), partition(0), alignment(alignment), + flags(flags), entsize(entsize), type(type), link(link), info(info) {} }; // This corresponds to a section of an input file. class InputSectionBase : public SectionBase { public: template - InputSectionBase(ObjFile &File, const typename ELFT::Shdr &Header, - StringRef Name, Kind SectionKind); + InputSectionBase(ObjFile &file, const typename ELFT::Shdr &header, + StringRef name, Kind sectionKind); + + InputSectionBase(InputFile *file, uint64_t flags, uint32_t type, + uint64_t entsize, uint32_t link, uint32_t info, + uint32_t alignment, ArrayRef data, StringRef name, + Kind sectionKind); - InputSectionBase(InputFile *File, uint64_t Flags, uint32_t Type, - uint64_t Entsize, uint32_t Link, uint32_t Info, - uint32_t Alignment, ArrayRef Data, StringRef Name, - Kind SectionKind); + static bool classof(const SectionBase *s) { return s->kind() != Output; } - static bool classof(const SectionBase *S) { return S->kind() != Output; } + // Relocations that refer to this section. + unsigned numRelocations : 31; + unsigned areRelocsRela : 1; + const void *firstRelocation = nullptr; // The file which contains this section. Its dynamic type is always // ObjFile, but in order to avoid ELFT, we use InputFile as // its static type. - InputFile *File; + InputFile *file; template ObjFile *getFile() const { - return cast_or_null>(File); + return cast_or_null>(file); } ArrayRef data() const { - if (UncompressedSize >= 0 && !UncompressedBuf) + if (uncompressedSize >= 0) uncompress(); - return RawData; + return rawData; } uint64_t getOffsetInFile() const; - // True if this section has already been placed to a linker script - // output section. This is needed because, in a linker script, you - // can refer to the same section more than once. For example, in - // the following linker script, - // - // .foo : { *(.text) } - // .bar : { *(.text) } - // - // .foo takes all .text sections, and .bar becomes empty. To achieve - // this, we need to memorize whether a section has been placed or - // not for each input section. - bool Assigned = false; - // Input sections are part of an output section. Special sections // like .eh_frame and merge sections are first combined into a // synthetic section that is then added to an output section. In all // cases this points one level up. - SectionBase *Parent = nullptr; - - // Relocations that refer to this section. - const void *FirstRelocation = nullptr; - unsigned NumRelocations : 31; - unsigned AreRelocsRela : 1; + SectionBase *parent = nullptr; template ArrayRef rels() const { - assert(!AreRelocsRela); + assert(!areRelocsRela); return llvm::makeArrayRef( - static_cast(FirstRelocation), - NumRelocations); + static_cast(firstRelocation), + numRelocations); } template ArrayRef relas() const { - assert(AreRelocsRela); + assert(areRelocsRela); return llvm::makeArrayRef( - static_cast(FirstRelocation), - NumRelocations); + static_cast(firstRelocation), + numRelocations); } // InputSections that are dependent on us (reverse dependency for GC) - llvm::TinyPtrVector DependentSections; + llvm::TinyPtrVector dependentSections; // Returns the size of this section (even if this is a common or BSS.) size_t getSize() const; @@ -172,23 +180,23 @@ public: // Get the function symbol that encloses this offset from within the // section. template - Defined *getEnclosingFunction(uint64_t Offset); + Defined *getEnclosingFunction(uint64_t offset); // Returns a source location string. Used to construct an error message. - template std::string getLocation(uint64_t Offset); - std::string getSrcMsg(const Symbol &Sym, uint64_t Offset); - std::string getObjMsg(uint64_t Offset); + template std::string getLocation(uint64_t offset); + std::string getSrcMsg(const Symbol &sym, uint64_t offset); + std::string getObjMsg(uint64_t offset); // Each section knows how to relocate itself. These functions apply // relocations, assuming that Buf points to this section's copy in // the mmap'ed output buffer. - template void relocate(uint8_t *Buf, uint8_t *BufEnd); - void relocateAlloc(uint8_t *Buf, uint8_t *BufEnd); + template void relocate(uint8_t *buf, uint8_t *bufEnd); + void relocateAlloc(uint8_t *buf, uint8_t *bufEnd); // The native ELF reloc data type is not very convenient to handle. // So we convert ELF reloc records to our own records in Relocations.cpp. // This vector contains such "cooked" relocations. - std::vector Relocations; + std::vector relocations; // A function compiled with -fsplit-stack calling a function // compiled without -fsplit-stack needs its prologue adjusted. Find @@ -196,25 +204,26 @@ public: // to relocation. See https://gcc.gnu.org/wiki/SplitStacks for more // information. template - void adjustSplitStackFunctionPrologues(uint8_t *Buf, uint8_t *End); + void adjustSplitStackFunctionPrologues(uint8_t *buf, uint8_t *end); template llvm::ArrayRef getDataAs() const { - size_t S = data().size(); - assert(S % sizeof(T) == 0); - return llvm::makeArrayRef((const T *)data().data(), S / sizeof(T)); + size_t s = data().size(); + assert(s % sizeof(T) == 0); + return llvm::makeArrayRef((const T *)data().data(), s / sizeof(T)); } protected: void parseCompressedHeader(); void uncompress() const; - mutable ArrayRef RawData; + mutable ArrayRef rawData; - // A pointer that owns uncompressed data if a section is compressed by zlib. - // Since the feature is not used often, this is usually a nullptr. - mutable std::unique_ptr UncompressedBuf; - int64_t UncompressedSize = -1; + // This field stores the uncompressed size of the compressed data in rawData, + // or -1 if rawData is not compressed (either because the section wasn't + // compressed in the first place, or because we ended up uncompressing it). + // Since the feature is not used often, this is usually -1. + mutable int64_t uncompressedSize = -1; }; // SectionPiece represents a piece of splittable section contents. @@ -222,14 +231,13 @@ protected: // have to be as compact as possible, which is why we don't store the size (can // be found by looking at the next one). struct SectionPiece { - SectionPiece(size_t Off, uint32_t Hash, bool Live) - : InputOff(Off), Hash(Hash), OutputOff(0), - Live(Live || !Config->GcSections) {} - - uint32_t InputOff; - uint32_t Hash; - int64_t OutputOff : 63; - uint64_t Live : 1; + SectionPiece(size_t off, uint32_t hash, bool live) + : inputOff(off), live(live || !config->gcSections), hash(hash >> 1) {} + + uint32_t inputOff; + uint32_t live : 1; + uint32_t hash : 31; + uint64_t outputOff = 0; }; static_assert(sizeof(SectionPiece) == 16, "SectionPiece is too big"); @@ -238,74 +246,74 @@ static_assert(sizeof(SectionPiece) == 16, "SectionPiece is too big"); class MergeInputSection : public InputSectionBase { public: template - MergeInputSection(ObjFile &F, const typename ELFT::Shdr &Header, - StringRef Name); - MergeInputSection(uint64_t Flags, uint32_t Type, uint64_t Entsize, - ArrayRef Data, StringRef Name); + MergeInputSection(ObjFile &f, const typename ELFT::Shdr &header, + StringRef name); + MergeInputSection(uint64_t flags, uint32_t type, uint64_t entsize, + ArrayRef data, StringRef name); - static bool classof(const SectionBase *S) { return S->kind() == Merge; } + static bool classof(const SectionBase *s) { return s->kind() == Merge; } void splitIntoPieces(); // Translate an offset in the input section to an offset in the parent // MergeSyntheticSection. - uint64_t getParentOffset(uint64_t Offset) const; + uint64_t getParentOffset(uint64_t offset) const; // Splittable sections are handled as a sequence of data // rather than a single large blob of data. - std::vector Pieces; + std::vector pieces; // Returns I'th piece's data. This function is very hot when // string merging is enabled, so we want to inline. LLVM_ATTRIBUTE_ALWAYS_INLINE - llvm::CachedHashStringRef getData(size_t I) const { - size_t Begin = Pieces[I].InputOff; - size_t End = - (Pieces.size() - 1 == I) ? data().size() : Pieces[I + 1].InputOff; - return {toStringRef(data().slice(Begin, End - Begin)), Pieces[I].Hash}; + llvm::CachedHashStringRef getData(size_t i) const { + size_t begin = pieces[i].inputOff; + size_t end = + (pieces.size() - 1 == i) ? data().size() : pieces[i + 1].inputOff; + return {toStringRef(data().slice(begin, end - begin)), pieces[i].hash}; } // Returns the SectionPiece at a given input section offset. - SectionPiece *getSectionPiece(uint64_t Offset); - const SectionPiece *getSectionPiece(uint64_t Offset) const { - return const_cast(this)->getSectionPiece(Offset); + SectionPiece *getSectionPiece(uint64_t offset); + const SectionPiece *getSectionPiece(uint64_t offset) const { + return const_cast(this)->getSectionPiece(offset); } SyntheticSection *getParent() const; private: - void splitStrings(ArrayRef A, size_t Size); - void splitNonStrings(ArrayRef A, size_t Size); + void splitStrings(ArrayRef a, size_t size); + void splitNonStrings(ArrayRef a, size_t size); }; struct EhSectionPiece { - EhSectionPiece(size_t Off, InputSectionBase *Sec, uint32_t Size, - unsigned FirstRelocation) - : InputOff(Off), Sec(Sec), Size(Size), FirstRelocation(FirstRelocation) {} + EhSectionPiece(size_t off, InputSectionBase *sec, uint32_t size, + unsigned firstRelocation) + : inputOff(off), sec(sec), size(size), firstRelocation(firstRelocation) {} ArrayRef data() { - return {Sec->data().data() + this->InputOff, Size}; + return {sec->data().data() + this->inputOff, size}; } - size_t InputOff; - ssize_t OutputOff = -1; - InputSectionBase *Sec; - uint32_t Size; - unsigned FirstRelocation; + size_t inputOff; + ssize_t outputOff = -1; + InputSectionBase *sec; + uint32_t size; + unsigned firstRelocation; }; // This corresponds to a .eh_frame section of an input file. class EhInputSection : public InputSectionBase { public: template - EhInputSection(ObjFile &F, const typename ELFT::Shdr &Header, - StringRef Name); - static bool classof(const SectionBase *S) { return S->kind() == EHFrame; } + EhInputSection(ObjFile &f, const typename ELFT::Shdr &header, + StringRef name); + static bool classof(const SectionBase *s) { return s->kind() == EHFrame; } template void split(); - template void split(ArrayRef Rels); + template void split(ArrayRef rels); // Splittable sections are handled as a sequence of data // rather than a single large blob of data. - std::vector Pieces; + std::vector pieces; SyntheticSection *getParent() const; }; @@ -316,17 +324,17 @@ public: // .eh_frame. It also includes the synthetic sections themselves. class InputSection : public InputSectionBase { public: - InputSection(InputFile *F, uint64_t Flags, uint32_t Type, uint32_t Alignment, - ArrayRef Data, StringRef Name, Kind K = Regular); + InputSection(InputFile *f, uint64_t flags, uint32_t type, uint32_t alignment, + ArrayRef data, StringRef name, Kind k = Regular); template - InputSection(ObjFile &F, const typename ELFT::Shdr &Header, - StringRef Name); + InputSection(ObjFile &f, const typename ELFT::Shdr &header, + StringRef name); // Write this section to a mmap'ed file, assuming Buf is pointing to // beginning of the output section. - template void writeTo(uint8_t *Buf); + template void writeTo(uint8_t *buf); - uint64_t getOffset(uint64_t Offset) const { return OutSecOff + Offset; } + uint64_t getOffset(uint64_t offset) const { return outSecOff + offset; } OutputSection *getParent() const; @@ -334,32 +342,32 @@ public: // OutputSection's InputSection list, and is used when ordering SHF_LINK_ORDER // sections. After assignAddresses is called, it represents the offset from // the beginning of the output section this section was assigned to. - uint64_t OutSecOff = 0; + uint64_t outSecOff = 0; - static bool classof(const SectionBase *S); + static bool classof(const SectionBase *s); InputSectionBase *getRelocatedSection() const; template - void relocateNonAlloc(uint8_t *Buf, llvm::ArrayRef Rels); + void relocateNonAlloc(uint8_t *buf, llvm::ArrayRef rels); // Used by ICF. - uint32_t Class[2] = {0, 0}; + uint32_t eqClass[2] = {0, 0}; // Called by ICF to merge two input sections. - void replace(InputSection *Other); + void replace(InputSection *other); - static InputSection Discarded; + static InputSection discarded; private: template - void copyRelocations(uint8_t *Buf, llvm::ArrayRef Rels); + void copyRelocations(uint8_t *buf, llvm::ArrayRef rels); - template void copyShtGroup(uint8_t *Buf); + template void copyShtGroup(uint8_t *buf); }; // The list of all input sections. -extern std::vector InputSections; +extern std::vector inputSections; } // namespace elf diff --git a/ELF/LTO.cpp b/ELF/LTO.cpp index ca44581..28d4bfe 100644 --- a/ELF/LTO.cpp +++ b/ELF/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 // //===----------------------------------------------------------------------===// @@ -13,6 +12,7 @@ #include "LinkerScript.h" #include "SymbolTable.h" #include "Symbols.h" +#include "lld/Common/Args.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/TargetOptionsCommandFlags.h" #include "llvm/ADT/STLExtras.h" @@ -47,134 +47,135 @@ using namespace lld::elf; // Creates an empty file to store a list of object files for final // linking of distributed ThinLTO. -static std::unique_ptr openFile(StringRef File) { - std::error_code EC; - auto Ret = - llvm::make_unique(File, EC, sys::fs::OpenFlags::F_None); - if (EC) { - error("cannot open " + File + ": " + EC.message()); +static std::unique_ptr openFile(StringRef file) { + std::error_code ec; + auto ret = + llvm::make_unique(file, ec, sys::fs::OpenFlags::F_None); + if (ec) { + error("cannot open " + file + ": " + ec.message()); return nullptr; } - return Ret; + return ret; } -static std::string getThinLTOOutputFile(StringRef ModulePath) { - return lto::getThinLTOOutputFile(ModulePath, - Config->ThinLTOPrefixReplace.first, - Config->ThinLTOPrefixReplace.second); +static std::string getThinLTOOutputFile(StringRef modulePath) { + return lto::getThinLTOOutputFile(modulePath, + config->thinLTOPrefixReplace.first, + config->thinLTOPrefixReplace.second); } static lto::Config createConfig() { - lto::Config C; + lto::Config c; // LLD supports the new relocations and address-significance tables. - C.Options = InitTargetOptionsFromCodeGenFlags(); - C.Options.RelaxELFRelocations = true; - C.Options.EmitAddrsig = true; + c.Options = initTargetOptionsFromCodeGenFlags(); + c.Options.RelaxELFRelocations = true; + c.Options.EmitAddrsig = true; // Always emit a section per function/datum with LTO. - C.Options.FunctionSections = true; - C.Options.DataSections = true; + c.Options.FunctionSections = true; + c.Options.DataSections = true; - if (Config->Relocatable) - C.RelocModel = None; - else if (Config->Pic) - C.RelocModel = Reloc::PIC_; + if (config->relocatable) + c.RelocModel = None; + else if (config->isPic) + c.RelocModel = Reloc::PIC_; else - C.RelocModel = Reloc::Static; + c.RelocModel = Reloc::Static; - C.CodeModel = GetCodeModelFromCMModel(); - C.DisableVerify = Config->DisableVerify; - C.DiagHandler = diagnosticHandler; - C.OptLevel = Config->LTOO; - C.CPU = GetCPUStr(); - C.MAttrs = GetMAttrs(); + c.CodeModel = getCodeModelFromCMModel(); + c.DisableVerify = config->disableVerify; + c.DiagHandler = diagnosticHandler; + c.OptLevel = config->ltoo; + c.CPU = getCPUStr(); + c.MAttrs = getMAttrs(); + c.CGOptLevel = args::getCGOptLevel(config->ltoo); // Set up a custom pipeline if we've been asked to. - C.OptPipeline = Config->LTONewPmPasses; - C.AAPipeline = Config->LTOAAPipeline; + c.OptPipeline = config->ltoNewPmPasses; + c.AAPipeline = config->ltoAAPipeline; // Set up optimization remarks if we've been asked to. - C.RemarksFilename = Config->OptRemarksFilename; - C.RemarksWithHotness = Config->OptRemarksWithHotness; - - C.SampleProfile = Config->LTOSampleProfile; - C.UseNewPM = Config->LTONewPassManager; - C.DebugPassManager = Config->LTODebugPassManager; - C.DwoDir = Config->DwoDir; - - if (Config->EmitLLVM) { - C.PostInternalizeModuleHook = [](size_t Task, const Module &M) { - if (std::unique_ptr OS = openFile(Config->OutputFile)) - WriteBitcodeToFile(M, *OS, false); + c.RemarksFilename = config->optRemarksFilename; + c.RemarksPasses = config->optRemarksPasses; + c.RemarksWithHotness = config->optRemarksWithHotness; + c.RemarksFormat = config->optRemarksFormat; + + c.SampleProfile = config->ltoSampleProfile; + c.UseNewPM = config->ltoNewPassManager; + c.DebugPassManager = config->ltoDebugPassManager; + c.DwoDir = config->dwoDir; + + c.CSIRProfile = config->ltoCSProfileFile; + c.RunCSIRInstr = config->ltoCSProfileGenerate; + + if (config->emitLLVM) { + c.PostInternalizeModuleHook = [](size_t task, const Module &m) { + if (std::unique_ptr os = openFile(config->outputFile)) + WriteBitcodeToFile(m, *os, false); return false; }; } - if (Config->SaveTemps) - checkError(C.addSaveTemps(Config->OutputFile.str() + ".", + if (config->saveTemps) + checkError(c.addSaveTemps(config->outputFile.str() + ".", /*UseInputModulePath*/ true)); - return C; + return c; } 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 != -1U) { - Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs); + // 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 != -1U) { + backend = lto::createInProcessThinBackend(config->thinLTOJobs); } - LTOObj = llvm::make_unique(createConfig(), Backend, - Config->LTOPartitions); + ltoObj = llvm::make_unique(createConfig(), backend, + config->ltoPartitions); - // Initialize UsedStartStop. - for (Symbol *Sym : Symtab->getSymbols()) { - StringRef S = Sym->getName(); - for (StringRef Prefix : {"__start_", "__stop_"}) - if (S.startswith(Prefix)) - UsedStartStop.insert(S.substr(Prefix.size())); - } + // Initialize usedStartStop. + symtab->forEachSymbol([&](Symbol *sym) { + StringRef s = sym->getName(); + for (StringRef prefix : {"__start_", "__stop_"}) + if (s.startswith(prefix)) + usedStartStop.insert(s.substr(prefix.size())); + }); } BitcodeCompiler::~BitcodeCompiler() = default; -static void undefine(Symbol *S) { - replaceSymbol(S, nullptr, S->getName(), STB_GLOBAL, STV_DEFAULT, - S->Type); -} - -void BitcodeCompiler::add(BitcodeFile &F) { - lto::InputFile &Obj = *F.Obj; - bool IsExec = !Config->Shared && !Config->Relocatable; +void BitcodeCompiler::add(BitcodeFile &f) { + lto::InputFile &obj = *f.obj; + bool isExec = !config->shared && !config->relocatable; - if (Config->ThinLTOIndexOnly) - ThinIndices.insert(Obj.getName()); + if (config->thinLTOIndexOnly) + thinIndices.insert(obj.getName()); - ArrayRef Syms = F.getSymbols(); - ArrayRef ObjSyms = Obj.symbols(); - std::vector Resols(Syms.size()); + ArrayRef syms = f.getSymbols(); + ArrayRef objSyms = obj.symbols(); + std::vector resols(syms.size()); // Provide a resolution to the LTO API for each symbol. - for (size_t I = 0, E = Syms.size(); I != E; ++I) { - Symbol *Sym = Syms[I]; - const lto::InputFile::Symbol &ObjSym = ObjSyms[I]; - lto::SymbolResolution &R = Resols[I]; + for (size_t i = 0, e = syms.size(); i != e; ++i) { + Symbol *sym = syms[i]; + const lto::InputFile::Symbol &objSym = objSyms[i]; + lto::SymbolResolution &r = resols[i]; // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile // reports two symbols for module ASM defined. Without this check, lld // 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->File == &F; + r.Prevailing = !objSym.isUndefined() && sym->file == &f; // We ask LTO to preserve following global symbols: // 1) All symbols when doing relocatable link, so that them can be used @@ -182,115 +183,121 @@ void BitcodeCompiler::add(BitcodeFile &F) { // 2) Symbols that are used in regular objects. // 3) C named sections if we have corresponding __start_/__stop_ symbol. // 4) Symbols that are defined in bitcode files and used for dynamic linking. - R.VisibleToRegularObj = Config->Relocatable || Sym->IsUsedInRegularObj || - (R.Prevailing && Sym->includeInDynsym()) || - UsedStartStop.count(ObjSym.getSectionName()); - const auto *DR = dyn_cast(Sym); - R.FinalDefinitionInLinkageUnit = - (IsExec || Sym->Visibility != STV_DEFAULT) && DR && + r.VisibleToRegularObj = config->relocatable || sym->isUsedInRegularObj || + (r.Prevailing && sym->includeInDynsym()) || + usedStartStop.count(objSym.getSectionName()); + const auto *dr = dyn_cast(sym); + r.FinalDefinitionInLinkageUnit = + (isExec || sym->visibility != STV_DEFAULT) && dr && // Skip absolute symbols from ELF objects, otherwise PC-rel relocations // will be generated by for them, triggering linker errors. // Symbol section is always null for bitcode symbols, hence the check // for isElf(). Skip linker script defined symbols as well: they have // no File defined. - !(DR->Section == nullptr && (!Sym->File || Sym->File->isElf())); + !(dr->section == nullptr && (!sym->file || sym->file->isElf())); - if (R.Prevailing) - undefine(Sym); + if (r.Prevailing) + sym->replace(Undefined{nullptr, sym->getName(), STB_GLOBAL, STV_DEFAULT, + sym->type}); // We tell LTO to not apply interprocedural optimization for wrapped // (with --wrap) symbols because otherwise LTO would inline them while // their values are still not final. - R.LinkerRedefined = !Sym->CanInline; + r.LinkerRedefined = !sym->canInline; } - checkError(LTOObj->add(std::move(F.Obj), Resols)); + checkError(ltoObj->add(std::move(f.obj), resols)); } -static void createEmptyIndex(StringRef ModulePath) { - std::string Path = replaceThinLTOSuffix(getThinLTOOutputFile(ModulePath)); - std::unique_ptr OS = openFile(Path + ".thinlto.bc"); - if (!OS) - return; - - ModuleSummaryIndex M(/*HaveGVs*/ false); - M.setSkipModuleByDistributedBackend(); - WriteIndexToFile(M, *OS); +// If LazyObjFile has not been added to link, emit empty index files. +// This is needed because this is what GNU gold plugin does and we have a +// distributed build system that depends on that behavior. +static void thinLTOCreateEmptyIndexFiles() { + for (LazyObjFile *f : lazyObjFiles) { + if (!isBitcode(f->mb)) + continue; + std::string path = replaceThinLTOSuffix(getThinLTOOutputFile(f->getName())); + std::unique_ptr os = openFile(path + ".thinlto.bc"); + if (!os) + continue; - if (Config->ThinLTOEmitImportsFiles) - openFile(Path + ".imports"); + ModuleSummaryIndex m(/*HaveGVs*/ false); + m.setSkipModuleByDistributedBackend(); + WriteIndexToFile(m, *os); + if (config->thinLTOEmitImportsFiles) + openFile(path + ".imports"); + } } // Merge all the bitcode files we have seen, codegen the result // and return the resulting ObjectFile(s). std::vector BitcodeCompiler::compile() { - unsigned MaxTasks = LTOObj->getMaxTasks(); - Buf.resize(MaxTasks); - Files.resize(MaxTasks); + unsigned maxTasks = ltoObj->getMaxTasks(); + buf.resize(maxTasks); + files.resize(maxTasks); // The --thinlto-cache-dir 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->ThinLTOCacheDir.empty()) - Cache = check( - lto::localCache(Config->ThinLTOCacheDir, - [&](size_t Task, std::unique_ptr MB) { - Files[Task] = std::move(MB); + lto::NativeObjectCache cache; + if (!config->thinLTOCacheDir.empty()) + cache = check( + lto::localCache(config->thinLTOCacheDir, + [&](size_t task, std::unique_ptr mb) { + files[task] = std::move(mb); })); - checkError(LTOObj->run( - [&](size_t Task) { - return llvm::make_unique( - llvm::make_unique(Buf[Task])); - }, - Cache)); + if (!bitcodeFiles.empty()) + checkError(ltoObj->run( + [&](size_t task) { + return llvm::make_unique( + llvm::make_unique(buf[task])); + }, + 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"); + for (StringRef s : thinIndices) { + std::string path = getThinLTOOutputFile(s); + openFile(path + ".thinlto.bc"); + if (config->thinLTOEmitImportsFiles) + openFile(path + ".imports"); } - // If LazyObjFile has not been added to link, emit empty index files. - // This is needed because this is what GNU gold plugin does and we have a - // distributed build system that depends on that behavior. - if (Config->ThinLTOIndexOnly) { - for (LazyObjFile *F : LazyObjFiles) - if (!F->AddedToLink && isBitcode(F->MB)) - createEmptyIndex(F->getName()); + if (config->thinLTOIndexOnly) { + thinLTOCreateEmptyIndexFiles(); - if (!Config->LTOObjPath.empty()) - saveBuffer(Buf[0], Config->LTOObjPath); + if (!config->ltoObjPath.empty()) + saveBuffer(buf[0], config->ltoObjPath); // ThinLTO with index only option is required to generate only the index // files. After that, we exit from linker and ThinLTO backend runs in a // distributed environment. - if (IndexFile) - IndexFile->close(); + if (indexFile) + indexFile->close(); return {}; } - if (!Config->ThinLTOCacheDir.empty()) - pruneCache(Config->ThinLTOCacheDir, Config->ThinLTOCachePolicy); + if (!config->thinLTOCacheDir.empty()) + pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy); - std::vector Ret; - for (unsigned I = 0; I != MaxTasks; ++I) { - if (Buf[I].empty()) - continue; - if (Config->SaveTemps) { - if (I == 0) - saveBuffer(Buf[I], Config->OutputFile + ".lto.o"); - else - saveBuffer(Buf[I], Config->OutputFile + Twine(I) + ".lto.o"); - } - InputFile *Obj = createObjectFile(MemoryBufferRef(Buf[I], "lto.tmp")); - Ret.push_back(Obj); + if (!config->ltoObjPath.empty()) { + saveBuffer(buf[0], config->ltoObjPath); + for (unsigned i = 1; i != maxTasks; ++i) + saveBuffer(buf[i], config->ltoObjPath + Twine(i)); } - for (std::unique_ptr &File : Files) - if (File) - Ret.push_back(createObjectFile(*File)); - return Ret; + if (config->saveTemps) { + saveBuffer(buf[0], config->outputFile + ".lto.o"); + for (unsigned i = 1; i != maxTasks; ++i) + saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.o"); + } + + std::vector ret; + for (unsigned i = 0; i != maxTasks; ++i) + if (!buf[i].empty()) + ret.push_back(createObjectFile(MemoryBufferRef(buf[i], "lto.tmp"))); + + for (std::unique_ptr &file : files) + if (file) + ret.push_back(createObjectFile(*file)); + return ret; } diff --git a/ELF/LTO.h b/ELF/LTO.h index a190da3..4cb42d8 100644 --- a/ELF/LTO.h +++ b/ELF/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 // //===----------------------------------------------------------------------===// // @@ -46,16 +45,16 @@ public: BitcodeCompiler(); ~BitcodeCompiler(); - void add(BitcodeFile &F); + void add(BitcodeFile &f); std::vector compile(); private: - std::unique_ptr LTOObj; - std::vector> Buf; - std::vector> Files; - llvm::DenseSet UsedStartStop; - std::unique_ptr IndexFile; - llvm::DenseSet ThinIndices; + std::unique_ptr ltoObj; + std::vector> buf; + std::vector> files; + llvm::DenseSet usedStartStop; + std::unique_ptr indexFile; + llvm::DenseSet thinIndices; }; } // namespace elf } // namespace lld diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp index fbc0254..49e44d7 100644 --- a/ELF/LinkerScript.cpp +++ b/ELF/LinkerScript.cpp @@ -1,9 +1,8 @@ //===- LinkerScript.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 // //===----------------------------------------------------------------------===// // @@ -47,26 +46,26 @@ using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; -LinkerScript *elf::Script; +LinkerScript *elf::script; -static uint64_t getOutputSectionVA(SectionBase *InputSec, StringRef Loc) { - if (OutputSection *OS = InputSec->getOutputSection()) - return OS->Addr; - error(Loc + ": unable to evaluate expression: input section " + - InputSec->Name + " has no output section assigned"); +static uint64_t getOutputSectionVA(SectionBase *inputSec, StringRef loc) { + if (OutputSection *os = inputSec->getOutputSection()) + return os->addr; + error(loc + ": unable to evaluate expression: input section " + + inputSec->name + " has no output section assigned"); return 0; } uint64_t ExprValue::getValue() const { - if (Sec) - return alignTo(Sec->getOffset(Val) + getOutputSectionVA(Sec, Loc), - Alignment); - return alignTo(Val, Alignment); + if (sec) + return alignTo(sec->getOffset(val) + getOutputSectionVA(sec, loc), + alignment); + return alignTo(val, alignment); } uint64_t ExprValue::getSecAddr() const { - if (Sec) - return Sec->getOffset(0) + getOutputSectionVA(Sec, Loc); + if (sec) + return sec->getOffset(0) + getOutputSectionVA(sec, loc); return 0; } @@ -74,106 +73,100 @@ uint64_t ExprValue::getSectionOffset() const { // If the alignment is trivial, we don't have to compute the full // value to know the offset. This allows this function to succeed in // cases where the output section is not yet known. - if (Alignment == 1 && (!Sec || !Sec->getOutputSection())) - return Val; + if (alignment == 1 && (!sec || !sec->getOutputSection())) + return val; return getValue() - getSecAddr(); } -OutputSection *LinkerScript::createOutputSection(StringRef Name, - StringRef Location) { - OutputSection *&SecRef = NameToOutputSection[Name]; - OutputSection *Sec; - if (SecRef && SecRef->Location.empty()) { +OutputSection *LinkerScript::createOutputSection(StringRef name, + StringRef location) { + OutputSection *&secRef = nameToOutputSection[name]; + OutputSection *sec; + if (secRef && secRef->location.empty()) { // There was a forward reference. - Sec = SecRef; + sec = secRef; } else { - Sec = make(Name, SHT_NOBITS, 0); - if (!SecRef) - SecRef = Sec; + sec = make(name, SHT_PROGBITS, 0); + if (!secRef) + secRef = sec; } - Sec->Location = Location; - return Sec; + sec->location = location; + return sec; } -OutputSection *LinkerScript::getOrCreateOutputSection(StringRef Name) { - OutputSection *&CmdRef = NameToOutputSection[Name]; - if (!CmdRef) - CmdRef = make(Name, SHT_PROGBITS, 0); - return CmdRef; +OutputSection *LinkerScript::getOrCreateOutputSection(StringRef name) { + OutputSection *&cmdRef = nameToOutputSection[name]; + if (!cmdRef) + cmdRef = make(name, SHT_PROGBITS, 0); + return cmdRef; } // Expands the memory region by the specified size. -static void expandMemoryRegion(MemoryRegion *MemRegion, uint64_t Size, - StringRef RegionName, StringRef SecName) { - MemRegion->CurPos += Size; - uint64_t NewSize = MemRegion->CurPos - MemRegion->Origin; - if (NewSize > MemRegion->Length) - error("section '" + SecName + "' will not fit in region '" + RegionName + - "': overflowed by " + Twine(NewSize - MemRegion->Length) + " bytes"); +static void expandMemoryRegion(MemoryRegion *memRegion, uint64_t size, + StringRef regionName, StringRef secName) { + memRegion->curPos += size; + uint64_t newSize = memRegion->curPos - memRegion->origin; + if (newSize > memRegion->length) + error("section '" + secName + "' will not fit in region '" + regionName + + "': overflowed by " + Twine(newSize - memRegion->length) + " bytes"); } -void LinkerScript::expandMemoryRegions(uint64_t Size) { - if (Ctx->MemRegion) - expandMemoryRegion(Ctx->MemRegion, Size, Ctx->MemRegion->Name, - Ctx->OutSec->Name); - // Only expand the LMARegion if it is different from MemRegion. - if (Ctx->LMARegion && Ctx->MemRegion != Ctx->LMARegion) - expandMemoryRegion(Ctx->LMARegion, Size, Ctx->LMARegion->Name, - Ctx->OutSec->Name); +void LinkerScript::expandMemoryRegions(uint64_t size) { + if (ctx->memRegion) + expandMemoryRegion(ctx->memRegion, size, ctx->memRegion->name, + ctx->outSec->name); + // Only expand the LMARegion if it is different from memRegion. + if (ctx->lmaRegion && ctx->memRegion != ctx->lmaRegion) + expandMemoryRegion(ctx->lmaRegion, size, ctx->lmaRegion->name, + ctx->outSec->name); } -void LinkerScript::expandOutputSection(uint64_t Size) { - Ctx->OutSec->Size += Size; - expandMemoryRegions(Size); +void LinkerScript::expandOutputSection(uint64_t size) { + ctx->outSec->size += size; + expandMemoryRegions(size); } -void LinkerScript::setDot(Expr E, const Twine &Loc, bool InSec) { - uint64_t Val = E().getValue(); - if (Val < Dot && InSec) - error(Loc + ": unable to move location counter backward for: " + - Ctx->OutSec->Name); +void LinkerScript::setDot(Expr e, const Twine &loc, bool inSec) { + uint64_t val = e().getValue(); + if (val < dot && inSec) + error(loc + ": unable to move location counter backward for: " + + ctx->outSec->name); // Update to location counter means update to section size. - if (InSec) - expandOutputSection(Val - Dot); - else - expandMemoryRegions(Val - Dot); + if (inSec) + expandOutputSection(val - dot); - Dot = Val; + dot = val; } // Used for handling linker symbol assignments, for both finalizing // their values and doing early declarations. Returns true if symbol // should be defined from linker script. -static bool shouldDefineSym(SymbolAssignment *Cmd) { - if (Cmd->Name == ".") +static bool shouldDefineSym(SymbolAssignment *cmd) { + if (cmd->name == ".") return false; - if (!Cmd->Provide) + if (!cmd->provide) return true; // If a symbol was in PROVIDE(), we need to define it only // when it is a referenced undefined symbol. - Symbol *B = Symtab->find(Cmd->Name); - if (B && !B->isDefined()) + Symbol *b = symtab->find(cmd->name); + if (b && !b->isDefined()) return true; return false; } // This function is called from processSectionCommands, // while we are fixing the output section layout. -void LinkerScript::addSymbol(SymbolAssignment *Cmd) { - if (!shouldDefineSym(Cmd)) +void LinkerScript::addSymbol(SymbolAssignment *cmd) { + if (!shouldDefineSym(cmd)) return; // Define a symbol. - Symbol *Sym; - uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT; - std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, Visibility, - /*CanOmitFromDynSym*/ false, - /*File*/ nullptr); - ExprValue Value = Cmd->Expression(); - SectionBase *Sec = Value.isAbsolute() ? nullptr : Value.Sec; + ExprValue value = cmd->expression(); + SectionBase *sec = value.isAbsolute() ? nullptr : value.sec; + uint8_t visibility = cmd->hidden ? STV_HIDDEN : STV_DEFAULT; // When this function is called, section addresses have not been // fixed yet. So, we may or may not know the value of the RHS @@ -186,68 +179,73 @@ void LinkerScript::addSymbol(SymbolAssignment *Cmd) { // We want to set symbol values early if we can. This allows us to // use symbols as variables in linker scripts. Doing so allows us to // write expressions like this: `alignment = 16; . = ALIGN(., alignment)`. - uint64_t SymValue = Value.Sec ? 0 : Value.getValue(); + uint64_t symValue = value.sec ? 0 : value.getValue(); + + Defined New(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, symValue, + 0, sec); - replaceSymbol(Sym, nullptr, Cmd->Name, STB_GLOBAL, Visibility, - STT_NOTYPE, SymValue, 0, Sec); - Cmd->Sym = cast(Sym); + Symbol *sym = symtab->insert(cmd->name); + sym->mergeProperties(New); + sym->replace(New); + cmd->sym = cast(sym); } // This function is called from LinkerScript::declareSymbols. // It creates a placeholder symbol if needed. -static void declareSymbol(SymbolAssignment *Cmd) { - if (!shouldDefineSym(Cmd)) +static void declareSymbol(SymbolAssignment *cmd) { + if (!shouldDefineSym(cmd)) return; + uint8_t visibility = cmd->hidden ? STV_HIDDEN : STV_DEFAULT; + Defined New(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, 0, 0, + nullptr); + // We can't calculate final value right now. - Symbol *Sym; - uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT; - std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, Visibility, - /*CanOmitFromDynSym*/ false, - /*File*/ nullptr); - replaceSymbol(Sym, nullptr, Cmd->Name, STB_GLOBAL, Visibility, - STT_NOTYPE, 0, 0, nullptr); - Cmd->Sym = cast(Sym); - Cmd->Provide = false; - Sym->ScriptDefined = true; + Symbol *sym = symtab->insert(cmd->name); + sym->mergeProperties(New); + sym->replace(New); + + cmd->sym = cast(sym); + cmd->provide = false; + sym->scriptDefined = true; } // This method is used to handle INSERT AFTER statement. Here we rebuild // the list of script commands to mix sections inserted into. void LinkerScript::processInsertCommands() { - std::vector V; - auto Insert = [&](std::vector &From) { - V.insert(V.end(), From.begin(), From.end()); - From.clear(); + std::vector v; + auto insert = [&](std::vector &from) { + v.insert(v.end(), from.begin(), from.end()); + from.clear(); }; - for (BaseCommand *Base : SectionCommands) { - if (auto *OS = dyn_cast(Base)) { - Insert(InsertBeforeCommands[OS->Name]); - V.push_back(Base); - Insert(InsertAfterCommands[OS->Name]); + for (BaseCommand *base : sectionCommands) { + if (auto *os = dyn_cast(base)) { + insert(insertBeforeCommands[os->name]); + v.push_back(base); + insert(insertAfterCommands[os->name]); continue; } - V.push_back(Base); + v.push_back(base); } - for (auto &Cmds : {InsertBeforeCommands, InsertAfterCommands}) - for (const std::pair> &P : Cmds) - if (!P.second.empty()) - error("unable to INSERT AFTER/BEFORE " + P.first + + for (auto &cmds : {insertBeforeCommands, insertAfterCommands}) + for (const std::pair> &p : cmds) + if (!p.second.empty()) + error("unable to INSERT AFTER/BEFORE " + p.first + ": section not defined"); - SectionCommands = std::move(V); + sectionCommands = std::move(v); } // Symbols defined in script should not be inlined by LTO. At the same time // we don't know their final values until late stages of link. Here we scan // over symbol assignment commands and create placeholder symbols if needed. void LinkerScript::declareSymbols() { - assert(!Ctx); - for (BaseCommand *Base : SectionCommands) { - if (auto *Cmd = dyn_cast(Base)) { - declareSymbol(Cmd); + assert(!ctx); + for (BaseCommand *base : sectionCommands) { + if (auto *cmd = dyn_cast(base)) { + declareSymbol(cmd); continue; } @@ -255,75 +253,75 @@ void LinkerScript::declareSymbols() { // we can't say for sure if it is going to be included or not. // Skip such sections for now. Improve the checks if we ever // need symbols from that sections to be declared early. - auto *Sec = cast(Base); - if (Sec->Constraint != ConstraintKind::NoConstraint) + auto *sec = cast(base); + if (sec->constraint != ConstraintKind::NoConstraint) continue; - for (BaseCommand *Base2 : Sec->SectionCommands) - if (auto *Cmd = dyn_cast(Base2)) - declareSymbol(Cmd); + for (BaseCommand *base2 : sec->sectionCommands) + if (auto *cmd = dyn_cast(base2)) + declareSymbol(cmd); } } // This function is called from assignAddresses, while we are // fixing the output section addresses. This function is supposed // to set the final value for a given symbol assignment. -void LinkerScript::assignSymbol(SymbolAssignment *Cmd, bool InSec) { - if (Cmd->Name == ".") { - setDot(Cmd->Expression, Cmd->Location, InSec); +void LinkerScript::assignSymbol(SymbolAssignment *cmd, bool inSec) { + if (cmd->name == ".") { + setDot(cmd->expression, cmd->location, inSec); return; } - if (!Cmd->Sym) + if (!cmd->sym) return; - ExprValue V = Cmd->Expression(); - if (V.isAbsolute()) { - Cmd->Sym->Section = nullptr; - Cmd->Sym->Value = V.getValue(); + ExprValue v = cmd->expression(); + if (v.isAbsolute()) { + cmd->sym->section = nullptr; + cmd->sym->value = v.getValue(); } else { - Cmd->Sym->Section = V.Sec; - Cmd->Sym->Value = V.getSectionOffset(); + cmd->sym->section = v.sec; + cmd->sym->value = v.getSectionOffset(); } } -static std::string getFilename(InputFile *File) { - if (!File) +static std::string getFilename(InputFile *file) { + if (!file) return ""; - if (File->ArchiveName.empty()) - return File->getName(); - return (File->ArchiveName + "(" + File->getName() + ")").str(); + if (file->archiveName.empty()) + return file->getName(); + return (file->archiveName + "(" + file->getName() + ")").str(); } -bool LinkerScript::shouldKeep(InputSectionBase *S) { - if (KeptSections.empty()) +bool LinkerScript::shouldKeep(InputSectionBase *s) { + if (keptSections.empty()) return false; - std::string Filename = getFilename(S->File); - for (InputSectionDescription *ID : KeptSections) - if (ID->FilePat.match(Filename)) - for (SectionPattern &P : ID->SectionPatterns) - if (P.SectionPat.match(S->Name)) + std::string filename = getFilename(s->file); + for (InputSectionDescription *id : keptSections) + if (id->filePat.match(filename)) + for (SectionPattern &p : id->sectionPatterns) + if (p.sectionPat.match(s->name)) return true; return false; } // A helper function for the SORT() command. static std::function -getComparator(SortSectionPolicy K) { - switch (K) { +getComparator(SortSectionPolicy k) { + switch (k) { case SortSectionPolicy::Alignment: - return [](InputSectionBase *A, InputSectionBase *B) { + return [](InputSectionBase *a, InputSectionBase *b) { // ">" is not a mistake. Sections with larger alignments are placed // before sections with smaller alignments in order to reduce the // amount of padding necessary. This is compatible with GNU. - return A->Alignment > B->Alignment; + return a->alignment > b->alignment; }; case SortSectionPolicy::Name: - return [](InputSectionBase *A, InputSectionBase *B) { - return A->Name < B->Name; + return [](InputSectionBase *a, InputSectionBase *b) { + return a->name < b->name; }; case SortSectionPolicy::Priority: - return [](InputSectionBase *A, InputSectionBase *B) { - return getPriority(A->Name) < getPriority(B->Name); + return [](InputSectionBase *a, InputSectionBase *b) { + return getPriority(a->name) < getPriority(b->name); }; default: llvm_unreachable("unknown sort policy"); @@ -331,22 +329,22 @@ getComparator(SortSectionPolicy K) { } // A helper function for the SORT() command. -static bool matchConstraints(ArrayRef Sections, - ConstraintKind Kind) { - if (Kind == ConstraintKind::NoConstraint) +static bool matchConstraints(ArrayRef sections, + ConstraintKind kind) { + if (kind == ConstraintKind::NoConstraint) return true; - bool IsRW = llvm::any_of( - Sections, [](InputSection *Sec) { return Sec->Flags & SHF_WRITE; }); + bool isRW = llvm::any_of( + sections, [](InputSection *sec) { return sec->flags & SHF_WRITE; }); - return (IsRW && Kind == ConstraintKind::ReadWrite) || - (!IsRW && Kind == ConstraintKind::ReadOnly); + return (isRW && kind == ConstraintKind::ReadWrite) || + (!isRW && kind == ConstraintKind::ReadOnly); } -static void sortSections(MutableArrayRef Vec, - SortSectionPolicy K) { - if (K != SortSectionPolicy::Default && K != SortSectionPolicy::None) - std::stable_sort(Vec.begin(), Vec.end(), getComparator(K)); +static void sortSections(MutableArrayRef vec, + SortSectionPolicy k) { + if (k != SortSectionPolicy::Default && k != SortSectionPolicy::None) + llvm::stable_sort(vec, getComparator(k)); } // Sort sections as instructed by SORT-family commands and --sort-section @@ -360,29 +358,29 @@ static void sortSections(MutableArrayRef Vec, // --sort-section is handled as an inner SORT command. // 3. If one SORT command is given, and if it is SORT_NONE, don't sort. // 4. If no SORT command is given, sort according to --sort-section. -static void sortInputSections(MutableArrayRef Vec, - const SectionPattern &Pat) { - if (Pat.SortOuter == SortSectionPolicy::None) +static void sortInputSections(MutableArrayRef vec, + const SectionPattern &pat) { + if (pat.sortOuter == SortSectionPolicy::None) return; - if (Pat.SortInner == SortSectionPolicy::Default) - sortSections(Vec, Config->SortSection); + if (pat.sortInner == SortSectionPolicy::Default) + sortSections(vec, config->sortSection); else - sortSections(Vec, Pat.SortInner); - sortSections(Vec, Pat.SortOuter); + sortSections(vec, pat.sortInner); + sortSections(vec, pat.sortOuter); } // Compute and remember which sections the InputSectionDescription matches. std::vector -LinkerScript::computeInputSections(const InputSectionDescription *Cmd) { - std::vector Ret; +LinkerScript::computeInputSections(const InputSectionDescription *cmd) { + std::vector ret; // Collects all sections that satisfy constraints of Cmd. - for (const SectionPattern &Pat : Cmd->SectionPatterns) { - size_t SizeBefore = Ret.size(); + for (const SectionPattern &pat : cmd->sectionPatterns) { + size_t sizeBefore = ret.size(); - for (InputSectionBase *Sec : InputSections) { - if (!Sec->Live || Sec->Assigned) + for (InputSectionBase *sec : inputSections) { + if (!sec->isLive() || sec->assigned) continue; // For -emit-relocs we have to ignore entries like @@ -390,59 +388,59 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd) { // which are common because they are in the default bfd script. // We do not ignore SHT_REL[A] linker-synthesized sections here because // want to support scripts that do custom layout for them. - if (auto *IS = dyn_cast(Sec)) - if (IS->getRelocatedSection()) + if (auto *isec = dyn_cast(sec)) + if (isec->getRelocatedSection()) continue; - std::string Filename = getFilename(Sec->File); - if (!Cmd->FilePat.match(Filename) || - Pat.ExcludedFilePat.match(Filename) || - !Pat.SectionPat.match(Sec->Name)) + std::string filename = getFilename(sec->file); + if (!cmd->filePat.match(filename) || + pat.excludedFilePat.match(filename) || + !pat.sectionPat.match(sec->name)) continue; // It is safe to assume that Sec is an InputSection // because mergeable or EH input sections have already been // handled and eliminated. - Ret.push_back(cast(Sec)); - Sec->Assigned = true; + ret.push_back(cast(sec)); + sec->assigned = true; } - sortInputSections(MutableArrayRef(Ret).slice(SizeBefore), - Pat); + sortInputSections(MutableArrayRef(ret).slice(sizeBefore), + pat); } - return Ret; + return ret; } -void LinkerScript::discard(ArrayRef V) { - for (InputSection *S : V) { - if (S == In.ShStrTab || S == In.RelaDyn || S == In.RelrDyn) - error("discarding " + S->Name + " section is not allowed"); +void LinkerScript::discard(ArrayRef v) { + for (InputSection *s : v) { + if (s == in.shStrTab || s == mainPart->relaDyn || s == mainPart->relrDyn) + error("discarding " + s->name + " section is not allowed"); // You can discard .hash and .gnu.hash sections by linker scripts. Since // they are synthesized sections, we need to handle them differently than // other regular sections. - if (S == In.GnuHashTab) - In.GnuHashTab = nullptr; - if (S == In.HashTab) - In.HashTab = nullptr; - - S->Assigned = false; - S->Live = false; - discard(S->DependentSections); + if (s == mainPart->gnuHashTab) + mainPart->gnuHashTab = nullptr; + if (s == mainPart->hashTab) + mainPart->hashTab = nullptr; + + s->assigned = false; + s->markDead(); + discard(s->dependentSections); } } std::vector -LinkerScript::createInputSectionList(OutputSection &OutCmd) { - std::vector Ret; +LinkerScript::createInputSectionList(OutputSection &outCmd) { + std::vector ret; - for (BaseCommand *Base : OutCmd.SectionCommands) { - if (auto *Cmd = dyn_cast(Base)) { - Cmd->Sections = computeInputSections(Cmd); - Ret.insert(Ret.end(), Cmd->Sections.begin(), Cmd->Sections.end()); + for (BaseCommand *base : outCmd.sectionCommands) { + if (auto *cmd = dyn_cast(base)) { + cmd->sections = computeInputSections(cmd); + ret.insert(ret.end(), cmd->sections.begin(), cmd->sections.end()); } } - return Ret; + return ret; } void LinkerScript::processSectionCommands() { @@ -455,34 +453,34 @@ void LinkerScript::processSectionCommands() { // To handle that, create a dummy aether section that fills the void before // the linker scripts switches to another section. It has an index of one // which will map to whatever the first actual section is. - Aether = make("", 0, SHF_ALLOC); - Aether->SectionIndex = 1; + aether = make("", 0, SHF_ALLOC); + aether->sectionIndex = 1; // Ctx captures the local AddressState and makes it accessible deliberately. // This is needed as there are some cases where we cannot just // thread the current state through to a lambda function created by the // script parser. - auto Deleter = make_unique(); - Ctx = Deleter.get(); - Ctx->OutSec = Aether; + auto deleter = make_unique(); + ctx = deleter.get(); + ctx->outSec = aether; - size_t I = 0; + size_t i = 0; // Add input sections to output sections. - for (BaseCommand *Base : SectionCommands) { + for (BaseCommand *base : sectionCommands) { // Handle symbol assignments outside of any output section. - if (auto *Cmd = dyn_cast(Base)) { - addSymbol(Cmd); + if (auto *cmd = dyn_cast(base)) { + addSymbol(cmd); continue; } - if (auto *Sec = dyn_cast(Base)) { - std::vector V = createInputSectionList(*Sec); + if (auto *sec = dyn_cast(base)) { + std::vector v = createInputSectionList(*sec); // The output section name `/DISCARD/' is special. // Any input section assigned to it is discarded. - if (Sec->Name == "/DISCARD/") { - discard(V); - Sec->SectionCommands.clear(); + if (sec->name == "/DISCARD/") { + discard(v); + sec->sectionCommands.clear(); continue; } @@ -493,60 +491,61 @@ void LinkerScript::processSectionCommands() { // // Because we'll iterate over SectionCommands many more times, the easy // way to "make it as if it wasn't present" is to make it empty. - if (!matchConstraints(V, Sec->Constraint)) { - for (InputSectionBase *S : V) - S->Assigned = false; - Sec->SectionCommands.clear(); + if (!matchConstraints(v, sec->constraint)) { + for (InputSectionBase *s : v) + s->assigned = false; + sec->sectionCommands.clear(); continue; } // A directive may contain symbol definitions like this: // ".foo : { ...; bar = .; }". Handle them. - for (BaseCommand *Base : Sec->SectionCommands) - if (auto *OutCmd = dyn_cast(Base)) - addSymbol(OutCmd); + for (BaseCommand *base : sec->sectionCommands) + if (auto *outCmd = dyn_cast(base)) + addSymbol(outCmd); // Handle subalign (e.g. ".foo : SUBALIGN(32) { ... }"). If subalign // is given, input sections are aligned to that value, whether the // given value is larger or smaller than the original section alignment. - if (Sec->SubalignExpr) { - uint32_t Subalign = Sec->SubalignExpr().getValue(); - for (InputSectionBase *S : V) - S->Alignment = Subalign; + if (sec->subalignExpr) { + uint32_t subalign = sec->subalignExpr().getValue(); + for (InputSectionBase *s : v) + s->alignment = subalign; } // Add input sections to an output section. - for (InputSection *S : V) - Sec->addSection(S); - - Sec->SectionIndex = I++; - if (Sec->Noload) - Sec->Type = SHT_NOBITS; - if (Sec->NonAlloc) - Sec->Flags &= ~(uint64_t)SHF_ALLOC; + for (InputSection *s : v) + sec->addSection(s); + + sec->sectionIndex = i++; + if (sec->noload) + sec->type = SHT_NOBITS; + if (sec->nonAlloc) + sec->flags &= ~(uint64_t)SHF_ALLOC; } } - Ctx = nullptr; + ctx = nullptr; } -static OutputSection *findByName(ArrayRef Vec, - StringRef Name) { - for (BaseCommand *Base : Vec) - if (auto *Sec = dyn_cast(Base)) - if (Sec->Name == Name) - return Sec; +static OutputSection *findByName(ArrayRef vec, + StringRef name) { + for (BaseCommand *base : vec) + if (auto *sec = dyn_cast(base)) + if (sec->name == name) + return sec; return nullptr; } -static OutputSection *createSection(InputSectionBase *IS, - StringRef OutsecName) { - OutputSection *Sec = Script->createOutputSection(OutsecName, ""); - Sec->addSection(cast(IS)); - return Sec; +static OutputSection *createSection(InputSectionBase *isec, + StringRef outsecName) { + OutputSection *sec = script->createOutputSection(outsecName, ""); + sec->addSection(cast(isec)); + return sec; } -static OutputSection *addInputSec(StringMap &Map, - InputSectionBase *IS, StringRef OutsecName) { +static OutputSection * +addInputSec(StringMap> &map, + InputSectionBase *isec, StringRef outsecName) { // Sections with SHT_GROUP or SHF_GROUP attributes reach here only when the -r // option is given. A section with SHT_GROUP defines a "section group", and // its members have SHF_GROUP attribute. Usually these flags have already been @@ -554,8 +553,8 @@ static OutputSection *addInputSec(StringMap &Map, // However, for the -r option, we want to pass through all section groups // as-is because adding/removing members or merging them with other groups // change their semantics. - if (IS->Type == SHT_GROUP || (IS->Flags & SHF_GROUP)) - return createSection(IS, OutsecName); + if (isec->type == SHT_GROUP || (isec->flags & SHF_GROUP)) + return createSection(isec, outsecName); // Imagine .zed : { *(.foo) *(.bar) } script. Both foo and bar may have // relocation sections .rela.foo and .rela.bar for example. Most tools do @@ -563,25 +562,25 @@ static OutputSection *addInputSec(StringMap &Map, // should combine these relocation sections into single output. // We skip synthetic sections because it can be .rela.dyn/.rela.plt or any // other REL[A] sections created by linker itself. - if (!isa(IS) && - (IS->Type == SHT_REL || IS->Type == SHT_RELA)) { - auto *Sec = cast(IS); - OutputSection *Out = Sec->getRelocatedSection()->getOutputSection(); + if (!isa(isec) && + (isec->type == SHT_REL || isec->type == SHT_RELA)) { + auto *sec = cast(isec); + OutputSection *out = sec->getRelocatedSection()->getOutputSection(); - if (Out->RelocationSection) { - Out->RelocationSection->addSection(Sec); + if (out->relocationSection) { + out->relocationSection->addSection(sec); return nullptr; } - Out->RelocationSection = createSection(IS, OutsecName); - return Out->RelocationSection; + out->relocationSection = createSection(isec, outsecName); + return out->relocationSection; } // When control reaches here, mergeable sections have already been merged into // synthetic sections. For relocatable case we want to create one output // section per syntetic section so that they have a valid sh_entsize. - if (Config->Relocatable && (IS->Flags & SHF_MERGE)) - return createSection(IS, OutsecName); + if (config->relocatable && (isec->flags & SHF_MERGE)) + return createSection(isec, outsecName); // The ELF spec just says // ---------------------------------------------------------------- @@ -625,159 +624,161 @@ static OutputSection *addInputSec(StringMap &Map, // // Given the above issues, we instead merge sections by name and error on // incompatible types and flags. - OutputSection *&Sec = Map[OutsecName]; - if (Sec) { - Sec->addSection(cast(IS)); + TinyPtrVector &v = map[outsecName]; + for (OutputSection *sec : v) { + if (sec->partition != isec->partition) + continue; + sec->addSection(cast(isec)); return nullptr; } - Sec = createSection(IS, OutsecName); - return Sec; + OutputSection *sec = createSection(isec, outsecName); + v.push_back(sec); + return sec; } // Add sections that didn't match any sections command. void LinkerScript::addOrphanSections() { - unsigned End = SectionCommands.size(); - StringMap Map; - std::vector V; + StringMap> map; + std::vector v; - auto Add = [&](InputSectionBase *S) { - if (!S->Live || S->Parent) + auto add = [&](InputSectionBase *s) { + if (!s->isLive() || s->parent) return; - StringRef Name = getOutputSectionName(S); + StringRef name = getOutputSectionName(s); - if (Config->OrphanHandling == OrphanHandlingPolicy::Error) - error(toString(S) + " is being placed in '" + Name + "'"); - else if (Config->OrphanHandling == OrphanHandlingPolicy::Warn) - warn(toString(S) + " is being placed in '" + Name + "'"); + if (config->orphanHandling == OrphanHandlingPolicy::Error) + error(toString(s) + " is being placed in '" + name + "'"); + else if (config->orphanHandling == OrphanHandlingPolicy::Warn) + warn(toString(s) + " is being placed in '" + name + "'"); - if (OutputSection *Sec = - findByName(makeArrayRef(SectionCommands).slice(0, End), Name)) { - Sec->addSection(cast(S)); + if (OutputSection *sec = findByName(sectionCommands, name)) { + sec->addSection(cast(s)); return; } - if (OutputSection *OS = addInputSec(Map, S, Name)) - V.push_back(OS); - assert(S->getOutputSection()->SectionIndex == UINT32_MAX); + if (OutputSection *os = addInputSec(map, s, name)) + v.push_back(os); + assert(s->getOutputSection()->sectionIndex == UINT32_MAX); }; // For futher --emit-reloc handling code we need target output section // to be created before we create relocation output section, so we want // to create target sections first. We do not want priority handling // for synthetic sections because them are special. - for (InputSectionBase *IS : InputSections) { - if (auto *Sec = dyn_cast(IS)) - if (InputSectionBase *Rel = Sec->getRelocatedSection()) - if (auto *RelIS = dyn_cast_or_null(Rel->Parent)) - Add(RelIS); - Add(IS); + for (InputSectionBase *isec : inputSections) { + if (auto *sec = dyn_cast(isec)) + if (InputSectionBase *rel = sec->getRelocatedSection()) + if (auto *relIS = dyn_cast_or_null(rel->parent)) + add(relIS); + add(isec); } // If no SECTIONS command was given, we should insert sections commands // before others, so that we can handle scripts which refers them, // for example: "foo = ABSOLUTE(ADDR(.text)));". // When SECTIONS command is present we just add all orphans to the end. - if (HasSectionsCommand) - SectionCommands.insert(SectionCommands.end(), V.begin(), V.end()); + if (hasSectionsCommand) + sectionCommands.insert(sectionCommands.end(), v.begin(), v.end()); else - SectionCommands.insert(SectionCommands.begin(), V.begin(), V.end()); + sectionCommands.insert(sectionCommands.begin(), v.begin(), v.end()); } -uint64_t LinkerScript::advance(uint64_t Size, unsigned Alignment) { - bool IsTbss = - (Ctx->OutSec->Flags & SHF_TLS) && Ctx->OutSec->Type == SHT_NOBITS; - uint64_t Start = IsTbss ? Dot + Ctx->ThreadBssOffset : Dot; - Start = alignTo(Start, Alignment); - uint64_t End = Start + Size; +uint64_t LinkerScript::advance(uint64_t size, unsigned alignment) { + bool isTbss = + (ctx->outSec->flags & SHF_TLS) && ctx->outSec->type == SHT_NOBITS; + uint64_t start = isTbss ? dot + ctx->threadBssOffset : dot; + start = alignTo(start, alignment); + uint64_t end = start + size; - if (IsTbss) - Ctx->ThreadBssOffset = End - Dot; + if (isTbss) + ctx->threadBssOffset = end - dot; else - Dot = End; - return End; + dot = end; + return end; } -void LinkerScript::output(InputSection *S) { - assert(Ctx->OutSec == S->getParent()); - uint64_t Before = advance(0, 1); - uint64_t Pos = advance(S->getSize(), S->Alignment); - S->OutSecOff = Pos - S->getSize() - Ctx->OutSec->Addr; +void LinkerScript::output(InputSection *s) { + assert(ctx->outSec == s->getParent()); + uint64_t before = advance(0, 1); + uint64_t pos = advance(s->getSize(), s->alignment); + s->outSecOff = pos - s->getSize() - ctx->outSec->addr; // Update output section size after adding each section. This is so that // SIZEOF works correctly in the case below: // .foo { *(.aaa) a = SIZEOF(.foo); *(.bbb) } - expandOutputSection(Pos - Before); + expandOutputSection(pos - before); } -void LinkerScript::switchTo(OutputSection *Sec) { - Ctx->OutSec = Sec; +void LinkerScript::switchTo(OutputSection *sec) { + ctx->outSec = sec; - uint64_t Before = advance(0, 1); - Ctx->OutSec->Addr = advance(0, Ctx->OutSec->Alignment); - expandMemoryRegions(Ctx->OutSec->Addr - Before); + uint64_t before = advance(0, 1); + ctx->outSec->addr = advance(0, ctx->outSec->alignment); + expandMemoryRegions(ctx->outSec->addr - before); } // This function searches for a memory region to place the given output // section in. If found, a pointer to the appropriate memory region is // returned. Otherwise, a nullptr is returned. -MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *Sec) { +MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *sec) { // If a memory region name was specified in the output section command, // then try to find that region first. - if (!Sec->MemoryRegionName.empty()) { - if (MemoryRegion *M = MemoryRegions.lookup(Sec->MemoryRegionName)) - return M; - error("memory region '" + Sec->MemoryRegionName + "' not declared"); + if (!sec->memoryRegionName.empty()) { + if (MemoryRegion *m = memoryRegions.lookup(sec->memoryRegionName)) + return m; + error("memory region '" + sec->memoryRegionName + "' not declared"); return nullptr; } // If at least one memory region is defined, all sections must // belong to some memory region. Otherwise, we don't need to do // anything for memory regions. - if (MemoryRegions.empty()) + if (memoryRegions.empty()) return nullptr; // See if a region can be found by matching section flags. - for (auto &Pair : MemoryRegions) { - MemoryRegion *M = Pair.second; - if ((M->Flags & Sec->Flags) && (M->NegFlags & Sec->Flags) == 0) - return M; + for (auto &pair : memoryRegions) { + MemoryRegion *m = pair.second; + if ((m->flags & sec->flags) && (m->negFlags & sec->flags) == 0) + return m; } // Otherwise, no suitable region was found. - if (Sec->Flags & SHF_ALLOC) - error("no memory region specified for section '" + Sec->Name + "'"); + if (sec->flags & SHF_ALLOC) + error("no memory region specified for section '" + sec->name + "'"); return nullptr; } -static OutputSection *findFirstSection(PhdrEntry *Load) { - for (OutputSection *Sec : OutputSections) - if (Sec->PtLoad == Load) - return Sec; +static OutputSection *findFirstSection(PhdrEntry *load) { + for (OutputSection *sec : outputSections) + if (sec->ptLoad == load) + return sec; return nullptr; } // This function assigns offsets to input sections and an output section // for a single sections command (e.g. ".text { *(.text); }"). -void LinkerScript::assignOffsets(OutputSection *Sec) { - if (!(Sec->Flags & SHF_ALLOC)) - Dot = 0; - else if (Sec->AddrExpr) - setDot(Sec->AddrExpr, Sec->Location, false); +void LinkerScript::assignOffsets(OutputSection *sec) { + if (!(sec->flags & SHF_ALLOC)) + dot = 0; - Ctx->MemRegion = Sec->MemRegion; - Ctx->LMARegion = Sec->LMARegion; - if (Ctx->MemRegion) - Dot = Ctx->MemRegion->CurPos; + ctx->memRegion = sec->memRegion; + ctx->lmaRegion = sec->lmaRegion; + if (ctx->memRegion) + dot = ctx->memRegion->curPos; - switchTo(Sec); + if ((sec->flags & SHF_ALLOC) && sec->addrExpr) + setDot(sec->addrExpr, sec->location, false); - if (Sec->LMAExpr) - Ctx->LMAOffset = Sec->LMAExpr().getValue() - Dot; + switchTo(sec); - if (MemoryRegion *MR = Sec->LMARegion) - Ctx->LMAOffset = MR->CurPos - Dot; + if (sec->lmaExpr) + ctx->lmaOffset = sec->lmaExpr().getValue() - dot; + + if (MemoryRegion *mr = sec->lmaRegion) + ctx->lmaOffset = mr->curPos - dot; // If neither AT nor AT> is specified for an allocatable section, the linker // will set the LMA such that the difference between VMA and LMA for the @@ -785,62 +786,71 @@ void LinkerScript::assignOffsets(OutputSection *Sec) { // https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html // This, however, should only be done by the first "non-header" section // in the segment. - if (PhdrEntry *L = Ctx->OutSec->PtLoad) - if (Sec == findFirstSection(L)) - L->LMAOffset = Ctx->LMAOffset; + if (PhdrEntry *l = ctx->outSec->ptLoad) + if (sec == findFirstSection(l)) + l->lmaOffset = ctx->lmaOffset; // We can call this method multiple times during the creation of // thunks and want to start over calculation each time. - Sec->Size = 0; + sec->size = 0; // We visited SectionsCommands from processSectionCommands to // layout sections. Now, we visit SectionsCommands again to fix // section offsets. - for (BaseCommand *Base : Sec->SectionCommands) { + for (BaseCommand *base : sec->sectionCommands) { // This handles the assignments to symbol or to the dot. - if (auto *Cmd = dyn_cast(Base)) { - Cmd->Addr = Dot; - assignSymbol(Cmd, true); - Cmd->Size = Dot - Cmd->Addr; + if (auto *cmd = dyn_cast(base)) { + cmd->addr = dot; + assignSymbol(cmd, true); + cmd->size = dot - cmd->addr; continue; } // Handle BYTE(), SHORT(), LONG(), or QUAD(). - if (auto *Cmd = dyn_cast(Base)) { - Cmd->Offset = Dot - Ctx->OutSec->Addr; - Dot += Cmd->Size; - expandOutputSection(Cmd->Size); + if (auto *cmd = dyn_cast(base)) { + cmd->offset = dot - ctx->outSec->addr; + dot += cmd->size; + expandOutputSection(cmd->size); continue; } // Handle a single input section description command. // It calculates and assigns the offsets for each section and also // updates the output section size. - for (InputSection *Sec : cast(Base)->Sections) - output(Sec); + for (InputSection *sec : cast(base)->sections) + output(sec); } } -static bool isDiscardable(OutputSection &Sec) { +static bool isDiscardable(OutputSection &sec) { + if (sec.name == "/DISCARD/") + return true; + // We do not remove empty sections that are explicitly // assigned to any segment. - if (!Sec.Phdrs.empty()) + if (!sec.phdrs.empty()) return false; - // We do not want to remove sections that reference symbols in address and - // other expressions. We add script symbols as undefined, and want to ensure - // all of them are defined in the output, hence have to keep them. - if (Sec.ExpressionsUseSymbols) + // We do not want to remove OutputSections with expressions that reference + // symbols even if the OutputSection is empty. We want to ensure that the + // expressions can be evaluated and report an error if they cannot. + if (sec.expressionsUseSymbols) return false; - for (BaseCommand *Base : Sec.SectionCommands) { - if (auto Cmd = dyn_cast(Base)) + // OutputSections may be referenced by name in ADDR and LOADADDR expressions, + // as an empty Section can has a valid VMA and LMA we keep the OutputSection + // to maintain the integrity of the other Expression. + if (sec.usedInExpression) + return false; + + for (BaseCommand *base : sec.sectionCommands) { + if (auto cmd = dyn_cast(base)) // Don't create empty output sections just for unreferenced PROVIDE // symbols. - if (Cmd->Name != "." && !Cmd->Sym) + if (cmd->name != "." && !cmd->sym) continue; - if (!isa(*Base)) + if (!isa(*base)) return false; } return true; @@ -867,33 +877,35 @@ void LinkerScript::adjustSectionsBeforeSorting() { // The other option is to pick flags that minimize the impact the section // will have on the rest of the linker. That is why we copy the flags from // the previous sections. Only a few flags are needed to keep the impact low. - uint64_t Flags = SHF_ALLOC; + uint64_t flags = SHF_ALLOC; - for (BaseCommand *&Cmd : SectionCommands) { - auto *Sec = dyn_cast(Cmd); - if (!Sec) + for (BaseCommand *&cmd : sectionCommands) { + auto *sec = dyn_cast(cmd); + if (!sec) continue; // Handle align (e.g. ".foo : ALIGN(16) { ... }"). - if (Sec->AlignExpr) - Sec->Alignment = - std::max(Sec->Alignment, Sec->AlignExpr().getValue()); + if (sec->alignExpr) + sec->alignment = + std::max(sec->alignment, sec->alignExpr().getValue()); - // A live output section means that some input section was added to it. It - // might have been removed (if it was empty synthetic section), but we at - // least know the flags. - if (Sec->Live) - Flags = Sec->Flags; + // The input section might have been removed (if it was an empty synthetic + // section), but we at least know the flags. + if (sec->hasInputSections) + flags = sec->flags; // We do not want to keep any special flags for output section // in case it is empty. - bool IsEmpty = getInputSections(Sec).empty(); - if (IsEmpty) - Sec->Flags = Flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR); - - if (IsEmpty && isDiscardable(*Sec)) { - Sec->Live = false; - Cmd = nullptr; + bool isEmpty = getInputSections(sec).empty(); + if (isEmpty) + sec->flags = flags & ((sec->nonAlloc ? 0 : (uint64_t)SHF_ALLOC) | + SHF_WRITE | SHF_EXECINSTR); + + if (isEmpty && isDiscardable(*sec)) { + sec->markDead(); + cmd = nullptr; + } else if (!sec->isLive()) { + sec->markLive(); } } @@ -903,20 +915,20 @@ void LinkerScript::adjustSectionsBeforeSorting() { // clutter the output. // We instead remove trivially empty sections. The bfd linker seems even // more aggressive at removing them. - llvm::erase_if(SectionCommands, [&](BaseCommand *Base) { return !Base; }); + llvm::erase_if(sectionCommands, [&](BaseCommand *base) { return !base; }); } void LinkerScript::adjustSectionsAfterSorting() { // Try and find an appropriate memory region to assign offsets in. - for (BaseCommand *Base : SectionCommands) { - if (auto *Sec = dyn_cast(Base)) { - if (!Sec->LMARegionName.empty()) { - if (MemoryRegion *M = MemoryRegions.lookup(Sec->LMARegionName)) - Sec->LMARegion = M; + for (BaseCommand *base : sectionCommands) { + if (auto *sec = dyn_cast(base)) { + if (!sec->lmaRegionName.empty()) { + if (MemoryRegion *m = memoryRegions.lookup(sec->lmaRegionName)) + sec->lmaRegion = m; else - error("memory region '" + Sec->LMARegionName + "' not declared"); + error("memory region '" + sec->lmaRegionName + "' not declared"); } - Sec->MemRegion = findMemoryRegion(Sec); + sec->memRegion = findMemoryRegion(sec); } } @@ -926,38 +938,38 @@ void LinkerScript::adjustSectionsAfterSorting() { // Below is an example of such linker script: // PHDRS { seg PT_LOAD; } // SECTIONS { .aaa : { *(.aaa) } } - std::vector DefPhdrs; - auto FirstPtLoad = llvm::find_if(PhdrsCommands, [](const PhdrsCommand &Cmd) { - return Cmd.Type == PT_LOAD; + std::vector defPhdrs; + auto firstPtLoad = llvm::find_if(phdrsCommands, [](const PhdrsCommand &cmd) { + return cmd.type == PT_LOAD; }); - if (FirstPtLoad != PhdrsCommands.end()) - DefPhdrs.push_back(FirstPtLoad->Name); + if (firstPtLoad != phdrsCommands.end()) + defPhdrs.push_back(firstPtLoad->name); // Walk the commands and propagate the program headers to commands that don't // explicitly specify them. - for (BaseCommand *Base : SectionCommands) { - auto *Sec = dyn_cast(Base); - if (!Sec) + for (BaseCommand *base : sectionCommands) { + auto *sec = dyn_cast(base); + if (!sec) continue; - if (Sec->Phdrs.empty()) { + if (sec->phdrs.empty()) { // To match the bfd linker script behaviour, only propagate program // headers to sections that are allocated. - if (Sec->Flags & SHF_ALLOC) - Sec->Phdrs = DefPhdrs; + if (sec->flags & SHF_ALLOC) + sec->phdrs = defPhdrs; } else { - DefPhdrs = Sec->Phdrs; + defPhdrs = sec->phdrs; } } } -static uint64_t computeBase(uint64_t Min, bool AllocateHeaders) { +static uint64_t computeBase(uint64_t min, bool allocateHeaders) { // If there is no SECTIONS or if the linkerscript is explicit about program // headers, do our best to allocate them. - if (!Script->HasSectionsCommand || AllocateHeaders) + if (!script->hasSectionsCommand || allocateHeaders) return 0; // Otherwise only allocate program headers if that would not add a page. - return alignDown(Min, Config->MaxPageSize); + return alignDown(min, config->maxPageSize); } // Try to find an address for the file and program headers output sections, @@ -971,118 +983,120 @@ static uint64_t computeBase(uint64_t Min, bool AllocateHeaders) { // // If there isn't enough space for these sections, we'll remove them from the // PT_LOAD segment, and we'll also remove the PT_PHDR segment. -void LinkerScript::allocateHeaders(std::vector &Phdrs) { - uint64_t Min = std::numeric_limits::max(); - for (OutputSection *Sec : OutputSections) - if (Sec->Flags & SHF_ALLOC) - Min = std::min(Min, Sec->Addr); - - auto It = llvm::find_if( - Phdrs, [](const PhdrEntry *E) { return E->p_type == PT_LOAD; }); - if (It == Phdrs.end()) +void LinkerScript::allocateHeaders(std::vector &phdrs) { + uint64_t min = std::numeric_limits::max(); + for (OutputSection *sec : outputSections) + if (sec->flags & SHF_ALLOC) + min = std::min(min, sec->addr); + + auto it = llvm::find_if( + phdrs, [](const PhdrEntry *e) { return e->p_type == PT_LOAD; }); + if (it == phdrs.end()) return; - PhdrEntry *FirstPTLoad = *It; + PhdrEntry *firstPTLoad = *it; - bool HasExplicitHeaders = - llvm::any_of(PhdrsCommands, [](const PhdrsCommand &Cmd) { - return Cmd.HasPhdrs || Cmd.HasFilehdr; + bool hasExplicitHeaders = + llvm::any_of(phdrsCommands, [](const PhdrsCommand &cmd) { + return cmd.hasPhdrs || cmd.hasFilehdr; }); - uint64_t HeaderSize = getHeaderSize(); - if (HeaderSize <= Min - computeBase(Min, HasExplicitHeaders)) { - Min = alignDown(Min - HeaderSize, Config->MaxPageSize); - Out::ElfHeader->Addr = Min; - Out::ProgramHeaders->Addr = Min + Out::ElfHeader->Size; + bool paged = !config->omagic && !config->nmagic; + uint64_t headerSize = getHeaderSize(); + if ((paged || hasExplicitHeaders) && + headerSize <= min - computeBase(min, hasExplicitHeaders)) { + min = alignDown(min - headerSize, config->maxPageSize); + Out::elfHeader->addr = min; + Out::programHeaders->addr = min + Out::elfHeader->size; return; } // Error if we were explicitly asked to allocate headers. - if (HasExplicitHeaders) + if (hasExplicitHeaders) error("could not allocate headers"); - Out::ElfHeader->PtLoad = nullptr; - Out::ProgramHeaders->PtLoad = nullptr; - FirstPTLoad->FirstSec = findFirstSection(FirstPTLoad); + Out::elfHeader->ptLoad = nullptr; + Out::programHeaders->ptLoad = nullptr; + firstPTLoad->firstSec = findFirstSection(firstPTLoad); - llvm::erase_if(Phdrs, - [](const PhdrEntry *E) { return E->p_type == PT_PHDR; }); + llvm::erase_if(phdrs, + [](const PhdrEntry *e) { return e->p_type == PT_PHDR; }); } LinkerScript::AddressState::AddressState() { - for (auto &MRI : Script->MemoryRegions) { - MemoryRegion *MR = MRI.second; - MR->CurPos = MR->Origin; + for (auto &mri : script->memoryRegions) { + MemoryRegion *mr = mri.second; + mr->curPos = mr->origin; } } static uint64_t getInitialDot() { // By default linker scripts use an initial value of 0 for '.', // but prefer -image-base if set. - if (Script->HasSectionsCommand) - return Config->ImageBase ? *Config->ImageBase : 0; + if (script->hasSectionsCommand) + return config->imageBase ? *config->imageBase : 0; - uint64_t StartAddr = UINT64_MAX; - // The Sections with -T
have been sorted in order of ascending - // address. We must lower StartAddr if the lowest -T
as + uint64_t startAddr = UINT64_MAX; + // The sections with -T
have been sorted in order of ascending + // address. We must lower startAddr if the lowest -T
as // calls to setDot() must be monotonically increasing. - for (auto &KV : Config->SectionStartMap) - StartAddr = std::min(StartAddr, KV.second); - return std::min(StartAddr, Target->getImageBase() + elf::getHeaderSize()); + for (auto &kv : config->sectionStartMap) + startAddr = std::min(startAddr, kv.second); + return std::min(startAddr, target->getImageBase() + elf::getHeaderSize()); } // Here we assign addresses as instructed by linker script SECTIONS // sub-commands. Doing that allows us to use final VA values, so here // we also handle rest commands like symbol assignments and ASSERTs. void LinkerScript::assignAddresses() { - Dot = getInitialDot(); - - auto Deleter = make_unique(); - Ctx = Deleter.get(); - ErrorOnMissingSection = true; - switchTo(Aether); - - for (BaseCommand *Base : SectionCommands) { - if (auto *Cmd = dyn_cast(Base)) { - Cmd->Addr = Dot; - assignSymbol(Cmd, false); - Cmd->Size = Dot - Cmd->Addr; + dot = getInitialDot(); + + auto deleter = make_unique(); + ctx = deleter.get(); + errorOnMissingSection = true; + switchTo(aether); + + for (BaseCommand *base : sectionCommands) { + if (auto *cmd = dyn_cast(base)) { + cmd->addr = dot; + assignSymbol(cmd, false); + cmd->size = dot - cmd->addr; continue; } - assignOffsets(cast(Base)); + assignOffsets(cast(base)); } - Ctx = nullptr; + ctx = nullptr; } // Creates program headers as instructed by PHDRS linker script command. std::vector LinkerScript::createPhdrs() { - std::vector Ret; + std::vector ret; // Process PHDRS and FILEHDR keywords because they are not // real output sections and cannot be added in the following loop. - for (const PhdrsCommand &Cmd : PhdrsCommands) { - PhdrEntry *Phdr = make(Cmd.Type, Cmd.Flags ? *Cmd.Flags : PF_R); + for (const PhdrsCommand &cmd : phdrsCommands) { + PhdrEntry *phdr = make(cmd.type, cmd.flags ? *cmd.flags : PF_R); - if (Cmd.HasFilehdr) - Phdr->add(Out::ElfHeader); - if (Cmd.HasPhdrs) - Phdr->add(Out::ProgramHeaders); + if (cmd.hasFilehdr) + phdr->add(Out::elfHeader); + if (cmd.hasPhdrs) + phdr->add(Out::programHeaders); - if (Cmd.LMAExpr) { - Phdr->p_paddr = Cmd.LMAExpr().getValue(); - Phdr->HasLMA = true; + if (cmd.lmaExpr) { + phdr->p_paddr = cmd.lmaExpr().getValue(); + phdr->hasLMA = true; } - Ret.push_back(Phdr); + ret.push_back(phdr); } // Add output sections to program headers. - for (OutputSection *Sec : OutputSections) { + for (OutputSection *sec : outputSections) { // Assign headers specified by linker script - for (size_t Id : getPhdrIndices(Sec)) { - Ret[Id]->add(Sec); - if (!PhdrsCommands[Id].Flags.hasValue()) - Ret[Id]->p_flags |= Sec->getPhdrFlags(); + for (size_t id : getPhdrIndices(sec)) { + ret[id]->add(sec); + if (!phdrsCommands[id].flags.hasValue()) + ret[id]->p_flags |= sec->getPhdrFlags(); } } - return Ret; + return ret; } // Returns true if we should emit an .interp section. @@ -1091,54 +1105,54 @@ std::vector LinkerScript::createPhdrs() { // no PT_INTERP is there, there's no place to emit an // .interp, so we don't do that in that case. bool LinkerScript::needsInterpSection() { - if (PhdrsCommands.empty()) + if (phdrsCommands.empty()) return true; - for (PhdrsCommand &Cmd : PhdrsCommands) - if (Cmd.Type == PT_INTERP) + for (PhdrsCommand &cmd : phdrsCommands) + if (cmd.type == PT_INTERP) return true; return false; } -ExprValue LinkerScript::getSymbolValue(StringRef Name, const Twine &Loc) { - if (Name == ".") { - if (Ctx) - return {Ctx->OutSec, false, Dot - Ctx->OutSec->Addr, Loc}; - error(Loc + ": unable to get location counter value"); +ExprValue LinkerScript::getSymbolValue(StringRef name, const Twine &loc) { + if (name == ".") { + if (ctx) + return {ctx->outSec, false, dot - ctx->outSec->addr, loc}; + error(loc + ": unable to get location counter value"); return 0; } - if (Symbol *Sym = Symtab->find(Name)) { - if (auto *DS = dyn_cast(Sym)) - return {DS->Section, false, DS->Value, Loc}; - if (isa(Sym)) - if (!ErrorOnMissingSection) - return {nullptr, false, 0, Loc}; + if (Symbol *sym = symtab->find(name)) { + if (auto *ds = dyn_cast(sym)) + return {ds->section, false, ds->value, loc}; + if (isa(sym)) + if (!errorOnMissingSection) + return {nullptr, false, 0, loc}; } - error(Loc + ": symbol not found: " + Name); + error(loc + ": symbol not found: " + name); return 0; } // Returns the index of the segment named Name. -static Optional getPhdrIndex(ArrayRef Vec, - StringRef Name) { - for (size_t I = 0; I < Vec.size(); ++I) - if (Vec[I].Name == Name) - return I; +static Optional getPhdrIndex(ArrayRef vec, + StringRef name) { + for (size_t i = 0; i < vec.size(); ++i) + if (vec[i].name == name) + return i; return None; } // Returns indices of ELF headers containing specific section. Each index is a // zero based number of ELF header listed within PHDRS {} script block. -std::vector LinkerScript::getPhdrIndices(OutputSection *Cmd) { - std::vector Ret; - - for (StringRef S : Cmd->Phdrs) { - if (Optional Idx = getPhdrIndex(PhdrsCommands, S)) - Ret.push_back(*Idx); - else if (S != "NONE") - error(Cmd->Location + ": section header '" + S + +std::vector LinkerScript::getPhdrIndices(OutputSection *cmd) { + std::vector ret; + + for (StringRef s : cmd->phdrs) { + if (Optional idx = getPhdrIndex(phdrsCommands, s)) + ret.push_back(*idx); + else if (s != "NONE") + error(cmd->location + ": section header '" + s + "' is not listed in PHDRS"); } - return Ret; + return ret; } diff --git a/ELF/LinkerScript.h b/ELF/LinkerScript.h index 5116198..9e9c08e 100644 --- a/ELF/LinkerScript.h +++ b/ELF/LinkerScript.h @@ -1,9 +1,8 @@ //===- LinkerScript.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 // //===----------------------------------------------------------------------===// @@ -32,7 +31,6 @@ namespace elf { class Defined; class InputSection; class InputSectionBase; -class InputSectionBase; class OutputSection; class SectionBase; class Symbol; @@ -40,35 +38,35 @@ class ThunkSection; // This represents an r-value in the linker script. struct ExprValue { - ExprValue(SectionBase *Sec, bool ForceAbsolute, uint64_t Val, - const Twine &Loc) - : Sec(Sec), ForceAbsolute(ForceAbsolute), Val(Val), Loc(Loc.str()) {} + ExprValue(SectionBase *sec, bool forceAbsolute, uint64_t val, + const Twine &loc) + : sec(sec), forceAbsolute(forceAbsolute), val(val), loc(loc.str()) {} - ExprValue(uint64_t Val) : ExprValue(nullptr, false, Val, "") {} + ExprValue(uint64_t val) : ExprValue(nullptr, false, val, "") {} - bool isAbsolute() const { return ForceAbsolute || Sec == nullptr; } + bool isAbsolute() const { return forceAbsolute || sec == nullptr; } uint64_t getValue() const; uint64_t getSecAddr() const; uint64_t getSectionOffset() const; // If a value is relative to a section, it has a non-null Sec. - SectionBase *Sec; + SectionBase *sec; // True if this expression is enclosed in ABSOLUTE(). // This flag affects the return value of getValue(). - bool ForceAbsolute; + bool forceAbsolute; - uint64_t Val; - uint64_t Alignment = 1; + uint64_t val; + uint64_t alignment = 1; // Original source location. Used for error messages. - std::string Loc; + std::string loc; }; // This represents an expression in the linker script. // ScriptParser::readExpr reads an expression and returns an Expr. // Later, we evaluate the expression by calling the function. -typedef std::function Expr; +using Expr = std::function; // This enum is used to implement linker script SECTIONS command. // https://sourceware.org/binutils/docs/ld/SECTIONS.html#SECTIONS @@ -80,42 +78,42 @@ enum SectionsCommandKind { }; struct BaseCommand { - BaseCommand(int K) : Kind(K) {} - int Kind; + BaseCommand(int k) : kind(k) {} + int kind; }; // This represents ". = " or " = ". struct SymbolAssignment : BaseCommand { - SymbolAssignment(StringRef Name, Expr E, std::string Loc) - : BaseCommand(AssignmentKind), Name(Name), Expression(E), Location(Loc) {} + SymbolAssignment(StringRef name, Expr e, std::string loc) + : BaseCommand(AssignmentKind), name(name), expression(e), location(loc) {} - static bool classof(const BaseCommand *C) { - return C->Kind == AssignmentKind; + static bool classof(const BaseCommand *c) { + return c->kind == AssignmentKind; } // The LHS of an expression. Name is either a symbol name or ".". - StringRef Name; - Defined *Sym = nullptr; + StringRef name; + Defined *sym = nullptr; // The RHS of an expression. - Expr Expression; + Expr expression; // Command attributes for PROVIDE, HIDDEN and PROVIDE_HIDDEN. - bool Provide = false; - bool Hidden = false; + bool provide = false; + bool hidden = false; // Holds file name and line number for error reporting. - std::string Location; + std::string location; // A string representation of this command. We use this for -Map. - std::string CommandString; + std::string commandString; // Address of this assignment command. - unsigned Addr; + unsigned addr; // Size of this assignment command. This is usually 0, but if // you move '.' this may be greater than 0. - unsigned Size; + unsigned size; }; // Linker scripts allow additional constraints to be put on ouput sections. @@ -128,83 +126,83 @@ enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite }; // target memory. Instances of the struct are created by parsing the // MEMORY command. struct MemoryRegion { - MemoryRegion(StringRef Name, uint64_t Origin, uint64_t Length, uint32_t Flags, - uint32_t NegFlags) - : Name(Name), Origin(Origin), Length(Length), Flags(Flags), - NegFlags(NegFlags) {} - - std::string Name; - uint64_t Origin; - uint64_t Length; - uint32_t Flags; - uint32_t NegFlags; - uint64_t CurPos = 0; + MemoryRegion(StringRef name, uint64_t origin, uint64_t length, uint32_t flags, + uint32_t negFlags) + : name(name), origin(origin), length(length), flags(flags), + negFlags(negFlags) {} + + std::string name; + uint64_t origin; + uint64_t length; + uint32_t flags; + uint32_t negFlags; + uint64_t curPos = 0; }; // This struct represents one section match pattern in SECTIONS() command. // It can optionally have negative match pattern for EXCLUDED_FILE command. // Also it may be surrounded with SORT() command, so contains sorting rules. struct SectionPattern { - SectionPattern(StringMatcher &&Pat1, StringMatcher &&Pat2) - : ExcludedFilePat(Pat1), SectionPat(Pat2), - SortOuter(SortSectionPolicy::Default), - SortInner(SortSectionPolicy::Default) {} - - StringMatcher ExcludedFilePat; - StringMatcher SectionPat; - SortSectionPolicy SortOuter; - SortSectionPolicy SortInner; + SectionPattern(StringMatcher &&pat1, StringMatcher &&pat2) + : excludedFilePat(pat1), sectionPat(pat2), + sortOuter(SortSectionPolicy::Default), + sortInner(SortSectionPolicy::Default) {} + + StringMatcher excludedFilePat; + StringMatcher sectionPat; + SortSectionPolicy sortOuter; + SortSectionPolicy sortInner; }; struct InputSectionDescription : BaseCommand { - InputSectionDescription(StringRef FilePattern) - : BaseCommand(InputSectionKind), FilePat(FilePattern) {} + InputSectionDescription(StringRef filePattern) + : BaseCommand(InputSectionKind), filePat(filePattern) {} - static bool classof(const BaseCommand *C) { - return C->Kind == InputSectionKind; + static bool classof(const BaseCommand *c) { + return c->kind == InputSectionKind; } - StringMatcher FilePat; + StringMatcher filePat; // Input sections that matches at least one of SectionPatterns // will be associated with this InputSectionDescription. - std::vector SectionPatterns; + std::vector sectionPatterns; - std::vector Sections; + std::vector sections; // Temporary record of synthetic ThunkSection instances and the pass that // they were created in. This is used to insert newly created ThunkSections // into Sections at the end of a createThunks() pass. - std::vector> ThunkSections; + std::vector> thunkSections; }; // Represents BYTE(), SHORT(), LONG(), or QUAD(). struct ByteCommand : BaseCommand { - ByteCommand(Expr E, unsigned Size, std::string CommandString) - : BaseCommand(ByteKind), CommandString(CommandString), Expression(E), - Size(Size) {} + ByteCommand(Expr e, unsigned size, std::string commandString) + : BaseCommand(ByteKind), commandString(commandString), expression(e), + size(size) {} - static bool classof(const BaseCommand *C) { return C->Kind == ByteKind; } + static bool classof(const BaseCommand *c) { return c->kind == ByteKind; } // Keeps string representing the command. Used for -Map" is perhaps better. - std::string CommandString; + std::string commandString; - Expr Expression; + Expr expression; // This is just an offset of this assignment command in the output section. - unsigned Offset; + unsigned offset; // Size of this data command. - unsigned Size; + unsigned size; }; struct PhdrsCommand { - StringRef Name; - unsigned Type = llvm::ELF::PT_NULL; - bool HasFilehdr = false; - bool HasPhdrs = false; - llvm::Optional Flags; - Expr LMAExpr = nullptr; + StringRef name; + unsigned type = llvm::ELF::PT_NULL; + bool hasFilehdr = false; + bool hasPhdrs = false; + llvm::Optional flags; + Expr lmaExpr = nullptr; }; class LinkerScript final { @@ -213,35 +211,35 @@ class LinkerScript final { // not be used outside of the scope of a call to the above functions. struct AddressState { AddressState(); - uint64_t ThreadBssOffset = 0; - OutputSection *OutSec = nullptr; - MemoryRegion *MemRegion = nullptr; - MemoryRegion *LMARegion = nullptr; - uint64_t LMAOffset = 0; + uint64_t threadBssOffset = 0; + OutputSection *outSec = nullptr; + MemoryRegion *memRegion = nullptr; + MemoryRegion *lmaRegion = nullptr; + uint64_t lmaOffset = 0; }; - llvm::DenseMap NameToOutputSection; + llvm::DenseMap nameToOutputSection; - void addSymbol(SymbolAssignment *Cmd); - void assignSymbol(SymbolAssignment *Cmd, bool InSec); - void setDot(Expr E, const Twine &Loc, bool InSec); - void expandOutputSection(uint64_t Size); - void expandMemoryRegions(uint64_t Size); + void addSymbol(SymbolAssignment *cmd); + void assignSymbol(SymbolAssignment *cmd, bool inSec); + void setDot(Expr e, const Twine &loc, bool inSec); + void expandOutputSection(uint64_t size); + void expandMemoryRegions(uint64_t size); std::vector computeInputSections(const InputSectionDescription *); - std::vector createInputSectionList(OutputSection &Cmd); + std::vector createInputSectionList(OutputSection &cmd); - std::vector getPhdrIndices(OutputSection *Sec); + std::vector getPhdrIndices(OutputSection *sec); - MemoryRegion *findMemoryRegion(OutputSection *Sec); + MemoryRegion *findMemoryRegion(OutputSection *sec); - void switchTo(OutputSection *Sec); - uint64_t advance(uint64_t Size, unsigned Align); - void output(InputSection *Sec); + void switchTo(OutputSection *sec); + uint64_t advance(uint64_t size, unsigned align); + void output(InputSection *sec); - void assignOffsets(OutputSection *Sec); + void assignOffsets(OutputSection *sec); // Ctx captures the local AddressState and makes it accessible // deliberately. This is needed as there are some cases where we cannot just @@ -249,21 +247,21 @@ class LinkerScript final { // script parser. // This should remain a plain pointer as its lifetime is smaller than // LinkerScript. - AddressState *Ctx = nullptr; + AddressState *ctx = nullptr; - OutputSection *Aether; + OutputSection *aether; - uint64_t Dot; + uint64_t dot; public: - OutputSection *createOutputSection(StringRef Name, StringRef Location); - OutputSection *getOrCreateOutputSection(StringRef Name); + OutputSection *createOutputSection(StringRef name, StringRef location); + OutputSection *getOrCreateOutputSection(StringRef name); - bool hasPhdrsCommands() { return !PhdrsCommands.empty(); } - uint64_t getDot() { return Dot; } - void discard(ArrayRef V); + bool hasPhdrsCommands() { return !phdrsCommands.empty(); } + uint64_t getDot() { return dot; } + void discard(ArrayRef v); - ExprValue getSymbolValue(StringRef Name, const Twine &Loc); + ExprValue getSymbolValue(StringRef name, const Twine &loc); void addOrphanSections(); void adjustSectionsBeforeSorting(); @@ -272,9 +270,9 @@ public: std::vector createPhdrs(); bool needsInterpSection(); - bool shouldKeep(InputSectionBase *S); + bool shouldKeep(InputSectionBase *s); void assignAddresses(); - void allocateHeaders(std::vector &Phdrs); + void allocateHeaders(std::vector &phdrs); void processSectionCommands(); void declareSymbols(); @@ -282,31 +280,31 @@ public: void processInsertCommands(); // SECTIONS command list. - std::vector SectionCommands; + std::vector sectionCommands; // PHDRS command list. - std::vector PhdrsCommands; + std::vector phdrsCommands; - bool HasSectionsCommand = false; - bool ErrorOnMissingSection = false; + bool hasSectionsCommand = false; + bool errorOnMissingSection = false; // List of section patterns specified with KEEP commands. They will // be kept even if they are unused and --gc-sections is specified. - std::vector KeptSections; + std::vector keptSections; // A map from memory region name to a memory region descriptor. - llvm::MapVector MemoryRegions; + llvm::MapVector memoryRegions; // A list of symbols referenced by the script. - std::vector ReferencedSymbols; + std::vector referencedSymbols; // Used to implement INSERT [AFTER|BEFORE]. Contains commands that need // to be inserted into SECTIONS commands list. - llvm::DenseMap> InsertAfterCommands; - llvm::DenseMap> InsertBeforeCommands; + llvm::DenseMap> insertAfterCommands; + llvm::DenseMap> insertBeforeCommands; }; -extern LinkerScript *Script; +extern LinkerScript *script; } // end namespace elf } // end namespace lld diff --git a/ELF/MapFile.cpp b/ELF/MapFile.cpp index b0dc620..a4a6238 100644 --- a/ELF/MapFile.cpp +++ b/ELF/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 // //===----------------------------------------------------------------------===// // @@ -38,69 +37,67 @@ using namespace llvm::object; using namespace lld; using namespace lld::elf; -typedef DenseMap> SymbolMapTy; +using SymbolMapTy = DenseMap>; -static const std::string Indent8 = " "; // 8 spaces -static const std::string Indent16 = " "; // 16 spaces +static const std::string indent8 = " "; // 8 spaces +static const std::string indent16 = " "; // 16 spaces // Print out the first three columns of a line. -static void writeHeader(raw_ostream &OS, uint64_t VMA, uint64_t LMA, - uint64_t Size, uint64_t Align) { - if (Config->Is64) - OS << format("%16llx %16llx %8llx %5lld ", VMA, LMA, Size, Align); +static void writeHeader(raw_ostream &os, uint64_t vma, uint64_t lma, + uint64_t size, uint64_t align) { + if (config->is64) + os << format("%16llx %16llx %8llx %5lld ", vma, lma, size, align); else - OS << format("%8llx %8llx %8llx %5lld ", VMA, LMA, Size, Align); + os << format("%8llx %8llx %8llx %5lld ", vma, lma, size, align); } // Returns a list of all symbols that we want to print out. static std::vector getSymbols() { - std::vector V; - for (InputFile *File : ObjectFiles) - for (Symbol *B : File->getSymbols()) - if (auto *DR = dyn_cast(B)) - if (!DR->isSection() && DR->Section && DR->Section->Live && - (DR->File == File || DR->NeedsPltAddr || DR->Section->Bss)) - V.push_back(DR); - return V; + std::vector v; + for (InputFile *file : objectFiles) + for (Symbol *b : file->getSymbols()) + if (auto *dr = dyn_cast(b)) + if (!dr->isSection() && dr->section && dr->section->isLive() && + (dr->file == file || dr->needsPltAddr || dr->section->bss)) + v.push_back(dr); + return v; } // Returns a map from sections to their symbols. -static SymbolMapTy getSectionSyms(ArrayRef Syms) { - SymbolMapTy Ret; - for (Defined *DR : Syms) - Ret[DR->Section].push_back(DR); +static SymbolMapTy getSectionSyms(ArrayRef syms) { + SymbolMapTy ret; + for (Defined *dr : syms) + ret[dr->section].push_back(dr); // Sort symbols by address. We want to print out symbols in the // order in the output file rather than the order they appeared // in the input files. - for (auto &It : Ret) { - SmallVectorImpl &V = It.second; - std::stable_sort(V.begin(), V.end(), [](Defined *A, Defined *B) { - return A->getVA() < B->getVA(); + for (auto &it : ret) + llvm::stable_sort(it.second, [](Defined *a, Defined *b) { + return a->getVA() < b->getVA(); }); - } - return Ret; + return ret; } // Construct a map from symbols to their stringified representations. // Demangling symbols (which is what toString() does) is slow, so // we do that in batch using parallel-for. static DenseMap -getSymbolStrings(ArrayRef Syms) { - std::vector Str(Syms.size()); - parallelForEachN(0, Syms.size(), [&](size_t I) { - raw_string_ostream OS(Str[I]); - OutputSection *OSec = Syms[I]->getOutputSection(); - uint64_t VMA = Syms[I]->getVA(); - uint64_t LMA = OSec ? OSec->getLMA() + VMA - OSec->getVA(0) : 0; - writeHeader(OS, VMA, LMA, Syms[I]->getSize(), 1); - OS << Indent16 << toString(*Syms[I]); +getSymbolStrings(ArrayRef syms) { + std::vector str(syms.size()); + parallelForEachN(0, syms.size(), [&](size_t i) { + raw_string_ostream os(str[i]); + OutputSection *osec = syms[i]->getOutputSection(); + uint64_t vma = syms[i]->getVA(); + uint64_t lma = osec ? osec->getLMA() + vma - osec->getVA(0) : 0; + writeHeader(os, vma, lma, syms[i]->getSize(), 1); + os << indent16 << toString(*syms[i]); }); - DenseMap Ret; - for (size_t I = 0, E = Syms.size(); I < E; ++I) - Ret[Syms[I]] = std::move(Str[I]); - return Ret; + DenseMap ret; + for (size_t i = 0, e = syms.size(); i < e; ++i) + ret[syms[i]] = std::move(str[i]); + return ret; } // Print .eh_frame contents. Since the section consists of EhSectionPieces, @@ -109,114 +106,115 @@ getSymbolStrings(ArrayRef Syms) { // .eh_frame tend to contain a lot of section pieces that are contiguous // both in input file and output file. Such pieces are squashed before // being displayed to make output compact. -static void printEhFrame(raw_ostream &OS, OutputSection *OSec) { - std::vector Pieces; +static void printEhFrame(raw_ostream &os, const EhFrameSection *sec) { + std::vector pieces; - auto Add = [&](const EhSectionPiece &P) { + auto add = [&](const EhSectionPiece &p) { // If P is adjacent to Last, squash the two. - if (!Pieces.empty()) { - EhSectionPiece &Last = Pieces.back(); - if (Last.Sec == P.Sec && Last.InputOff + Last.Size == P.InputOff && - Last.OutputOff + Last.Size == P.OutputOff) { - Last.Size += P.Size; + if (!pieces.empty()) { + EhSectionPiece &last = pieces.back(); + if (last.sec == p.sec && last.inputOff + last.size == p.inputOff && + last.outputOff + last.size == p.outputOff) { + last.size += p.size; return; } } - Pieces.push_back(P); + pieces.push_back(p); }; // Gather section pieces. - for (const CieRecord *Rec : In.EhFrame->getCieRecords()) { - Add(*Rec->Cie); - for (const EhSectionPiece *Fde : Rec->Fdes) - Add(*Fde); + for (const CieRecord *rec : sec->getCieRecords()) { + add(*rec->cie); + for (const EhSectionPiece *fde : rec->fdes) + add(*fde); } // Print out section pieces. - for (EhSectionPiece &P : Pieces) { - writeHeader(OS, OSec->Addr + P.OutputOff, OSec->getLMA() + P.OutputOff, - P.Size, 1); - OS << Indent8 << toString(P.Sec->File) << ":(" << P.Sec->Name << "+0x" - << Twine::utohexstr(P.InputOff) + ")\n"; + const OutputSection *osec = sec->getOutputSection(); + for (EhSectionPiece &p : pieces) { + writeHeader(os, osec->addr + p.outputOff, osec->getLMA() + p.outputOff, + p.size, 1); + os << indent8 << toString(p.sec->file) << ":(" << p.sec->name << "+0x" + << Twine::utohexstr(p.inputOff) + ")\n"; } } void elf::writeMapFile() { - if (Config->MapFile.empty()) + if (config->mapFile.empty()) return; // Open a map file for writing. - std::error_code EC; - raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None); - if (EC) { - error("cannot open " + Config->MapFile + ": " + EC.message()); + std::error_code ec; + raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None); + if (ec) { + error("cannot open " + config->mapFile + ": " + ec.message()); return; } // Collect symbol info that we want to print out. - std::vector Syms = getSymbols(); - SymbolMapTy SectionSyms = getSectionSyms(Syms); - DenseMap SymStr = getSymbolStrings(Syms); + std::vector syms = getSymbols(); + SymbolMapTy sectionSyms = getSectionSyms(syms); + DenseMap symStr = getSymbolStrings(syms); // Print out the header line. - int W = Config->Is64 ? 16 : 8; - OS << right_justify("VMA", W) << ' ' << right_justify("LMA", W) + int w = config->is64 ? 16 : 8; + os << right_justify("VMA", w) << ' ' << right_justify("LMA", w) << " Size Align Out In Symbol\n"; - OutputSection* OSec = nullptr; - for (BaseCommand *Base : Script->SectionCommands) { - if (auto *Cmd = dyn_cast(Base)) { - if (Cmd->Provide && !Cmd->Sym) + OutputSection* osec = nullptr; + for (BaseCommand *base : script->sectionCommands) { + if (auto *cmd = dyn_cast(base)) { + if (cmd->provide && !cmd->sym) continue; - uint64_t LMA = OSec ? OSec->getLMA() + Cmd->Addr - OSec->getVA(0) : 0; - writeHeader(OS, Cmd->Addr, LMA, Cmd->Size, 1); - OS << Cmd->CommandString << '\n'; + uint64_t lma = osec ? osec->getLMA() + cmd->addr - osec->getVA(0) : 0; + writeHeader(os, cmd->addr, lma, cmd->size, 1); + os << cmd->commandString << '\n'; continue; } - OSec = cast(Base); - writeHeader(OS, OSec->Addr, OSec->getLMA(), OSec->Size, OSec->Alignment); - OS << OSec->Name << '\n'; + osec = cast(base); + writeHeader(os, osec->addr, osec->getLMA(), osec->size, osec->alignment); + os << osec->name << '\n'; // Dump symbols for each input section. - for (BaseCommand *Base : OSec->SectionCommands) { - if (auto *ISD = dyn_cast(Base)) { - for (InputSection *IS : ISD->Sections) { - if (IS == In.EhFrame) { - printEhFrame(OS, OSec); + for (BaseCommand *base : osec->sectionCommands) { + if (auto *isd = dyn_cast(base)) { + for (InputSection *isec : isd->sections) { + if (auto *ehSec = dyn_cast(isec)) { + printEhFrame(os, ehSec); continue; } - writeHeader(OS, IS->getVA(0), OSec->getLMA() + IS->getOffset(0), - IS->getSize(), IS->Alignment); - OS << Indent8 << toString(IS) << '\n'; - for (Symbol *Sym : SectionSyms[IS]) - OS << SymStr[Sym] << '\n'; + writeHeader(os, isec->getVA(0), osec->getLMA() + isec->getOffset(0), + isec->getSize(), isec->alignment); + os << indent8 << toString(isec) << '\n'; + for (Symbol *sym : sectionSyms[isec]) + os << symStr[sym] << '\n'; } continue; } - if (auto *Cmd = dyn_cast(Base)) { - writeHeader(OS, OSec->Addr + Cmd->Offset, OSec->getLMA() + Cmd->Offset, - Cmd->Size, 1); - OS << Indent8 << Cmd->CommandString << '\n'; + if (auto *cmd = dyn_cast(base)) { + writeHeader(os, osec->addr + cmd->offset, osec->getLMA() + cmd->offset, + cmd->size, 1); + os << indent8 << cmd->commandString << '\n'; continue; } - if (auto *Cmd = dyn_cast(Base)) { - if (Cmd->Provide && !Cmd->Sym) + if (auto *cmd = dyn_cast(base)) { + if (cmd->provide && !cmd->sym) continue; - writeHeader(OS, Cmd->Addr, OSec->getLMA() + Cmd->Addr - OSec->getVA(0), - Cmd->Size, 1); - OS << Indent8 << Cmd->CommandString << '\n'; + writeHeader(os, cmd->addr, osec->getLMA() + cmd->addr - osec->getVA(0), + cmd->size, 1); + os << indent8 << cmd->commandString << '\n'; continue; } } } } -static void print(StringRef A, StringRef B) { - outs() << left_justify(A, 49) << " " << B << "\n"; +static void print(StringRef a, StringRef b) { + outs() << left_justify(a, 49) << " " << b << "\n"; } // Output a cross reference table to stdout. This is for --cref. @@ -231,18 +229,18 @@ static void print(StringRef A, StringRef B) { // In this case, strlen is defined by libc.so.6 and used by other two // files. void elf::writeCrossReferenceTable() { - if (!Config->Cref) + if (!config->cref) return; // Collect symbols and files. - MapVector> Map; - for (InputFile *File : ObjectFiles) { - for (Symbol *Sym : File->getSymbols()) { - if (isa(Sym)) - Map[Sym].insert(File); - if (auto *D = dyn_cast(Sym)) - if (!D->isLocal() && (!D->Section || D->Section->Live)) - Map[D].insert(File); + MapVector> map; + for (InputFile *file : objectFiles) { + for (Symbol *sym : file->getSymbols()) { + if (isa(sym)) + map[sym].insert(file); + if (auto *d = dyn_cast(sym)) + if (!d->isLocal() && (!d->section || d->section->isLive())) + map[d].insert(file); } } @@ -251,13 +249,13 @@ void elf::writeCrossReferenceTable() { print("Symbol", "File"); // Print out a table. - for (auto KV : Map) { - Symbol *Sym = KV.first; - SetVector &Files = KV.second; + for (auto kv : map) { + Symbol *sym = kv.first; + SetVector &files = kv.second; - print(toString(*Sym), toString(Sym->File)); - for (InputFile *File : Files) - if (File != Sym->File) - print("", toString(File)); + print(toString(*sym), toString(sym->file)); + for (InputFile *file : files) + if (file != sym->file) + print("", toString(file)); } } diff --git a/ELF/MapFile.h b/ELF/MapFile.h index 0282425..7e79389 100644 --- a/ELF/MapFile.h +++ b/ELF/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 // //===----------------------------------------------------------------------===// diff --git a/ELF/MarkLive.cpp b/ELF/MarkLive.cpp index 8d0ec09..36b847f 100644 --- a/ELF/MarkLive.cpp +++ b/ELF/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 // //===----------------------------------------------------------------------===// // @@ -26,6 +25,7 @@ #include "OutputSections.h" #include "SymbolTable.h" #include "Symbols.h" +#include "SyntheticSections.h" #include "Target.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" @@ -42,66 +42,79 @@ using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; +namespace { +template class MarkLive { +public: + MarkLive(unsigned partition) : partition(partition) {} + + void run(); + void moveToMain(); + +private: + void enqueue(InputSectionBase *sec, uint64_t offset); + void markSymbol(Symbol *sym); + void mark(); + + template + void resolveReloc(InputSectionBase &sec, RelTy &rel, bool isLSDA); + + template + void scanEhFrameSection(EhInputSection &eh, ArrayRef rels); + + // The index of the partition that we are currently processing. + unsigned partition; + + // A list of sections to visit. + SmallVector queue; + + // There are normally few input sections whose names are valid C + // identifiers, so we just store a std::vector instead of a multimap. + DenseMap> cNamedSections; +}; +} // namespace + template -static typename ELFT::uint getAddend(InputSectionBase &Sec, - const typename ELFT::Rel &Rel) { - return Target->getImplicitAddend(Sec.data().begin() + Rel.r_offset, - Rel.getType(Config->IsMips64EL)); +static uint64_t getAddend(InputSectionBase &sec, + const typename ELFT::Rel &rel) { + return target->getImplicitAddend(sec.data().begin() + rel.r_offset, + rel.getType(config->isMips64EL)); } template -static typename ELFT::uint getAddend(InputSectionBase &Sec, - const typename ELFT::Rela &Rel) { - return Rel.r_addend; +static uint64_t getAddend(InputSectionBase &sec, + const typename ELFT::Rela &rel) { + return rel.r_addend; } -// There are normally few input sections whose names are valid C -// identifiers, so we just store a std::vector instead of a multimap. -static DenseMap> CNamedSections; - -template -static void -resolveReloc(InputSectionBase &Sec, RelT &Rel, - llvm::function_ref Fn) { - Symbol &B = Sec.getFile()->getRelocTargetSym(Rel); +template +template +void MarkLive::resolveReloc(InputSectionBase &sec, RelTy &rel, + bool isLSDA) { + Symbol &sym = sec.getFile()->getRelocTargetSym(rel); // If a symbol is referenced in a live section, it is used. - B.Used = true; - if (auto *SS = dyn_cast(&B)) - if (!SS->isWeak()) - SS->getFile().IsNeeded = true; - - if (auto *D = dyn_cast(&B)) { - auto *RelSec = dyn_cast_or_null(D->Section); - if (!RelSec) + sym.used = true; + + if (auto *d = dyn_cast(&sym)) { + auto *relSec = dyn_cast_or_null(d->section); + if (!relSec) return; - uint64_t Offset = D->Value; - if (D->isSection()) - Offset += getAddend(Sec, Rel); - Fn(RelSec, Offset); - return; - } - if (!B.isDefined()) - for (InputSectionBase *Sec : CNamedSections.lookup(B.getName())) - Fn(Sec, 0); -} + uint64_t offset = d->value; + if (d->isSection()) + offset += getAddend(sec, rel); -// Calls Fn for each section that Sec refers to via relocations. -template -static void -forEachSuccessor(InputSection &Sec, - llvm::function_ref Fn) { - if (Sec.AreRelocsRela) { - for (const typename ELFT::Rela &Rel : Sec.template relas()) - resolveReloc(Sec, Rel, Fn); - } else { - for (const typename ELFT::Rel &Rel : Sec.template rels()) - resolveReloc(Sec, Rel, Fn); + if (!isLSDA || !(relSec->flags & SHF_EXECINSTR)) + enqueue(relSec, offset); + return; } - for (InputSectionBase *IS : Sec.DependentSections) - Fn(IS, 0); + if (auto *ss = dyn_cast(&sym)) + if (!ss->isWeak()) + ss->getFile().isNeeded = true; + + for (InputSectionBase *sec : cNamedSections.lookup(sym.getName())) + enqueue(sec, 0); } // The .eh_frame section is an unfortunate special case. @@ -118,175 +131,203 @@ forEachSuccessor(InputSection &Sec, // A possible improvement would be to fully process .eh_frame in the middle of // the gc pass. With that we would be able to also gc some sections holding // LSDAs and personality functions if we found that they were unused. -template -static void -scanEhFrameSection(EhInputSection &EH, ArrayRef Rels, - llvm::function_ref Fn) { - const endianness E = ELFT::TargetEndianness; - - for (unsigned I = 0, N = EH.Pieces.size(); I < N; ++I) { - EhSectionPiece &Piece = EH.Pieces[I]; - unsigned FirstRelI = Piece.FirstRelocation; - if (FirstRelI == (unsigned)-1) +template +template +void MarkLive::scanEhFrameSection(EhInputSection &eh, + ArrayRef rels) { + for (size_t i = 0, end = eh.pieces.size(); i < end; ++i) { + EhSectionPiece &piece = eh.pieces[i]; + size_t firstRelI = piece.firstRelocation; + if (firstRelI == (unsigned)-1) continue; - if (read32(Piece.data().data() + 4) == 0) { + + if (read32(piece.data().data() + 4) == 0) { // This is a CIE, we only need to worry about the first relocation. It is // known to point to the personality function. - resolveReloc(EH, Rels[FirstRelI], Fn); + resolveReloc(eh, rels[firstRelI], false); continue; } + // This is a FDE. The relocations point to the described function or to // a LSDA. We only need to keep the LSDA alive, so ignore anything that // points to executable sections. - typename ELFT::uint PieceEnd = Piece.InputOff + Piece.Size; - for (unsigned I2 = FirstRelI, N2 = Rels.size(); I2 < N2; ++I2) { - const RelTy &Rel = Rels[I2]; - if (Rel.r_offset >= PieceEnd) - break; - resolveReloc(EH, Rels[I2], - [&](InputSectionBase *Sec, uint64_t Offset) { - if (Sec && Sec != &InputSection::Discarded && - !(Sec->Flags & SHF_EXECINSTR)) - Fn(Sec, 0); - }); - } + uint64_t pieceEnd = piece.inputOff + piece.size; + for (size_t j = firstRelI, end2 = rels.size(); j < end2; ++j) + if (rels[j].r_offset < pieceEnd) + resolveReloc(eh, rels[j], true); } } -template -static void -scanEhFrameSection(EhInputSection &EH, - llvm::function_ref Fn) { - if (!EH.NumRelocations) - return; - - if (EH.AreRelocsRela) - scanEhFrameSection(EH, EH.template relas(), Fn); - else - scanEhFrameSection(EH, EH.template rels(), Fn); -} - // Some sections are used directly by the loader, so they should never be // garbage-collected. This function returns true if a given section is such // section. -template static bool isReserved(InputSectionBase *Sec) { - switch (Sec->Type) { +static bool isReserved(InputSectionBase *sec) { + switch (sec->type) { case SHT_FINI_ARRAY: case SHT_INIT_ARRAY: case SHT_NOTE: case SHT_PREINIT_ARRAY: return true; default: - StringRef S = Sec->Name; - return S.startswith(".ctors") || S.startswith(".dtors") || - S.startswith(".init") || S.startswith(".fini") || - S.startswith(".jcr"); + StringRef s = sec->name; + return s.startswith(".ctors") || s.startswith(".dtors") || + s.startswith(".init") || s.startswith(".fini") || + s.startswith(".jcr"); } } -// This is the main function of the garbage collector. -// Starting from GC-root sections, this function visits all reachable -// sections to set their "Live" bits. -template static void doGcSections() { - SmallVector Q; - CNamedSections.clear(); - - auto Enqueue = [&](InputSectionBase *Sec, uint64_t Offset) { - // Skip over discarded sections. This in theory shouldn't happen, because - // the ELF spec doesn't allow a relocation to point to a deduplicated - // COMDAT section directly. Unfortunately this happens in practice (e.g. - // .eh_frame) so we need to add a check. - if (Sec == &InputSection::Discarded) - return; - +template +void MarkLive::enqueue(InputSectionBase *sec, uint64_t offset) { + // Skip over discarded sections. This in theory shouldn't happen, because + // the ELF spec doesn't allow a relocation to point to a deduplicated + // COMDAT section directly. Unfortunately this happens in practice (e.g. + // .eh_frame) so we need to add a check. + if (sec == &InputSection::discarded) + return; - // Usually, a whole section is marked as live or dead, but in mergeable - // (splittable) sections, each piece of data has independent liveness bit. - // So we explicitly tell it which offset is in use. - if (auto *MS = dyn_cast(Sec)) - MS->getSectionPiece(Offset)->Live = true; + // Usually, a whole section is marked as live or dead, but in mergeable + // (splittable) sections, each piece of data has independent liveness bit. + // So we explicitly tell it which offset is in use. + if (auto *ms = dyn_cast(sec)) + ms->getSectionPiece(offset)->live = true; - if (Sec->Live) - return; - Sec->Live = true; + // Set Sec->Partition to the meet (i.e. the "minimum") of Partition and + // Sec->Partition in the following lattice: 1 < other < 0. If Sec->Partition + // doesn't change, we don't need to do anything. + if (sec->partition == 1 || sec->partition == partition) + return; + sec->partition = sec->partition ? 1 : partition; - // Add input section to the queue. - if (InputSection *S = dyn_cast(Sec)) - Q.push_back(S); - }; + // Add input section to the queue. + if (InputSection *s = dyn_cast(sec)) + queue.push_back(s); +} - auto MarkSymbol = [&](Symbol *Sym) { - if (auto *D = dyn_cast_or_null(Sym)) - if (auto *IS = dyn_cast_or_null(D->Section)) - Enqueue(IS, D->Value); - }; +template void MarkLive::markSymbol(Symbol *sym) { + if (auto *d = dyn_cast_or_null(sym)) + if (auto *isec = dyn_cast_or_null(d->section)) + enqueue(isec, d->value); +} +// This is the main function of the garbage collector. +// Starting from GC-root sections, this function visits all reachable +// sections to set their "Live" bits. +template void MarkLive::run() { // Add GC root symbols. - MarkSymbol(Symtab->find(Config->Entry)); - MarkSymbol(Symtab->find(Config->Init)); - MarkSymbol(Symtab->find(Config->Fini)); - for (StringRef S : Config->Undefined) - MarkSymbol(Symtab->find(S)); - for (StringRef S : Script->ReferencedSymbols) - MarkSymbol(Symtab->find(S)); // Preserve externally-visible symbols if the symbols defined by this // file can interrupt other ELF file's symbols at runtime. - for (Symbol *S : Symtab->getSymbols()) - if (S->includeInDynsym()) - MarkSymbol(S); + symtab->forEachSymbol([&](Symbol *sym) { + if (sym->includeInDynsym() && sym->partition == partition) + markSymbol(sym); + }); + + // If this isn't the main partition, that's all that we need to preserve. + if (partition != 1) { + mark(); + return; + } + + markSymbol(symtab->find(config->entry)); + markSymbol(symtab->find(config->init)); + markSymbol(symtab->find(config->fini)); + for (StringRef s : config->undefined) + markSymbol(symtab->find(s)); + for (StringRef s : script->referencedSymbols) + markSymbol(symtab->find(s)); // Preserve special sections and those which are specified in linker // script KEEP command. - for (InputSectionBase *Sec : InputSections) { + for (InputSectionBase *sec : inputSections) { // Mark .eh_frame sections as live because there are usually no relocations // that point to .eh_frames. Otherwise, the garbage collector would drop // all of them. We also want to preserve personality routines and LSDA // referenced by .eh_frame sections, so we scan them for that here. - if (auto *EH = dyn_cast(Sec)) { - EH->Live = true; - scanEhFrameSection(*EH, Enqueue); + if (auto *eh = dyn_cast(sec)) { + eh->markLive(); + if (!eh->numRelocations) + continue; + + if (eh->areRelocsRela) + scanEhFrameSection(*eh, eh->template relas()); + else + scanEhFrameSection(*eh, eh->template rels()); } - if (Sec->Flags & SHF_LINK_ORDER) + if (sec->flags & SHF_LINK_ORDER) continue; - if (isReserved(Sec) || Script->shouldKeep(Sec)) { - Enqueue(Sec, 0); - } else if (isValidCIdentifier(Sec->Name)) { - CNamedSections[Saver.save("__start_" + Sec->Name)].push_back(Sec); - CNamedSections[Saver.save("__stop_" + Sec->Name)].push_back(Sec); + if (isReserved(sec) || script->shouldKeep(sec)) { + enqueue(sec, 0); + } else if (isValidCIdentifier(sec->name)) { + cNamedSections[saver.save("__start_" + sec->name)].push_back(sec); + cNamedSections[saver.save("__stop_" + sec->name)].push_back(sec); } } + mark(); +} + +template void MarkLive::mark() { // Mark all reachable sections. - while (!Q.empty()) - forEachSuccessor(*Q.pop_back_val(), Enqueue); + while (!queue.empty()) { + InputSectionBase &sec = *queue.pop_back_val(); + + if (sec.areRelocsRela) { + for (const typename ELFT::Rela &rel : sec.template relas()) + resolveReloc(sec, rel, false); + } else { + for (const typename ELFT::Rel &rel : sec.template rels()) + resolveReloc(sec, rel, false); + } + + for (InputSectionBase *isec : sec.dependentSections) + enqueue(isec, 0); + } +} + +// Move the sections for some symbols to the main partition, specifically ifuncs +// (because they can result in an IRELATIVE being added to the main partition's +// GOT, which means that the ifunc must be available when the main partition is +// loaded) and TLS symbols (because we only know how to correctly process TLS +// relocations for the main partition). +template void MarkLive::moveToMain() { + for (InputFile *file : objectFiles) + for (Symbol *s : file->getSymbols()) + if (auto *d = dyn_cast(s)) + if ((d->type == STT_GNU_IFUNC || d->type == STT_TLS) && d->section && + d->section->isLive()) + markSymbol(s); + + mark(); } // Before calling this function, Live bits are off for all // input sections. This function make some or all of them on // so that they are emitted to the output file. template void elf::markLive() { - if (!Config->GcSections) { - // If -gc-sections is missing, no sections are removed. - for (InputSectionBase *Sec : InputSections) - Sec->Live = true; + // If -gc-sections is not given, no sections are removed. + if (!config->gcSections) { + for (InputSectionBase *sec : inputSections) + sec->markLive(); // If a DSO defines a symbol referenced in a regular object, it is needed. - for (Symbol *Sym : Symtab->getSymbols()) - if (auto *S = dyn_cast(Sym)) - if (S->IsUsedInRegularObj && !S->isWeak()) - S->getFile().IsNeeded = true; + symtab->forEachSymbol([](Symbol *sym) { + if (auto *s = dyn_cast(sym)) + if (s->isUsedInRegularObj && !s->isWeak()) + s->getFile().isNeeded = true; + }); return; } + // Otheriwse, do mark-sweep GC. + // // The -gc-sections option works only for SHF_ALLOC sections // (sections that are memory-mapped at runtime). So we can // unconditionally make non-SHF_ALLOC sections alive except // SHF_LINK_ORDER and SHT_REL/SHT_RELA sections. // - // Usually, SHF_ALLOC sections are not removed even if they are + // Usually, non-SHF_ALLOC sections are not removed even if they are // unreachable through relocations because reachability is not // a good signal whether they are garbage or not (e.g. there is // usually no section referring to a .comment section, but we @@ -300,22 +341,30 @@ template void elf::markLive() { // or -emit-reloc were given. And they are subject of garbage // collection because, if we remove a text section, we also // remove its relocation section. - for (InputSectionBase *Sec : InputSections) { - bool IsAlloc = (Sec->Flags & SHF_ALLOC); - bool IsLinkOrder = (Sec->Flags & SHF_LINK_ORDER); - bool IsRel = (Sec->Type == SHT_REL || Sec->Type == SHT_RELA); - if (!IsAlloc && !IsLinkOrder && !IsRel) - Sec->Live = true; + for (InputSectionBase *sec : inputSections) { + bool isAlloc = (sec->flags & SHF_ALLOC); + bool isLinkOrder = (sec->flags & SHF_LINK_ORDER); + bool isRel = (sec->type == SHT_REL || sec->type == SHT_RELA); + + if (!isAlloc && !isLinkOrder && !isRel) + sec->markLive(); } // Follow the graph to mark all live sections. - doGcSections(); + for (unsigned curPart = 1; curPart <= partitions.size(); ++curPart) + MarkLive(curPart).run(); + + // If we have multiple partitions, some sections need to live in the main + // partition even if they were allocated to a loadable partition. Move them + // there now. + if (partitions.size() != 1) + MarkLive(1).moveToMain(); // Report garbage-collected sections. - if (Config->PrintGcSections) - for (InputSectionBase *Sec : InputSections) - if (!Sec->Live) - message("removing unused section " + toString(Sec)); + if (config->printGcSections) + for (InputSectionBase *sec : inputSections) + if (!sec->isLive()) + message("removing unused section " + toString(sec)); } template void elf::markLive(); diff --git a/ELF/MarkLive.h b/ELF/MarkLive.h index c9b99ad..63b5b26 100644 --- a/ELF/MarkLive.h +++ b/ELF/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 // //===----------------------------------------------------------------------===// diff --git a/ELF/Options.td b/ELF/Options.td index e43a21b..3ebb46f 100644 --- a/ELF/Options.td +++ b/ELF/Options.td @@ -30,7 +30,7 @@ def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">; def build_id: F<"build-id">, HelpText<"Alias for --build-id=fast">; def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">, - MetaVarName<"[fast,md5,sha,uuid,0x]">; + MetaVarName<"[fast,md5,sha1,uuid,0x]">; defm check_sections: B<"check-sections", "Check section addresses for overlaps (default)", @@ -63,10 +63,18 @@ defm allow_multiple_definition: B<"allow-multiple-definition", "Allow multiple definitions", "Do not allow multiple definitions (default)">; +defm allow_shlib_undefined: B<"allow-shlib-undefined", + "Allow unresolved references in shared libraries (default when linking a shared library)", + "Do not allow unresolved references in shared libraries (default when linking an executable)">; + defm apply_dynamic_relocs: B<"apply-dynamic-relocs", "Apply link-time values for dynamic relocations", "Do not apply link-time values for dynamic relocations (default)">; +defm dependent_libraries: B<"dependent-libraries", + "Process dependent library specifiers from input files (default)", + "Ignore dependent library specifiers from input files">; + defm as_needed: B<"as-needed", "Only set DT_NEEDED for shared libraries if used", "Always set DT_NEEDED for shared libraries (default)">; @@ -163,6 +171,13 @@ defm fini: Eq<"fini", "Specify a finalizer function">, MetaVarName<"">; def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">, HelpText<"Apply fixes for AArch64 Cortex-A53 erratum 843419">; +// This option is intentionally hidden from the user as the implementation +// is not complete. +def require_cet: F<"require-cet">; + +def force_bti: F<"force-bti">, + HelpText<"Force enable AArch64 BTI in PLT, warn if Input ELF file does not have GNU_PROPERTY_AARCH64_FEATURE_1_BTI property">; + defm format: Eq<"format", "Change the input format of the inputs following this option">, MetaVarName<"[default,elf,binary]">; @@ -214,6 +229,9 @@ defm merge_exidx_entries: B<"merge-exidx-entries", "Enable merging .ARM.exidx entries (default)", "Disable merging .ARM.exidx entries">; +def nmagic: F<"nmagic">, MetaVarName<"">, + HelpText<"Do not page align sections, link against static libraries.">; + def nostdlib: F<"nostdlib">, HelpText<"Only search directories specified on the command line">; @@ -226,8 +244,11 @@ def no_dynamic_linker: F<"no-dynamic-linker">, def noinhibit_exec: F<"noinhibit-exec">, HelpText<"Retain the executable output file whenever it is still usable">; +def no_nmagic: F<"no-nmagic">, MetaVarName<"">, + HelpText<"Page align sections (default)">; + def no_omagic: F<"no-omagic">, MetaVarName<"">, - HelpText<"Do not set the text data sections to be writable">; + HelpText<"Do not set the text data sections to be writable, page align sections (default)">; def no_rosegment: F<"no-rosegment">, HelpText<"Do not put read-only non-executable sections in their own segment">; @@ -242,7 +263,7 @@ def oformat: Separate<["--"], "oformat">, MetaVarName<"">, HelpText<"Specify the binary format for the output object file">; def omagic: Flag<["--"], "omagic">, MetaVarName<"">, - HelpText<"Set the text and data sections to be readable and writable">; + HelpText<"Set the text and data sections to be readable and writable, do not page align sections, link against static libraries">; defm orphan_handling: Eq<"orphan-handling", "Control how orphan sections are handled when linker script used">; @@ -251,10 +272,16 @@ defm pack_dyn_relocs: Eq<"pack-dyn-relocs", "Pack dynamic relocations in the given format">, MetaVarName<"[none,android,relr,android+relr]">; +def pac_plt: F<"pac-plt">, + HelpText<"AArch64 only, use pointer authentication in PLT">; + defm use_android_relr_tags: B<"use-android-relr-tags", "Use SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*", "Use SHT_RELR / DT_RELR* tags (default)">; +def pic_veneer: F<"pic-veneer">, + HelpText<"Always generate position independent thunks (veneers)">; + defm pie: B<"pie", "Create a position independent executable", "Do not create a position independent executable (default)">; @@ -267,6 +294,9 @@ defm print_icf_sections: B<"print-icf-sections", "List identical folded sections", "Do not list identical folded sections (default)">; +defm print_symbol_order: Eq<"print-symbol-order", + "Print a symbol order specified by --call-graph-ordering-file into the speficied file">; + def pop_state: F<"pop-state">, HelpText<"Undo the effect of -push-state">; @@ -336,6 +366,9 @@ defm trace_symbol: Eq<"trace-symbol", "Trace references to symbols">; defm undefined: Eq<"undefined", "Force undefined symbol during linking">, MetaVarName<"">; +defm undefined_glob: Eq<"undefined-glob", "Force undefined symbol during linking">, + MetaVarName<"">; + defm unresolved_symbols: Eq<"unresolved-symbols", "Determine how to handle unresolved symbols">; @@ -383,6 +416,9 @@ defm wrap: Eq<"wrap", "Use wrapper functions for symbol">, def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"