Index: vendor/lld/dist-release_80/COFF/Chunks.cpp =================================================================== --- vendor/lld/dist-release_80/COFF/Chunks.cpp (revision 343801) +++ vendor/lld/dist-release_80/COFF/Chunks.cpp (revision 343802) @@ -1,863 +1,883 @@ //===- Chunks.cpp ---------------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "Chunks.h" #include "InputFiles.h" #include "Symbols.h" #include "Writer.h" #include "SymbolTable.h" #include "lld/Common/ErrorHandler.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::COFF; using llvm::support::ulittle32_t; namespace lld { namespace coff { SectionChunk::SectionChunk(ObjFile *F, const coff_section *H) : Chunk(SectionKind), Repl(this), Header(H), File(F), Relocs(File->getCOFFObj()->getRelocations(Header)) { // Initialize SectionName. File->getCOFFObj()->getSectionName(Header, SectionName); Alignment = Header->getAlignment(); // If linker GC is disabled, every chunk starts out alive. If linker GC is // enabled, treat non-comdat sections as roots. Generally optimized object // 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)); } // Reset RelocTargets to their original targets before thunks were added. void SectionChunk::resetRelocTargets() { for (size_t I = 0, E = Relocs.size(); I < E; ++I) RelocTargets[I] = File->getSymbol(Relocs[I].SymbolTableIndex); } static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); } static void add32(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); } static void add64(uint8_t *P, int64_t V) { write64le(P, read64le(P) + V); } static void or16(uint8_t *P, uint16_t V) { write16le(P, read16le(P) | V); } static void or32(uint8_t *P, uint32_t V) { write32le(P, read32le(P) | V); } // 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) return true; 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)) return; uint64_t SecRel = S - OS->getRVA(); if (SecRel > UINT32_MAX) { error("overflow in SECREL relocation in section: " + Sec->getSectionName()); return; } add32(Off, SecRel); } 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); 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; default: 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) { 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; default: 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 uint16_t readMOV(uint8_t *Off, bool MOVT) { uint16_t Op1 = read16le(Off); if ((Op1 & 0xfbf0) != (MOVT ? 0xf2c0 : 0xf240)) error("unexpected instruction in " + Twine(MOVT ? "MOVT" : "MOVW") + " instruction in MOV32T relocation"); uint16_t Op2 = read16le(Off + 2); if ((Op2 & 0x8000) != 0) error("unexpected instruction in " + Twine(MOVT ? "MOVT" : "MOVW") + " instruction in MOV32T relocation"); return (Op2 & 0x00ff) | ((Op2 >> 4) & 0x0700) | ((Op1 << 1) & 0x0800) | ((Op1 & 0x000f) << 12); } void applyMOV32T(uint8_t *Off, uint32_t V) { uint16_t ImmW = readMOV(Off, 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)) 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)); } 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)); // 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)); } 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; default: 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); } // 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)); } // Add the 12 bit page offset to the existing immediate. // Ldr/str instructions store the opcode immediate scaled // by the load/store size (giving a larger range for larger // loads/stores). The immediate is always (both before and after // fixing up the relocation) stored scaled similarly. // Even if larger loads/stores have a larger range, limit the // effective offset to 12 bit, since it is intended to be a // page offset. static void applyArm64Ldr(uint8_t *Off, uint64_t Imm) { uint32_t Orig = read32le(Off); uint32_t Size = Orig >> 30; // 0x04000000 indicates SIMD/FP registers // 0x00800000 indicates 128 bit if ((Orig & 0x4800000) == 0x4800000) Size += 4; if ((Imm & ((1 << Size) - 1)) != 0) error("misaligned ldr/str offset"); 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 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) { error("overflow in SECREL_HIGH12A relocation in section: " + Sec->getSectionName()); return; } 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); } void applyArm64Branch26(uint8_t *Off, int64_t V) { if (!isInt<28>(V)) error("relocation out of range"); or32(Off, (V & 0x0FFFFFFC) >> 2); } static void applyArm64Branch19(uint8_t *Off, int64_t V) { if (!isInt<21>(V)) error("relocation out of range"); or32(Off, (V & 0x001FFFFC) << 3); } 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; default: error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " + toString(File)); } } static void maybeReportRelocationToDiscarded(const SectionChunk *FromChunk, Defined *Sym, const coff_relocation &Rel) { // Don't report these errors when the relocation comes from a debug info // section or in mingw mode. MinGW mode object files (built by GCC) can // have leftover sections with relocations against discarded comdat // sections. Such sections are left as is, with relocations untouched. if (FromChunk->isCodeView() || FromChunk->isDWARF() || Config->MinGW) return; // Get the name of the symbol. If it's null, it was discarded early, so we // have to go back to the object file. ObjFile *File = FromChunk->File; StringRef Name; if (Sym) { Name = Sym->getName(); } else { COFFSymbolRef COFFSym = check(File->getCOFFObj()->getSymbol(Rel.SymbolTableIndex)); File->getCOFFObj()->getSymbolName(COFFSym, Name); } error("relocation against symbol in discarded section: " + Name + getSymbolLocations(File, Rel.SymbolTableIndex)); } void SectionChunk::writeTo(uint8_t *Buf) const { if (!hasData()) return; // Copy section contents from source object file to output file. ArrayRef A = getContents(); if (!A.empty()) memcpy(Buf + OutputSectionOff, 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]; // 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) { error("relocation points beyond the end of its parent section"); continue; } uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress; // Use the potentially remapped Symbol instead of the one that the // relocation points to. auto *Sym = dyn_cast_or_null(RelocTargets[I]); // Get the output section of the symbol for this relocation. The output // section is needed to compute SECREL and SECTION relocations used in debug // info. 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); continue; } uint64_t S = Sym->getRVA(); // Compute the RVA of the relocation for relative relocations. uint64_t P = RVA + Rel.VirtualAddress; switch (Config->Machine) { case AMD64: applyRelX64(Off, Rel.Type, OS, S, P); break; case I386: applyRelX86(Off, Rel.Type, OS, S, P); break; case ARMNT: applyRelARM(Off, Rel.Type, OS, S, P); break; case ARM64: applyRelARM64(Off, Rel.Type, OS, S, P); break; default: llvm_unreachable("unknown machine type"); } } } void SectionChunk::addAssociative(SectionChunk *Child) { AssocChildren.push_back(Child); } static uint8_t getBaserelType(const coff_relocation &Rel) { switch (Config->Machine) { case AMD64: 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) return IMAGE_REL_BASED_HIGHLOW; return IMAGE_REL_BASED_ABSOLUTE; case ARMNT: if (Rel.Type == IMAGE_REL_ARM_ADDR32) return IMAGE_REL_BASED_HIGHLOW; 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) return IMAGE_REL_BASED_DIR64; return IMAGE_REL_BASED_ABSOLUTE; default: llvm_unreachable("unknown machine type"); } } // Windows-specific. // Collect all locations that contain absolute addresses, which need to be // fixed by the loader if load-time relocation is needed. // Only called when base relocation is enabled. void SectionChunk::getBaserels(std::vector *Res) { for (size_t I = 0, E = Relocs.size(); I < E; I++) { const coff_relocation &Rel = Relocs[I]; uint8_t Ty = getBaserelType(Rel); if (Ty == IMAGE_REL_BASED_ABSOLUTE) continue; // Use the potentially remapped Symbol instead of the one that the // relocation points to. Symbol *Target = RelocTargets[I]; if (!Target || isa(Target)) continue; Res->emplace_back(RVA + Rel.VirtualAddress, Ty); } } // MinGW specific. // Check whether a static relocation of type Type can be deferred and // handled at runtime as a pseudo relocation (for references to a module // local variable, which turned out to actually need to be imported from // another DLL) This returns the size the relocation is supposed to update, // in bits, or 0 if the relocation cannot be handled as a runtime pseudo // relocation. static int getRuntimePseudoRelocSize(uint16_t Type) { // Relocations that either contain an absolute address, or a plain // relative offset, since the runtime pseudo reloc implementation // adds 8/16/32/64 bit values to a memory address. // // Given a pseudo relocation entry, // // typedef struct { // DWORD sym; // DWORD target; // DWORD flags; // } runtime_pseudo_reloc_item_v2; // // the runtime relocation performs this adjustment: // *(base + .target) += *(base + .sym) - (base + .sym) // // This works for both absolute addresses (IMAGE_REL_*_ADDR32/64, // IMAGE_REL_I386_DIR32, where the memory location initially contains // the address of the IAT slot, and for relative addresses (IMAGE_REL*_REL32), // where the memory location originally contains the relative offset to the // IAT slot. // // This requires the target address to be writable, either directly out of // the image, or temporarily changed at runtime with VirtualProtect. // Since this only operates on direct address values, it doesn't work for // ARM/ARM64 relocations, other than the plain ADDR32/ADDR64 relocations. switch (Config->Machine) { case AMD64: switch (Type) { case IMAGE_REL_AMD64_ADDR64: return 64; case IMAGE_REL_AMD64_ADDR32: case IMAGE_REL_AMD64_REL32: case IMAGE_REL_AMD64_REL32_1: case IMAGE_REL_AMD64_REL32_2: case IMAGE_REL_AMD64_REL32_3: case IMAGE_REL_AMD64_REL32_4: case IMAGE_REL_AMD64_REL32_5: return 32; default: return 0; } case I386: switch (Type) { case IMAGE_REL_I386_DIR32: case IMAGE_REL_I386_REL32: return 32; default: return 0; } case ARMNT: switch (Type) { case IMAGE_REL_ARM_ADDR32: return 32; default: return 0; } case ARM64: switch (Type) { case IMAGE_REL_ARM64_ADDR64: return 64; case IMAGE_REL_ARM64_ADDR32: return 32; default: return 0; } default: llvm_unreachable("unknown machine type"); } } // MinGW specific. // Append information to the provided vector about all relocations that // need to be handled at runtime as runtime pseudo relocations (references // to a module local variable, which turned out to actually need to be // imported from another DLL). void SectionChunk::getRuntimePseudoRelocs( std::vector &Res) { for (const coff_relocation &Rel : Relocs) { 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() + " with relocation type " + File->getCOFFObj()->getRelocationTypeName(Rel.Type) + " in " + toString(File)); continue; } // SizeInBits is used to initialize the Flags field; currently no // other flags are defined. Res.emplace_back( RuntimePseudoReloc(Target, this, Rel.VirtualAddress, SizeInBits)); } } bool SectionChunk::hasData() const { return !(Header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA); } uint32_t SectionChunk::getOutputCharacteristics() const { return Header->Characteristics & (PermMask | TypeMask); } bool SectionChunk::isCOMDAT() const { 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()); } StringRef SectionChunk::getDebugName() { if (Sym) return Sym->getName(); return ""; } ArrayRef SectionChunk::getContents() const { ArrayRef A; File->getCOFFObj()->getSectionContents(Header, A); return A; } void SectionChunk::replace(SectionChunk *Other) { Alignment = std::max(Alignment, Other->Alignment); 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; } CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) { // Common symbols are aligned on natural boundaries up to 32 bytes. // This is what MSVC link.exe does. Alignment = std::min(uint64_t(32), PowerOf2Ceil(Sym.getValue())); } uint32_t CommonChunk::getOutputCharacteristics() const { return IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE; } void StringChunk::writeTo(uint8_t *Buf) const { memcpy(Buf + OutputSectionOff, Str.data(), Str.size()); Buf[OutputSectionOff + Str.size()] = '\0'; } ImportThunkChunkX64::ImportThunkChunkX64(Defined *S) : ImpSymbol(S) { // Intel Optimization Manual says that all branch targets // should be 16-byte aligned. MSVC linker does this too. Alignment = 16; } void ImportThunkChunkX64::writeTo(uint8_t *Buf) const { memcpy(Buf + OutputSectionOff, ImportThunkX86, sizeof(ImportThunkX86)); // The first two bytes is a JMP instruction. Fill its operand. write32le(Buf + OutputSectionOff + 2, ImpSymbol->getRVA() - RVA - getSize()); } void ImportThunkChunkX86::getBaserels(std::vector *Res) { Res->emplace_back(getRVA() + 2); } void ImportThunkChunkX86::writeTo(uint8_t *Buf) const { memcpy(Buf + OutputSectionOff, ImportThunkX86, sizeof(ImportThunkX86)); // The first two bytes is a JMP instruction. Fill its operand. write32le(Buf + OutputSectionOff + 2, ImpSymbol->getRVA() + Config->ImageBase); } 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)); // Fix mov.w and mov.t operands. applyMOV32T(Buf + OutputSectionOff, 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); } // A Thumb2, PIC, non-interworking range extension thunk. const uint8_t ArmThunk[] = { 0x40, 0xf2, 0x00, 0x0c, // P: movw ip,:lower16:S - (P + (L1-P) + 4) 0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4) 0xe7, 0x44, // L1: add pc, ip }; -size_t RangeExtensionThunk::getSize() const { +size_t RangeExtensionThunkARM::getSize() const { assert(Config->Machine == ARMNT); return sizeof(ArmThunk); } -void RangeExtensionThunk::writeTo(uint8_t *Buf) const { +void RangeExtensionThunkARM::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)); +} + +// 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 RangeExtensionThunkARM64::writeTo(uint8_t *Buf) const { + assert(Config->Machine == ARM64); + memcpy(Buf + OutputSectionOff, Arm64Thunk, sizeof(Arm64Thunk)); + applyArm64Addr(Buf + OutputSectionOff + 0, Target->getRVA(), RVA, 12); + applyArm64Imm(Buf + OutputSectionOff + 4, Target->getRVA() & 0xfff, 0); } void LocalImportChunk::getBaserels(std::vector *Res) { Res->emplace_back(getRVA()); } size_t LocalImportChunk::getSize() const { return Config->Wordsize; } void LocalImportChunk::writeTo(uint8_t *Buf) const { if (Config->is64()) { write64le(Buf + OutputSectionOff, Sym->getRVA() + Config->ImageBase); } else { write32le(Buf + OutputSectionOff, 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 && "RVA tables should be de-duplicated"); } // MinGW specific, for the "automatic import of variables from DLLs" feature. size_t PseudoRelocTableChunk::getSize() const { if (Relocs.empty()) return 0; return 12 + 12 * Relocs.size(); } // MinGW specific. void PseudoRelocTableChunk::writeTo(uint8_t *Buf) const { if (Relocs.empty()) return; ulittle32_t *Table = reinterpret_cast(Buf + OutputSectionOff); // This is the list header, to signal the runtime pseudo relocation v2 // format. Table[0] = 0; Table[1] = 0; Table[2] = 1; size_t Idx = 3; for (const RuntimePseudoReloc &RPR : Relocs) { Table[Idx + 0] = RPR.Sym->getRVA(); Table[Idx + 1] = RPR.Target->getRVA() + RPR.TargetOffset; Table[Idx + 2] = RPR.Flags; Idx += 3; } } // Windows-specific. This class represents a block in .reloc section. // The format is described here. // // On Windows, each DLL is linked against a fixed base address and // usually loaded to that address. However, if there's already another // DLL that overlaps, the loader has to relocate it. To do that, DLLs // contain .reloc sections which contain offsets that need to be fixed // up at runtime. If the loader finds that a DLL cannot be loaded to its // desired base address, it loads it to somewhere else, and add - to each offset that is // specified by the .reloc section. In ELF terms, .reloc sections // contain relative relocations in REL format (as opposed to RELA.) // // This already significantly reduces the size of relocations compared // to ELF .rel.dyn, but Windows does more to reduce it (probably because // it was invented for PCs in the late '80s or early '90s.) Offsets in // .reloc are grouped by page where the page size is 12 bits, and // offsets sharing the same page address are stored consecutively to // represent them with less space. This is very similar to the page // table which is grouped by (multiple stages of) pages. // // For example, let's say we have 0x00030, 0x00500, 0x00700, 0x00A00, // 0x20004, and 0x20008 in a .reloc section for x64. The uppermost 4 // bits have a type IMAGE_REL_BASED_DIR64 or 0xA. In the section, they // are represented like this: // // 0x00000 -- page address (4 bytes) // 16 -- size of this block (4 bytes) // 0xA030 -- entries (2 bytes each) // 0xA500 // 0xA700 // 0xAA00 // 0x20000 -- page address (4 bytes) // 12 -- size of this block (4 bytes) // 0xA004 -- entries (2 bytes each) // 0xA008 // // Usually we have a lot of relocations for each page, so the number of // bytes for one .reloc entry is close to 2 bytes on average. BaserelChunk::BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End) { // 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; } } void BaserelChunk::writeTo(uint8_t *Buf) const { memcpy(Buf + OutputSectionOff, Data.data(), Data.size()); } uint8_t Baserel::getDefaultType() { switch (Config->Machine) { case AMD64: case ARM64: return IMAGE_REL_BASED_DIR64; case I386: case ARMNT: return IMAGE_REL_BASED_HIGHLOW; default: llvm_unreachable("unknown machine type"); } } std::map MergeChunk::Instances; MergeChunk::MergeChunk(uint32_t Alignment) : Builder(StringTableBuilder::RAW, Alignment) { this->Alignment = Alignment; } void MergeChunk::addSection(SectionChunk *C) { auto *&MC = Instances[C->Alignment]; if (!MC) MC = make(C->Alignment); 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; } 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; } } uint32_t MergeChunk::getOutputCharacteristics() const { return IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA; } size_t MergeChunk::getSize() const { return Builder.getSize(); } void MergeChunk::writeTo(uint8_t *Buf) const { Builder.write(Buf + OutputSectionOff); } // MinGW specific. size_t AbsolutePointerChunk::getSize() const { return Config->Wordsize; } void AbsolutePointerChunk::writeTo(uint8_t *Buf) const { if (Config->is64()) { write64le(Buf + OutputSectionOff, Value); } else { write32le(Buf + OutputSectionOff, Value); } } } // namespace coff } // namespace lld Index: vendor/lld/dist-release_80/COFF/Chunks.h =================================================================== --- vendor/lld/dist-release_80/COFF/Chunks.h (revision 343801) +++ vendor/lld/dist-release_80/COFF/Chunks.h (revision 343802) @@ -1,518 +1,527 @@ //===- Chunks.h -------------------------------------------------*- C++ -*-===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLD_COFF_CHUNKS_H #define LLD_COFF_CHUNKS_H #include "Config.h" #include "InputFiles.h" #include "lld/Common/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/COFF.h" #include #include namespace lld { namespace coff { using llvm::COFF::ImportDirectoryTableEntry; using llvm::object::COFFSymbolRef; using llvm::object::SectionRef; using llvm::object::coff_relocation; using llvm::object::coff_section; class Baserel; class Defined; class DefinedImportData; class DefinedRegular; class ObjFile; class OutputSection; class RuntimePseudoReloc; class Symbol; // Mask for permissions (discardable, writable, readable, executable, etc). const uint32_t PermMask = 0xFE000000; // Mask for section types (code, data, bss). const uint32_t TypeMask = 0x000000E0; // A Chunk represents a chunk of data that will occupy space in the // output (if the resolver chose that). It may or may not be backed by // a section of an input file. It could be linker-created data, or // doesn't even have actual data (if common or bss). class Chunk { public: enum Kind { SectionKind, OtherKind }; Kind kind() const { return ChunkKind; } virtual ~Chunk() = default; // Returns the size of this chunk (even if this is a common or BSS.) virtual size_t getSize() const = 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 {} // Called by the writer once before assigning addresses and writing // the output. virtual void readRelocTargets() {} // Called if restarting thunk addition. virtual void resetRelocTargets() {} // Called by the writer after an RVA is assigned, but before calling // getSize(). virtual void finalizeContents() {} // The writer sets and uses the addresses. uint64_t getRVA() const { return RVA; } void setRVA(uint64_t V) { RVA = V; } // 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; } // Returns readable/writable/executable bits. virtual uint32_t getOutputCharacteristics() const { return 0; } // 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) {} // 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 ""; } // The alignment of this chunk. The writer uses the value. uint32_t Alignment = 1; protected: Chunk(Kind K = OtherKind) : ChunkKind(K) {} const Kind ChunkKind; // The 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; }; // A chunk corresponding a section of an input file. class SectionChunk final : public Chunk { // Identical COMDAT Folding feature accesses section internal data. friend class ICF; public: class symbol_iterator : public llvm::iterator_adaptor_base< symbol_iterator, const coff_relocation *, std::random_access_iterator_tag, Symbol *> { friend SectionChunk; ObjFile *File; 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); } }; SectionChunk(ObjFile *File, const coff_section *Header); static bool classof(const Chunk *C) { return C->kind() == SectionKind; } void readRelocTargets() override; void resetRelocTargets() override; size_t getSize() const override { return Header->SizeOfRawData; } ArrayRef 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; 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 getRuntimePseudoRelocs(std::vector &Res); // Called if the garbage collector decides to not include this chunk // in a final output. It's supposed to print out a log message to stdout. void printDiscardedMessage() const; // Adds COMDAT associative sections to this COMDAT section. A chunk // and its children are treated as a group by the garbage collector. void addAssociative(SectionChunk *Child); StringRef getDebugName() override; // True if this is a codeview debug info chunk. These will not be laid out in // the image. Instead they will end up in the PDB, if one is requested. bool isCodeView() const { return SectionName == ".debug" || SectionName.startswith(".debug$"); } // True if this is a DWARF debug info or exception handling chunk. bool isDWARF() const { return SectionName.startswith(".debug_") || SectionName == ".eh_frame"; } // Allow iteration over the bodies of this chunk's relocated symbols. llvm::iterator_range symbols() const { return llvm::make_range(symbol_iterator(File, Relocs.begin()), symbol_iterator(File, Relocs.end())); } // Allow iteration over the associated child chunks for this section. ArrayRef children() const { return AssocChildren; } // The section ID this chunk belongs to in its Obj. uint32_t getSectionNumber() const; // A pointer pointing to a replacement for this chunk. // Initially it points to "this" object. If this chunk is merged // with other chunk by ICF, it points to another chunk, // and this chunk is considered as dead. SectionChunk *Repl; // 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; const coff_section *Header; // The file that this chunk was created from. ObjFile *File; // The COMDAT leader symbol if this is a COMDAT chunk. DefinedRegular *Sym = nullptr; ArrayRef Relocs; // Used by the garbage collector. bool Live; // When inserting a thunk, we need to adjust a relocation to point to // the thunk instead of the actual original target Symbol. std::vector RelocTargets; private: StringRef SectionName; std::vector AssocChildren; // Used for ICF (Identical COMDAT Folding) void replace(SectionChunk *Other); uint32_t Class[2] = {0, 0}; }; // This class is used to implement an lld-specific feature (not implemented in // MSVC) that minimizes the output size by finding string literals sharing tail // parts and merging them. // // If string tail merging is enabled and a section is identified as containing a // string literal, it is added to a MergeChunk with an appropriate alignment. // The MergeChunk then tail merges the strings using the StringTableBuilder // class and assigns RVAs and section offsets to each of the member chunks based // on the offsets assigned by the StringTableBuilder. class MergeChunk : public Chunk { public: MergeChunk(uint32_t Alignment); static void addSection(SectionChunk *C); void finalizeContents() override; uint32_t getOutputCharacteristics() const override; StringRef getSectionName() const override { return ".rdata"; } size_t getSize() const override; void writeTo(uint8_t *Buf) const override; static std::map Instances; std::vector Sections; private: llvm::StringTableBuilder Builder; bool Finalized = false; }; // A chunk for common symbols. Common chunks don't have actual data. class CommonChunk : public Chunk { public: CommonChunk(const COFFSymbolRef Sym); size_t getSize() const override { return Sym.getValue(); } bool hasData() const override { return false; } uint32_t getOutputCharacteristics() const override; StringRef getSectionName() const override { return ".bss"; } private: const COFFSymbolRef Sym; }; // A chunk for linker-created strings. class StringChunk : public Chunk { public: 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; }; static const uint8_t ImportThunkX86[] = { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // JMP *0x0 }; 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[] = { 0x10, 0x00, 0x00, 0x90, // adrp x16, #0 0x10, 0x02, 0x40, 0xf9, // ldr x16, [x16] 0x00, 0x02, 0x1f, 0xd6, // br x16 }; // Windows-specific. // A chunk for DLL import jump table entry. In a final output, its // contents will be a JMP instruction to some __imp_ symbol. class ImportThunkChunkX64 : public Chunk { public: 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 Chunk { 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; private: Defined *ImpSymbol; }; class ImportThunkChunkARM : public Chunk { 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; private: Defined *ImpSymbol; }; class ImportThunkChunkARM64 : public Chunk { public: explicit ImportThunkChunkARM64(Defined *S) : ImpSymbol(S) {} size_t getSize() const override { return sizeof(ImportThunkARM64); } void writeTo(uint8_t *Buf) const override; private: Defined *ImpSymbol; }; -class RangeExtensionThunk : public Chunk { +class RangeExtensionThunkARM : public Chunk { public: - explicit RangeExtensionThunk(Defined *T) : Target(T) {} + explicit RangeExtensionThunkARM(Defined *T) : Target(T) {} + size_t getSize() const override; + void writeTo(uint8_t *Buf) const override; + + Defined *Target; +}; + +class RangeExtensionThunkARM64 : public Chunk { +public: + explicit RangeExtensionThunkARM64(Defined *T) : Target(T) {} size_t getSize() const override; void writeTo(uint8_t *Buf) const override; Defined *Target; }; // Windows-specific. // See comments for DefinedLocalImport class. class LocalImportChunk : public Chunk { public: explicit LocalImportChunk(Defined *S) : Sym(S) { Alignment = Config->Wordsize; } size_t getSize() const override; void getBaserels(std::vector *Res) override; void writeTo(uint8_t *Buf) const override; private: 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; struct DenseMapInfo { static ChunkAndOffset getEmptyKey() { return {llvm::DenseMapInfo::getEmptyKey(), 0}; } static ChunkAndOffset getTombstoneKey() { return {llvm::DenseMapInfo::getTombstoneKey(), 0}; } static unsigned getHashValue(const ChunkAndOffset &CO) { return llvm::DenseMapInfo>::getHashValue( {CO.InputChunk, CO.Offset}); } static bool isEqual(const ChunkAndOffset &LHS, const ChunkAndOffset &RHS) { return LHS.InputChunk == RHS.InputChunk && LHS.Offset == RHS.Offset; } }; }; using SymbolRVASet = llvm::DenseSet; // Table which contains symbol RVAs. Used for /safeseh and /guard:cf. class RVATableChunk : public Chunk { 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; private: 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 { public: 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; }; class Baserel { public: 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; }; // This is a placeholder Chunk, to allow attaching a DefinedSynthetic to a // specific place in a section, without any data. This is used for the MinGW // specific symbol __RUNTIME_PSEUDO_RELOC_LIST_END__, even though the concept // of an empty chunk isn't MinGW specific. class EmptyChunk : public Chunk { public: EmptyChunk() {} size_t getSize() const override { return 0; } void writeTo(uint8_t *Buf) const override {} }; // MinGW specific, for the "automatic import of variables from DLLs" feature. // This provides the table of runtime pseudo relocations, for variable // references that turned out to need to be imported from a DLL even though // the reference didn't use the dllimport attribute. The MinGW runtime will // process this table after loading, before handling control over to user // code. class PseudoRelocTableChunk : public Chunk { public: PseudoRelocTableChunk(std::vector &Relocs) : Relocs(std::move(Relocs)) { Alignment = 4; } size_t getSize() const override; void writeTo(uint8_t *Buf) const override; private: std::vector Relocs; }; // MinGW specific; information about one individual location in the image // that needs to be fixed up at runtime after loading. This represents // one individual element in the PseudoRelocTableChunk table. class RuntimePseudoReloc { public: RuntimePseudoReloc(Defined *Sym, SectionChunk *Target, uint32_t TargetOffset, int Flags) : Sym(Sym), Target(Target), TargetOffset(TargetOffset), Flags(Flags) {} Defined *Sym; SectionChunk *Target; uint32_t TargetOffset; // The Flags field contains the size of the relocation, in bits. No other // flags are currently defined. int Flags; }; // MinGW specific. A Chunk that contains one pointer-sized absolute value. class AbsolutePointerChunk : public Chunk { public: AbsolutePointerChunk(uint64_t Value) : Value(Value) { Alignment = getSize(); } size_t getSize() const override; void writeTo(uint8_t *Buf) const override; private: uint64_t Value; }; void applyMOV32T(uint8_t *Off, uint32_t V); void applyBranch24T(uint8_t *Off, int32_t V); void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift); void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit); void applyArm64Branch26(uint8_t *Off, int64_t V); } // namespace coff } // namespace lld namespace llvm { template <> struct DenseMapInfo : lld::coff::ChunkAndOffset::DenseMapInfo {}; } #endif Index: vendor/lld/dist-release_80/COFF/DLL.cpp =================================================================== --- vendor/lld/dist-release_80/COFF/DLL.cpp (revision 343801) +++ vendor/lld/dist-release_80/COFF/DLL.cpp (revision 343802) @@ -1,631 +1,645 @@ //===- DLL.cpp ------------------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines various types of chunks for the DLL import or export // descriptor tables. They are inherently Windows-specific. // You need to read Microsoft PE/COFF spec to understand details // about the data structures. // // If you are not particularly interested in linking against Windows // DLL, you can skip this file, and you should still be able to // understand the rest of the linker. // //===----------------------------------------------------------------------===// #include "DLL.h" #include "Chunks.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Path.h" using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::COFF; namespace lld { namespace coff { namespace { // Import table // A chunk for the import descriptor table. class HintNameChunk : public Chunk { public: 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); } void writeTo(uint8_t *Buf) const override { + memset(Buf + OutputSectionOff, 0, getSize()); write16le(Buf + OutputSectionOff, Hint); memcpy(Buf + OutputSectionOff + 2, Name.data(), Name.size()); } private: StringRef Name; uint16_t Hint; }; // A chunk for the import descriptor table. class LookupChunk : public Chunk { public: explicit LookupChunk(Chunk *C) : HintName(C) { Alignment = Config->Wordsize; } size_t getSize() const override { return Config->Wordsize; } void writeTo(uint8_t *Buf) const override { - write32le(Buf + OutputSectionOff, HintName->getRVA()); + if (Config->is64()) + write64le(Buf + OutputSectionOff, HintName->getRVA()); + else + write32le(Buf + OutputSectionOff, HintName->getRVA()); } 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 { public: explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) { Alignment = Config->Wordsize; } size_t getSize() const override { return Config->Wordsize; } void writeTo(uint8_t *Buf) const override { // An import-by-ordinal slot has MSB 1 to indicate that // this is import-by-ordinal (and not import-by-name). if (Config->is64()) { write64le(Buf + OutputSectionOff, (1ULL << 63) | Ordinal); } else { write32le(Buf + OutputSectionOff, (1ULL << 31) | Ordinal); } } uint16_t Ordinal; }; // A chunk for the import descriptor table. class ImportDirectoryChunk : public Chunk { public: explicit ImportDirectoryChunk(Chunk *N) : DLLName(N) {} size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); } void writeTo(uint8_t *Buf) const override { + memset(Buf + OutputSectionOff, 0, getSize()); + auto *E = (coff_import_directory_table_entry *)(Buf + OutputSectionOff); E->ImportLookupTableRVA = LookupTab->getRVA(); E->NameRVA = DLLName->getRVA(); E->ImportAddressTableRVA = AddressTab->getRVA(); } 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 { public: explicit NullChunk(size_t N) : Size(N) {} bool hasData() const override { return false; } size_t getSize() const override { return Size; } + void writeTo(uint8_t *Buf) const override { + memset(Buf + OutputSectionOff, 0, Size); + } + private: size_t Size; }; static std::vector> 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]; }; std::map, 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) { // 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(); }); V.push_back(std::move(Syms)); } 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 { public: 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 { + memset(Buf + OutputSectionOff, 0, getSize()); + 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(); } 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[] = { 0x51, // push rcx 0x52, // push rdx 0x41, 0x50, // push r8 0x41, 0x51, // push r9 0x48, 0x83, 0xEC, 0x48, // sub rsp, 48h 0x66, 0x0F, 0x7F, 0x04, 0x24, // movdqa xmmword ptr [rsp], xmm0 0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x10, // movdqa xmmword ptr [rsp+10h], xmm1 0x66, 0x0F, 0x7F, 0x54, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h], xmm2 0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h], xmm3 0x48, 0x8D, 0x15, 0, 0, 0, 0, // lea rdx, [__imp_] 0x48, 0x8D, 0x0D, 0, 0, 0, 0, // lea rcx, [___DELAY_IMPORT_...] 0xE8, 0, 0, 0, 0, // call __delayLoadHelper2 0x66, 0x0F, 0x6F, 0x04, 0x24, // movdqa xmm0, xmmword ptr [rsp] 0x66, 0x0F, 0x6F, 0x4C, 0x24, 0x10, // movdqa xmm1, xmmword ptr [rsp+10h] 0x66, 0x0F, 0x6F, 0x54, 0x24, 0x20, // movdqa xmm2, xmmword ptr [rsp+20h] 0x66, 0x0F, 0x6F, 0x5C, 0x24, 0x30, // movdqa xmm3, xmmword ptr [rsp+30h] 0x48, 0x83, 0xC4, 0x48, // add rsp, 48h 0x41, 0x59, // pop r9 0x41, 0x58, // pop r8 0x5A, // pop rdx 0x59, // pop rcx 0xFF, 0xE0, // jmp rax }; static const uint8_t ThunkX86[] = { 0x51, // push ecx 0x52, // push edx 0x68, 0, 0, 0, 0, // push offset ___imp__ 0x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR__dll 0xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@8 0x5A, // pop edx 0x59, // pop ecx 0xFF, 0xE0, // jmp eax }; static const uint8_t ThunkARM[] = { 0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 __imp_ 0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 __imp_ 0x2d, 0xe9, 0x0f, 0x48, // push.w {r0, r1, r2, r3, r11, lr} 0x0d, 0xf2, 0x10, 0x0b, // addw r11, sp, #16 0x2d, 0xed, 0x10, 0x0b, // vpush {d0, d1, d2, d3, d4, d5, d6, d7} 0x61, 0x46, // mov r1, ip 0x40, 0xf2, 0x00, 0x00, // mov.w r0, #0 DELAY_IMPORT_DESCRIPTOR 0xc0, 0xf2, 0x00, 0x00, // mov.t r0, #0 DELAY_IMPORT_DESCRIPTOR 0x00, 0xf0, 0x00, 0xd0, // bl #0 __delayLoadHelper2 0x84, 0x46, // mov ip, r0 0xbd, 0xec, 0x10, 0x0b, // vpop {d0, d1, d2, d3, d4, d5, d6, d7} 0xbd, 0xe8, 0x0f, 0x48, // pop.w {r0, r1, r2, r3, r11, lr} 0x60, 0x47, // bx ip }; static const uint8_t ThunkARM64[] = { 0x11, 0x00, 0x00, 0x90, // adrp x17, #0 __imp_ 0x31, 0x02, 0x00, 0x91, // add x17, x17, #0 :lo12:__imp_ 0xfd, 0x7b, 0xb3, 0xa9, // stp x29, x30, [sp, #-208]! 0xfd, 0x03, 0x00, 0x91, // mov x29, sp 0xe0, 0x07, 0x01, 0xa9, // stp x0, x1, [sp, #16] 0xe2, 0x0f, 0x02, 0xa9, // stp x2, x3, [sp, #32] 0xe4, 0x17, 0x03, 0xa9, // stp x4, x5, [sp, #48] 0xe6, 0x1f, 0x04, 0xa9, // stp x6, x7, [sp, #64] 0xe0, 0x87, 0x02, 0xad, // stp q0, q1, [sp, #80] 0xe2, 0x8f, 0x03, 0xad, // stp q2, q3, [sp, #112] 0xe4, 0x97, 0x04, 0xad, // stp q4, q5, [sp, #144] 0xe6, 0x9f, 0x05, 0xad, // stp q6, q7, [sp, #176] 0xe1, 0x03, 0x11, 0xaa, // mov x1, x17 0x00, 0x00, 0x00, 0x90, // adrp x0, #0 DELAY_IMPORT_DESCRIPTOR 0x00, 0x00, 0x00, 0x91, // add x0, x0, #0 :lo12:DELAY_IMPORT_DESCRIPTOR 0x00, 0x00, 0x00, 0x94, // bl #0 __delayLoadHelper2 0xf0, 0x03, 0x00, 0xaa, // mov x16, x0 0xe6, 0x9f, 0x45, 0xad, // ldp q6, q7, [sp, #176] 0xe4, 0x97, 0x44, 0xad, // ldp q4, q5, [sp, #144] 0xe2, 0x8f, 0x43, 0xad, // ldp q2, q3, [sp, #112] 0xe0, 0x87, 0x42, 0xad, // ldp q0, q1, [sp, #80] 0xe6, 0x1f, 0x44, 0xa9, // ldp x6, x7, [sp, #64] 0xe4, 0x17, 0x43, 0xa9, // ldp x4, x5, [sp, #48] 0xe2, 0x0f, 0x42, 0xa9, // ldp x2, x3, [sp, #32] 0xe0, 0x07, 0x41, 0xa9, // ldp x0, x1, [sp, #16] 0xfd, 0x7b, 0xcd, 0xa8, // ldp x29, x30, [sp], #208 0x00, 0x02, 0x1f, 0xd6, // br x16 }; // A chunk for the delay import thunk. class ThunkChunkX64 : public Chunk { public: ThunkChunkX64(Defined *I, Chunk *D, Defined *H) : Imp(I), Desc(D), Helper(H) {} size_t getSize() const override { return sizeof(ThunkX64); } void writeTo(uint8_t *Buf) const override { memcpy(Buf + OutputSectionOff, ThunkX64, sizeof(ThunkX64)); write32le(Buf + OutputSectionOff + 36, Imp->getRVA() - RVA - 40); write32le(Buf + OutputSectionOff + 43, Desc->getRVA() - RVA - 47); write32le(Buf + OutputSectionOff + 48, Helper->getRVA() - RVA - 52); } Defined *Imp = nullptr; Chunk *Desc = nullptr; Defined *Helper = nullptr; }; class ThunkChunkX86 : public Chunk { public: ThunkChunkX86(Defined *I, Chunk *D, Defined *H) : Imp(I), Desc(D), Helper(H) {} size_t getSize() const override { return sizeof(ThunkX86); } 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 getBaserels(std::vector *Res) override { Res->emplace_back(RVA + 3); Res->emplace_back(RVA + 8); } Defined *Imp = nullptr; Chunk *Desc = nullptr; Defined *Helper = nullptr; }; class ThunkChunkARM : public Chunk { public: ThunkChunkARM(Defined *I, Chunk *D, Defined *H) : Imp(I), Desc(D), Helper(H) {} size_t getSize() const override { return sizeof(ThunkARM); } void writeTo(uint8_t *Buf) const override { memcpy(Buf + OutputSectionOff, ThunkARM, sizeof(ThunkARM)); applyMOV32T(Buf + OutputSectionOff + 0, Imp->getRVA() + Config->ImageBase); applyMOV32T(Buf + OutputSectionOff + 22, Desc->getRVA() + Config->ImageBase); applyBranch24T(Buf + OutputSectionOff + 30, Helper->getRVA() - RVA - 34); } void getBaserels(std::vector *Res) override { Res->emplace_back(RVA + 0, IMAGE_REL_BASED_ARM_MOV32T); Res->emplace_back(RVA + 22, IMAGE_REL_BASED_ARM_MOV32T); } Defined *Imp = nullptr; Chunk *Desc = nullptr; Defined *Helper = nullptr; }; class ThunkChunkARM64 : public Chunk { public: ThunkChunkARM64(Defined *I, Chunk *D, Defined *H) : Imp(I), Desc(D), Helper(H) {} size_t getSize() const override { return sizeof(ThunkARM64); } void writeTo(uint8_t *Buf) const override { memcpy(Buf + OutputSectionOff, ThunkARM64, sizeof(ThunkARM64)); applyArm64Addr(Buf + OutputSectionOff + 0, Imp->getRVA(), RVA + 0, 12); applyArm64Imm(Buf + OutputSectionOff + 4, Imp->getRVA() & 0xfff, 0); applyArm64Addr(Buf + OutputSectionOff + 52, Desc->getRVA(), RVA + 52, 12); applyArm64Imm(Buf + OutputSectionOff + 56, Desc->getRVA() & 0xfff, 0); applyArm64Branch26(Buf + OutputSectionOff + 60, Helper->getRVA() - RVA - 60); } Defined *Imp = nullptr; Chunk *Desc = nullptr; Defined *Helper = nullptr; }; // A chunk for the import descriptor table. class DelayAddressChunk : public Chunk { public: explicit DelayAddressChunk(Chunk *C) : Thunk(C) { Alignment = 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); } else { 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); } } void getBaserels(std::vector *Res) override { Res->emplace_back(RVA); } Chunk *Thunk; }; // Export table // Read Microsoft PE/COFF spec 5.3 for details. // A chunk for the export descriptor table. class ExportDirectoryChunk : public Chunk { 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) {} size_t getSize() const override { return sizeof(export_directory_table_entry); } void writeTo(uint8_t *Buf) const override { + memset(Buf + OutputSectionOff, 0, getSize()); + 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(); } uint16_t MaxOrdinal; uint16_t NameTabSize; Chunk *DLLName; Chunk *AddressTab; Chunk *NameTab; Chunk *OrdinalTab; }; class AddressTableChunk : public Chunk { public: 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()); for (const Export &E : Config->Exports) { uint8_t *P = Buf + OutputSectionOff + 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); } else { write32le(P, cast(E.Sym)->getRVA() | Bit); } } } private: size_t Size; }; class NamePointersChunk : public Chunk { 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; } } private: std::vector Chunks; }; class ExportOrdinalChunk : public Chunk { public: 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) continue; write16le(P, E.Ordinal); P += 2; } } private: size_t Size; }; } // anonymous namespace void IdataContents::create() { std::vector> V = binImports(Imports); // Create .idata contents for each DLL. 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. // 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)); continue; } 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)); 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); } // Add null terminator. 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 DelayLoadContents::getDataChunks() { 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); } void DelayLoadContents::create(Defined *H) { Helper = H; std::vector> V = binImports(Imports); // Create .didat contents for each DLL. 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())); } else { auto *C = make(ExtName, 0); Names.push_back(make(C)); HintNames.push_back(C); } } // Terminate with null values. 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); // Fill the delay import table header fields. 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))); } Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *S, Chunk *Dir) { switch (Config->Machine) { case AMD64: return make(S, Dir, Helper); case I386: return make(S, Dir, Helper); case ARMNT: return make(S, Dir, Helper); case ARM64: return make(S, Dir, Helper); 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()) 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()); } } // namespace coff } // namespace lld Index: vendor/lld/dist-release_80/COFF/ICF.cpp =================================================================== --- vendor/lld/dist-release_80/COFF/ICF.cpp (revision 343801) +++ vendor/lld/dist-release_80/COFF/ICF.cpp (revision 343802) @@ -1,316 +1,318 @@ //===- ICF.cpp ------------------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // ICF is short for Identical Code Folding. That is a size optimization to // identify and merge two or more read-only sections (typically functions) // that happened to have the same contents. It usually reduces output size // by a few percent. // // On Windows, ICF is enabled by default. // // See ELF/ICF.cpp for the details about the algortihm. // //===----------------------------------------------------------------------===// #include "ICF.h" #include "Chunks.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Threads.h" #include "lld/Common/Timer.h" #include "llvm/ADT/Hashing.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/xxhash.h" #include #include #include using namespace llvm; namespace lld { namespace coff { static Timer ICFTimer("ICF", Timer::root()); class ICF { public: void run(ArrayRef V); private: void segregate(size_t Begin, size_t End, bool Constant); bool assocEquals(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); size_t findBoundary(size_t Begin, size_t End); void forEachClassRange(size_t Begin, size_t End, std::function Fn); void forEachClass(std::function Fn); std::vector Chunks; int Cnt = 0; std::atomic Repeat = {false}; }; // Returns true if section S is subject of ICF. // // Microsoft's documentation // (https://msdn.microsoft.com/en-us/library/bxwfs976.aspx; visited April // 2017) says that /opt:icf folds both functions and read-only data. // Despite that, the MSVC linker folds only functions. We found // a few instances of programs that are not safe for data merging. // Therefore, we merge only functions just like the MSVC tool. However, we also // merge read-only sections in a couple of cases where the address of the // section is insignificant to the user program and the behaviour matches that // of the Visual C++ linker. bool ICF::isEligible(SectionChunk *C) { // 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) return false; // Code sections are eligible. 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") return true; // So are vtables. if (C->Sym && C->Sym->getName().startswith("??_7")) return true; // Anything else not in an address-significance table is eligible. return !C->KeepUnique; } // Split an equivalence class into smaller classes. 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); }); 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; // If we created a group, we need to iterate the main loop again. if (Mid != End) Repeat = true; 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; }; 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()) return false; // Compare relocations. 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) 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]; return false; }; if (!std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.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); } // Compare "moving" part of two sections, namely relocation targets. bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) { // Compare relocations. auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) { 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]; return false; }; return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.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; } 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) { // If the number of sections are too small to use threading, // call Fn sequentially. if (Chunks.size() < 1024) { forEachClassRange(0, Chunks.size(), Fn); ++Cnt; return; } // Shard into non-overlapping intervals, and call Fn in parallel. // The sharding must be completed before any calls to Fn are made // so that Fn can modify the Chunks in its shard without causing data // races. const size_t NumShards = 256; size_t Step = Chunks.size() / NumShards; size_t Boundaries[NumShards + 1]; Boundaries[0] = 0; Boundaries[NumShards] = Chunks.size(); parallelForEachN(1, NumShards, [&](size_t I) { Boundaries[I] = findBoundary((I - 1) * Step, Chunks.size()); }); parallelForEachN(1, NumShards + 1, [&](size_t I) { if (Boundaries[I - 1] < Boundaries[I]) { forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn); } }); ++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); // 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); else SC->Class[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++; // Initially, we use hash values to partition sections. parallelForEach(Chunks, [&](SectionChunk *SC) { - SC->Class[1] = xxHash64(SC->getContents()); + SC->Class[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->Class[Cnt % 2]; + for (Symbol *B : SC->symbols()) + if (auto *Sym = dyn_cast_or_null(B)) + Hash += Sym->getChunk()->Class[Cnt % 2]; + // Set MSB to 1 to avoid collisions with non-hash classs. + SC->Class[(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]; }); // Compare static contents and assign unique IDs for each static content. forEachClass([&](size_t Begin, size_t End) { segregate(Begin, End, true); }); // Split groups by comparing relocations until convergence is obtained. do { Repeat = false; forEachClass( [&](size_t Begin, size_t End) { segregate(Begin, End, false); }); } while (Repeat); log("ICF needed " + Twine(Cnt) + " iterations"); // Merge sections in the same classs. 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]); } }); } // Entry point to ICF. void doICF(ArrayRef Chunks) { ICF().run(Chunks); } } // namespace coff } // namespace lld Index: vendor/lld/dist-release_80/COFF/Writer.cpp =================================================================== --- vendor/lld/dist-release_80/COFF/Writer.cpp (revision 343801) +++ vendor/lld/dist-release_80/COFF/Writer.cpp (revision 343802) @@ -1,1719 +1,1745 @@ //===- Writer.cpp ---------------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "Writer.h" #include "Config.h" #include "DLL.h" #include "InputFiles.h" #include "MapFile.h" #include "PDB.h" #include "SymbolTable.h" #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "lld/Common/Timer.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" #include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Support/xxhash.h" #include #include #include #include #include using namespace llvm; using namespace llvm::COFF; using namespace llvm::object; using namespace llvm::support; using namespace llvm::support::endian; using namespace lld; using namespace lld::coff; /* To re-generate DOSProgram: $ cat > /tmp/DOSProgram.asm org 0 ; Copy cs to ds. push cs pop ds ; Point ds:dx at the $-terminated string. mov dx, str ; Int 21/AH=09h: Write string to standard output. mov ah, 0x9 int 0x21 ; Int 21/AH=4Ch: Exit with return code (in AL). mov ax, 0x4C01 int 0x21 str: db 'This program cannot be run in DOS mode.$' align 8, db 0 $ nasm -fbin /tmp/DOSProgram.asm -o /tmp/DOSProgram.bin $ xxd -i /tmp/DOSProgram.bin */ static unsigned char DOSProgram[] = { 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, "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 NumberOfDataDirectory = 16; namespace { class DebugDirectoryChunk : public Chunk { public: DebugDirectoryChunk(const std::vector &R, bool WriteRepro) : Records(R), WriteRepro(WriteRepro) {} size_t getSize() const override { return (Records.size() + int(WriteRepro)) * sizeof(debug_directory); } void writeTo(uint8_t *B) const override { auto *D = reinterpret_cast(B + OutputSectionOff); 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) { // 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); } } 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; }; class CVDebugRecordChunk : public Chunk { public: size_t getSize() const override { return sizeof(codeview::DebugInfo) + Config->PDBAltPath.size() + 1; } 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); // 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'; } mutable codeview::DebugInfo *BuildId = nullptr; }; // The writer writes a SymbolTable result to a file. class Writer { public: Writer() : Buffer(errorHandler().OutputBuffer) {} void run(); private: void createSections(); void createMiscChunks(); void createImportTables(); void appendImportThunks(); void locateImportTables( std::map, std::vector> &Map); void createExportTable(); void mergeSections(); void readRelocTargets(); void removeUnusedSections(); void assignAddresses(); void finalizeAddresses(); void removeEmptySections(); void createSymbolAndStringTable(); 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 setSectionPermissions(); void writeSections(); void writeBuildId(); void sortExceptionTable(); void sortCRTSectionChunks(std::vector &Chunks); llvm::Optional createSymbol(Defined *D); size_t addEntryToStringTable(StringRef Str); OutputSection *findSection(StringRef Name); void addBaserels(); 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; // The first and last .pdata sections in the output file. // // We need to keep track of the location of .pdata in whichever section it // gets merged into so that we can sort its contents and emit a correct data // directory entry for the exception table. This is also the case for some // other sections (such as .edata) but because the contents of those sections // are entirely linker-generated we can keep track of their locations using // the chunks that the linker creates. All .pdata chunks come from input // files, so we need to keep track of them separately. Chunk *FirstPdata = nullptr; Chunk *LastPdata; }; } // anonymous namespace namespace lld { namespace coff { 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::insertChunkAtStart(Chunk *C) { Chunks.insert(Chunks.begin(), C); C->setOutputSection(this); } 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(); } // Write the section header to a given buffer. 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); } 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)); } } } // 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; + 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); + Chunk *C; + switch (Config->Machine) { + case ARMNT: + C = make(Target); + break; + case ARM64: + C = make(Target); + break; + default: + llvm_unreachable("Unexpected architecture"); + } Defined *D = make("", C); LastThunk = D; return {D, true}; } // This checks all relocations, and for any relocation which isn't in range // it adds a thunk after the section chunk that contains the relocation. // If the latest thunk for the specific target is in range, that is used // instead of creating a new thunk. All range checks are done with the // specified margin, to make sure that relocations that originally are in // range, but only barely, also get thunks - in case other added thunks makes // the target go out of range. // // After adding thunks, we verify that all relocations are in range (with // no extra margin requirements). If this failed, we restart (throwing away // the previously created thunks) and retry with a wider margin. -static bool createThunks(std::vector &Chunks, int Margin) { +static bool createThunks(OutputSection *OS, int Margin) { bool AddressesChanged = false; DenseMap LastThunks; 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]); + 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; // 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]; // 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 // it), giving us some uncertainty once we have added one thunk. uint64_t P = SC->getRVA() + Rel.VirtualAddress + ThunksSize; Defined *Sym = dyn_cast_or_null(RelocTarget); if (!Sym) continue; uint64_t S = Sym->getRVA(); 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); + ThunkChunk->setOutputSection(OS); + OS->Chunks.insert(OS->Chunks.begin() + ThunkInsertionSpot, ThunkChunk); ThunkInsertionSpot++; ThunksSize += ThunkChunk->getSize(); ThunkInsertionRVA += ThunkChunk->getSize(); AddressesChanged = true; } RelocTarget = Thunk; } } 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) 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]; Defined *Sym = dyn_cast_or_null(RelocTarget); if (!Sym) continue; uint64_t P = SC->getRVA() + Rel.VirtualAddress; uint64_t S = Sym->getRVA(); if (!isInRange(Rel.Type, S, P, 0)) return false; } } return true; } // Assign addresses and add thunks if necessary. void Writer::finalizeAddresses() { assignAddresses(); - if (Config->Machine != ARMNT) + if (Config->Machine != ARMNT && Config->Machine != ARM64) return; size_t OrigNumChunks = 0; for (OutputSection *Sec : OutputSections) { Sec->OrigChunks = Sec->Chunks; OrigNumChunks += Sec->Chunks.size(); } 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; break; } NumChunks += Sec->Chunks.size(); } 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 > 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; } // 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); + AddressesChanged |= createThunks(Sec, Margin); // If the verification above thought we needed thunks, we should have // added some. assert(AddressesChanged); // Recalculate the layout for the whole image (and verify the ranges at // the start of the next round). assignAddresses(); Pass++; } } // The main function of the writer. void Writer::run() { ScopedTimer T1(CodeLayoutTimer); createImportTables(); createSections(); createMiscChunks(); appendImportThunks(); createExportTable(); mergeSections(); readRelocTargets(); removeUnusedSections(); finalizeAddresses(); removeEmptySections(); setSectionPermissions(); createSymbolAndStringTable(); if (FileSize > UINT32_MAX) fatal("image size (" + Twine(FileSize) + ") " + "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")"); openFile(Config->OutputFile); if (Config->is64()) { writeHeader(); } else { writeHeader(); } writeSections(); sortExceptionTable(); T1.stop(); if (!Config->PDBPath.empty() && Config->Debug) { assert(BuildId); createPDB(Symtab, OutputSections, SectionTable, BuildId->BuildId); } writeBuildId(); writeMapFile(OutputSections); 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; // Treat a later period as a separator for MinGW, for sections like // ".ctors.01234". 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()); return 0; }; std::stable_sort(Chunks.begin(), Chunks.end(), [=](const Chunk *A, const Chunk *B) { return GetPriority(A) < GetPriority(B); }); } // Sort concrete section chunks from GNU import libraries. // // GNU binutils doesn't use short import files, but instead produces import // libraries that consist of object files, with section chunks for the .idata$* // sections. These are linked just as regular static libraries. Each import // library consists of one header object, one object file for every imported // symbol, and one trailer object. In order for the .idata tables/lists to // be formed correctly, the section chunks within each .idata$* section need // to be grouped by library, and sorted alphabetically within each library // (which makes sure the header comes first and the trailer last). static bool fixGnuImportChunks( std::map, std::vector> &Map) { uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; // 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(); } 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")) 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 SC1, order them ascending. If SC2 or both null, // S is not less than T. 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; }); } 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(); // 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()); }; // 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); } // 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(); } // 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); } 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); // 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(); continue; } Map[{C->getSectionName(), C->getOutputCharacteristics()}].push_back(C); } // Even in non MinGW cases, we might need to link against GNU import // libraries. bool HasIdata = fixGnuImportChunks(Map); if (!Idata.empty()) HasIdata = true; if (HasIdata) addSyntheticIdata(Idata, Map); // Process an /order option. if (!Config->Order.empty()) for (auto &Pair : Map) sortBySectionOrder(Pair.second); if (HasIdata) locateImportTables(Map); // 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; 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; log("Processing section " + Pair.first.first + " -> " + Name); sortCRTSectionChunks(Pair.second); } OutputSection *Sec = CreateSection(Name, OutChars); std::vector &Chunks = Pair.second; for (Chunk *C : Chunks) Sec->addChunk(C); } // 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) 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) return 1; return 0; }; std::stable_sort(OutputSections.begin(), OutputSections.end(), [&](OutputSection *S, OutputSection *T) { return SectionOrder(S) < SectionOrder(T); }); } void Writer::createMiscChunks() { for (auto &P : MergeChunk::Instances) RdataSec->addChunk(P.second); // Create thunks for locally-dllimported symbols. 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); } 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); for (Chunk *C : DebugRecords) DebugInfoSec->addChunk(C); } // Create SEH table. x86-only. if (Config->Machine == I386) createSEHTable(); // Create /guard:cf tables if requested. if (Config->GuardCF != GuardCFLevel::Off) createGuardCFTables(); if (Config->MinGW) { createRuntimePseudoRelocs(); insertCtorDtorSymbols(); } } // Create .idata section for the DLL-imported symbol table. // The format of this section is inherently Windows-specific. // IdataContents class abstracted away the details for us, // so we just let it create chunks and add them to the section. void Writer::createImportTables() { // Initialize DLLOrder so that import entries are ordered in // the same order as in the command line. (That affects DLL // initialization order, and this ordering is MSVC-compatible.) for (ImportFile *File : ImportFile::Instances) { if (!File->Live) 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); } else { Idata.add(ImpSym); } } } void Writer::appendImportThunks() { if (ImportFile::Instances.empty()) return; for (ImportFile *File : ImportFile::Instances) { if (!File->Live) continue; 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 (!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()) return; 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) 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(); }; 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++; } 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; break; case Symbol::DefinedSyntheticKind: // Relative symbols are unrepresentable in a COFF symbol table. return None; default: { // Don't write symbols that won't be written to the output to the symbol // table. Chunk *C = Def->getChunk(); if (!C) return None; OutputSection *OS = C->getOutputSection(); if (!OS) return None; 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); } else { 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(); } else { Sym.Type = IMAGE_SYM_TYPE_NULL; Sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; } Sym.NumberOfAuxSymbols = 0; return Sym; } void Writer::createSymbolAndStringTable() { // PE/COFF images are limited to 8 byte section names. Longer names can be // supported by writing a non-standard string table, but this string table is // not mapped at runtime and the long names will therefore be inaccessible. // link.exe always truncates section names to 8 bytes, whereas binutils always // preserves long section names via the string table. LLD adopts a hybrid // solution where discardable sections have long names preserved and // non-discardable sections have their names truncated, to ensure that any // section which is mapped at runtime also has its name mapped at runtime. for (OutputSection *Sec : OutputSections) { if (Sec->Name.size() <= COFF::NameSize) continue; if ((Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) continue; 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) continue; D->WrittenToSymtab = true; if (Optional Sym = createSymbol(D)) OutputSymtab.push_back(*Sym); } } } 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); } void Writer::mergeSections() { 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) continue; 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()) break; ToName = I->second; } OutputSection *From = findSection(P.first); OutputSection *To = findSection(ToName); if (!From) continue; if (!To) { From->Name = ToName; continue; } 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) 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); } 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); } template void Writer::writeHeader() { // Write DOS header. For backwards compatibility, the first part of a PE/COFF // executable consists of an MS-DOS MZ executable. If the executable is run // under DOS, that program gets run (usually to just print an error message). // When run under Windows, the loader looks at AddressOfNewExeHeader and uses // the PE header instead. uint8_t *Buf = Buffer->getBufferStart(); auto *DOS = reinterpret_cast(Buf); Buf += sizeof(dos_header); DOS->Magic[0] = 'M'; DOS->Magic[1] = 'Z'; DOS->UsedBytesInTheLastPage = DOSStubSize % 512; DOS->FileSizeInPages = divideCeil(DOSStubSize, 512); DOS->HeaderSizeInParagraphs = sizeof(dos_header) / 16; DOS->AddressOfRelocationTable = sizeof(dos_header); DOS->AddressOfNewExeHeader = DOSStubSize; // Write DOS program. memcpy(Buf, DOSProgram, sizeof(DOSProgram)); Buf += sizeof(DOSProgram); // Write PE magic 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; // Write PE header 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(); // 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(); // 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() ? 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 (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()) fatal("_load_config_used is too large"); 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(); } // Write section table for (OutputSection *Sec : OutputSections) { Sec->writeHeaderTo(Buf); Buf += sizeof(coff_section); } SectionTable = ArrayRef( Buf - OutputSections.size() * sizeof(coff_section), Buf); 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]; // 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()); } 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); } // 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"); 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}); } // 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) 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); } // 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()) { // 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) continue; for (const coff_relocation &Reloc : SC->Relocs) { 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); } } } // Create the guard function id table. This is a table of RVAs of all // address-taken functions. It is sorted and uniqued, just like the safe SEH // table. void Writer::createGuardCFTables() { SymbolRVASet AddressTakenSyms; SymbolRVASet LongJmpTargets; for (ObjFile *File : ObjFile::Instances) { // 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); } else { markSymbolsWithRelocations(File, AddressTakenSyms); } } // Mark the image entry as address-taken. 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); // 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; 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", "__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(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); } // 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) { // 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) 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)); 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()) { warn("ignoring invalid symbol table index in section " + C->getSectionName() + " in object " + toString(File)); continue; } 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 // relative relocations. void Writer::maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym, StringRef CountSym) { if (TableSymbols.empty()) return; 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); } // MinGW specific. Gather all relocations that are imported from a DLL even // though the code didn't expect it to, produce the table that the runtime // uses for fixing them up, and provide the synthetic symbols that the // runtime uses for finding the table. void Writer::createRuntimePseudoRelocs() { std::vector Rels; for (Chunk *C : Symtab->getChunks()) { auto *SC = dyn_cast(C); if (!SC || !SC->Live) continue; 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); } // MinGW specific. // The MinGW .ctors and .dtors lists have sentinels at each end; // a (uintptr_t)-1 at the start and a (uintptr_t)0 at the end. // There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__ // and __DTOR_LIST__ respectively. void Writer::insertCtorDtorSymbols() { AbsolutePointerChunk *CtorListHead = make(-1); AbsolutePointerChunk *CtorListEnd = make(0); AbsolutePointerChunk *DtorListHead = make(-1); AbsolutePointerChunk *DtorListEnd = make(0); CtorsSec->insertChunkAtStart(CtorListHead); CtorsSec->addChunk(CtorListEnd); DtorsSec->insertChunkAtStart(DtorListHead); DtorsSec->addChunk(DtorListEnd); 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); } } // Write section contents to a mmap'ed file. void Writer::writeSections() { // Record the number of sections to apply section index relocations // against absolute symbols. See applySecIdx in Chunks.cpp.. DefinedAbsolute::NumOutputSections = OutputSections.size(); 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); }); } } void Writer::writeBuildId() { // There are two important parts to the build ID. // 1) If building with debug info, the COFF debug directory contains a // timestamp as well as a Guid and Age of the PDB. // 2) In all cases, the PE COFF file header also contains a timestamp. // For reproducibility, instead of a timestamp we want to use a hash of the // PE contents. if (Config->Debug) { assert(BuildId && "BuildId is not set!"); // BuildId->BuildId was filled in when the PDB was written. } // At this point the only fields in the COFF file which remain unset are the // "timestamp" in the COFF file header, and the ones in the coff debug // directory. Now we can hash the file and write that hash to the various // timestamp fields in the file. StringRef OutputFileData( reinterpret_cast(Buffer->getBufferStart()), Buffer->getBufferSize()); 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) Timestamp = static_cast(Hash); 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); // xxhash only gives us 8 bytes, so put some fixed data in the other half. memcpy(&BuildId->BuildId->PDB70.Signature[8], "LLD PDB.", 8); } 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; } // Sort .pdata section contents according to PE/COFF spec 5.5. void Writer::sortExceptionTable() { 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(); }; 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; }); 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; }); return; } errs() << "warning: don't know how to handle .pdata.\n"; } // The CRT section contains, among other things, the array of function // pointers that initialize every global variable that is not trivially // constructed. The CRT calls them one after the other prior to invoking // main(). // // As per C++ spec, 3.6.2/2.3, // "Variables with ordered initialization defined within a single // translation unit shall be initialized in the order of their definitions // in the translation unit" // // It is therefore critical to sort the chunks containing the function // pointers in the order that they are listed in the object file (top to // bottom), otherwise global objects might not be initialized in the // correct order. void Writer::sortCRTSectionChunks(std::vector &Chunks) { auto SectionChunkOrder = [](const Chunk *A, const Chunk *B) { auto SA = dyn_cast(A); auto SB = dyn_cast(B); assert(SA && SB && "Non-section chunks in CRT section!"); StringRef SAObj = SA->File->MB.getBufferIdentifier(); StringRef SBObj = SB->File->MB.getBufferIdentifier(); return SAObj == SBObj && SA->getSectionNumber() < SB->getSectionNumber(); }; std::stable_sort(Chunks.begin(), Chunks.end(), SectionChunkOrder); 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; 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; } // Add base relocations to .reloc section. void Writer::addBaserels() { if (!Config->Relocatable) return; 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); // Add the addresses to .reloc section. 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) continue; RelocSec->addChunk(make(Page, &V[I], &V[0] + J)); I = J; Page = P; } if (I == J) return; RelocSec->addChunk(make(Page, &V[I], &V[0] + J)); } Index: vendor/lld/dist-release_80/ELF/ICF.cpp =================================================================== --- vendor/lld/dist-release_80/ELF/ICF.cpp (revision 343801) +++ vendor/lld/dist-release_80/ELF/ICF.cpp (revision 343802) @@ -1,509 +1,512 @@ //===- ICF.cpp ------------------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // ICF is short for Identical Code Folding. This is a size optimization to // identify and merge two or more read-only sections (typically functions) // that happened to have the same contents. It usually reduces output size // by a few percent. // // In ICF, two sections are considered identical if they have the same // section flags, section data, and relocations. Relocations are tricky, // because two relocations are considered the same if they have the same // relocation types, values, and if they point to the same sections *in // terms of ICF*. // // Here is an example. If foo and bar defined below are compiled to the // same machine instructions, ICF can and should merge the two, although // their relocations point to each other. // // void foo() { bar(); } // void bar() { foo(); } // // If you merge the two, their relocations point to the same section and // thus you know they are mergeable, but how do you know they are // mergeable in the first place? This is not an easy problem to solve. // // What we are doing in LLD is to partition sections into equivalence // classes. Sections in the same equivalence class when the algorithm // terminates are considered identical. Here are details: // // 1. First, we partition sections using their hash values as keys. Hash // values contain section types, section contents and numbers of // relocations. During this step, relocation targets are not taken into // account. We just put sections that apparently differ into different // equivalence classes. // // 2. Next, for each equivalence class, we visit sections to compare // relocation targets. Relocation targets are considered equivalent if // their targets are in the same equivalence class. Sections with // different relocation targets are put into different equivalence // clases. // // 3. If we split an equivalence class in step 2, two relocations // previously target the same equivalence class may now target // different equivalence classes. Therefore, we repeat step 2 until a // convergence is obtained. // // 4. For each equivalence class C, pick an arbitrary section in C, and // merge all the other sections in C with it. // // For small programs, this algorithm needs 3-5 iterations. For large // programs such as Chromium, it takes more than 20 iterations. // // This algorithm was mentioned as an "optimistic algorithm" in [1], // though gold implements a different algorithm than this. // // We parallelize each step so that multiple threads can work on different // equivalence classes concurrently. That gave us a large performance // boost when applying ICF on large programs. For example, MSVC link.exe // or GNU gold takes 10-20 seconds to apply ICF on Chromium, whose output // size is about 1.5 GB, but LLD can finish it in less than 2 seconds on a // 2.8 GHz 40 core machine. Even without threading, LLD's ICF is still // faster than MSVC or gold though. // // [1] Safe ICF: Pointer Safe and Unwinding aware Identical Code Folding // in the Gold Linker // http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36912.pdf // //===----------------------------------------------------------------------===// #include "ICF.h" #include "Config.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Writer.h" #include "lld/Common/Threads.h" #include "llvm/ADT/StringExtras.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/ELF.h" #include "llvm/Support/xxhash.h" #include #include using namespace lld; using namespace lld::elf; using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; namespace { template class ICF { public: void run(); private: void segregate(size_t Begin, size_t End, bool Constant); template 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 equalsConstant(const InputSection *A, const InputSection *B); bool equalsVariable(const InputSection *A, const InputSection *B); size_t findBoundary(size_t Begin, size_t End); void forEachClassRange(size_t Begin, size_t End, llvm::function_ref Fn); void forEachClass(llvm::function_ref Fn); std::vector Sections; // We repeat the main loop while `Repeat` is true. std::atomic Repeat; // The main loop counter. 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 // garbage. We read equivalence classes from slot 0 and write to slot 1. // So, Class[0] represents the current class, and Class[1] represents // the next class. On each iteration, we switch their roles and use them // alternately. // // Why are we doing this? Recall that other threads may be working on // other equivalence classes in parallel. They may read sections that we // are updating. We cannot update equivalence classes in place because // it breaks the invariance that all possibly-identical sections must be // in the same equivalence class at any moment. In other words, the for // loop to update equivalence classes is not atomic, and that is // observable from other threads. By writing new classes to other // places, we can keep the invariance. // // Below, `Current` has the index of the current class, and `Next` has // the index of the next class. If threading is enabled, they are either // (0, 1) or (1, 0). // // Note on single-thread: if that's the case, they are always (0, 0) // 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; }; } // Returns true if section S is subject of ICF. static bool isEligible(InputSection *S) { if (!S->Live || 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.")) 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) 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)) 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") 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)) return false; return true; } // Split an equivalence class into smaller classes. template 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). // // The algorithm is quadratic in the worst case, but that is not an // issue in practice because the number of the distinct sections in // each range is usually very small. 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); }); 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; // If we created a group, we need to iterate the main loop again. if (Mid != End) Repeat = true; 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)) return false; 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) continue; return false; } 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) return false; // Relocations referring to absolute symbols are constant-equal if their // values are equal. if (!DA->Section && !DB->Section && DA->Value + AddA == DB->Value + AddB) continue; if (!DA->Section || !DB->Section) return false; if (DA->Section->kind() != DB->Section->kind()) return false; // Relocations referring to InputSections are constant-equal if their // section offsets are equal. if (isa(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) return false; 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) return false; } return true; } // 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()) 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)) 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()); } // 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()); 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) continue; 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) continue; auto *X = dyn_cast(DA->Section); if (!X) continue; 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) return false; if (X->Class[Current] != Y->Class[Current]) return false; }; return true; } // 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()); } 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; } // Sections in the same equivalence class are contiguous in Sections // vector. Therefore, Sections vector can be considered as contiguous // groups of sections, grouped by the class. // // 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; } } // Call Fn on each equivalence class. template 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; return; } 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()); }); parallelForEachN(1, NumShards + 1, [&](size_t I) { if (Boundaries[I - 1] < Boundaries[I]) forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn); }); ++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]; +static void combineRelocHashes(unsigned Cnt, InputSection *IS, + ArrayRef Rels) { + uint32_t Hash = IS->Class[Cnt % 2]; 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]; + Hash += RelSec->Class[Cnt % 2]; } // Set MSB to 1 to avoid collisions with non-hash IDs. - IS->Class[0] = Hash | (1U << 31); + IS->Class[(Cnt + 1) % 2] = Hash | (1U << 31); } 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); // Initially, we use hash values to partition sections. parallelForEach(Sections, [&](InputSection *S) { - S->Class[1] = xxHash64(S->data()); + S->Class[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]; }); // Compare static contents and assign unique IDs for each static content. forEachClass([&](size_t Begin, size_t End) { segregate(Begin, End, true); }); // Split groups by comparing relocations until convergence is obtained. do { Repeat = false; forEachClass( [&](size_t Begin, size_t End) { segregate(Begin, End, false); }); } while (Repeat); 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) 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]); // 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; } }); } // ICF entry point function. template void elf::doIcf() { ICF().run(); } template void elf::doIcf(); template void elf::doIcf(); template void elf::doIcf(); template void elf::doIcf(); Index: vendor/lld/dist-release_80/ELF/InputFiles.cpp =================================================================== --- vendor/lld/dist-release_80/ELF/InputFiles.cpp (revision 343801) +++ vendor/lld/dist-release_80/ELF/InputFiles.cpp (revision 343802) @@ -1,1347 +1,1345 @@ //===- InputFiles.cpp -----------------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "InputSection.h" #include "LinkerScript.h" #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "llvm/ADT/STLExtras.h" #include "llvm/CodeGen/Analysis.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/LTO/LTO.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/ARMAttributeParser.h" #include "llvm/Support/ARMBuildAttributes.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::sys; using namespace llvm::sys::fs; 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; 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; } 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); log(Path); 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 if (Tar) Tar->append(relativeToRoot(Path), MBRef.getBuffer()); return MBRef; } // 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 + ")"; } template 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 it failed, lookup again as a variable. 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; } std::string InputFile::getSrcMsg(const Symbol &Sym, InputSectionBase &Sec, uint64_t Offset) { if (kind() != ObjKind) return ""; switch (Config->EKind) { default: llvm_unreachable("Invalid kind"); case ELF32LEKind: return getSrcMsgAux(cast>(*this), Sym, Sec, Offset); case ELF32BEKind: return getSrcMsgAux(cast>(*this), Sym, Sec, Offset); case ELF64LEKind: return getSrcMsgAux(cast>(*this), Sym, Sec, Offset); case ELF64BEKind: 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()); }); }; Expected ExpectedLT = Dwarf->getLineTableForUnit(CU.get(), Report); const DWARFDebugLine::LineTable *LT = nullptr; if (ExpectedLT) LT = *ExpectedLT; else Report(ExpectedLT.takeError()); if (!LT) continue; LineTables.push_back(LT); // 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) continue; // Skip if a local variable because we don't need them for generating // error messages. In general, only non-local symbols can fail to be // linked. if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0)) continue; // Get the source filename index for the variable. unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0); if (!LT->hasFileAtIndex(File)) continue; // Get the line number on which the variable is declared. unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0); // 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}}); } } } // Returns the pair of file name and line number describing location of data // object (variable, array, etc) definition. template Optional> 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()) 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)) return None; 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(); }); // 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; return None; } // Returns "", "foo.a(bar.o)" or "baz.o". std::string lld::toString(const InputFile *F) { if (!F) return ""; if (F->ToStringCache.empty()) { if (F->ArchiveName.empty()) F->ToStringCache = F->getName(); else F->ToStringCache = (F->ArchiveName + "(" + F->getName() + ")").str(); } 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; EMachine = getObj().getHeader()->e_machine; OSABI = getObj().getHeader()->e_ident[llvm::ELF::EI_OSABI]; } template typename ELFT::SymRange ELFFileBase::getGlobalELFSyms() { return makeArrayRef(ELFSyms.begin() + FirstGlobal, ELFSyms.end()); } template uint32_t ELFFileBase::getSectionIndex(const Elf_Sym &Sym) const { return CHECK(getObj().getSectionIndex(&Sym, ELFSyms, SymtabSHNDX), this); } 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()) fatal(toString(this) + ": invalid sh_info in symbol table"); StringTable = CHECK(getObj().getStringTableForSymtab(*Symtab, Sections), this); } template ObjFile::ObjFile(MemoryBufferRef M, StringRef ArchiveName) : ELFFileBase(Base::ObjKind, M) { this->ArchiveName = ArchiveName; } template ArrayRef ObjFile::getLocalSymbols() { if (this->Symbols.empty()) return {}; return makeArrayRef(this->Symbols).slice(1, this->FirstGlobal - 1); } template ArrayRef ObjFile::getGlobalSymbols() { return makeArrayRef(this->Symbols).slice(this->FirstGlobal); } template void ObjFile::parse(DenseSet &ComdatGroups) { // Read a section table. JustSymbols is usually false. if (this->JustSymbols) initializeJustSymbols(); else initializeSections(ComdatGroups); // Read a symbol table. initializeSymbols(); } // Sections with SHT_GROUP and comdat bits define comdat section groups. // 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); // As a special case, if a symbol is a section symbol and has no name, // we use a section name as a signature. // // Such SHT_GROUP sections are invalid from the perspective of the ELF // 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); -} - 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. // // Doing the same for -r would create a problem as it would combine sections // with different sh_entsize. One option would be to just copy every SHF_MERGE // section as is to the output. While this would produce a valid ELF file with // usable SHF_MERGE sections, tools like (llvm-)?dwarfdump get confused when // they see two .debug_str. We could have separate logic for combining // 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) 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) return false; // Check for sh_entsize. The ELF spec is not clear about the zero // sh_entsize. It says that "the member [sh_entsize] contains 0 if // 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) return false; 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)) return false; if (Flags & SHF_WRITE) fatal(toString(this) + ": writable SHF_MERGE section is not supported"); return true; } // This is for --just-symbols. // // --just-symbols is a very minor feature that allows you to link your // output against other existing program, so that if you load both your // program and the other program into memory, your output can refer the // other program's symbols. // // 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()); for (const Elf_Shdr &Sec : ObjSections) { if (Sec.sh_type != SHT_SYMTAB) continue; this->initSymtab(ObjSections, &Sec); return; } } 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) continue; const Elf_Shdr &Sec = ObjSections[I]; if (Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE) CGProfile = check( this->getObj().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) { // 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) warn(toString(this) + ": --icf=safe is incompatible with object " "files created using objcopy or ld -r"); } this->Sections[I] = &InputSection::Discarded; continue; } 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. + 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 = ComdatGroups.insert(CachedHashStringRef(Signature)).second; if (IsNew) { if (Config->Relocatable) this->Sections[I] = createInputSection(Sec); - continue; + continue; } + // Otherwise, discard group members. - for (uint32_t SecIndex : Entries) { + 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; } break; } case SHT_SYMTAB: this->initSymtab(ObjSections, &Sec); break; case SHT_SYMTAB_SHNDX: this->SymtabSHNDX = CHECK(Obj.getSHNDXTable(Sec, ObjSections), this); break; case SHT_STRTAB: case SHT_NULL: break; default: 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) fatal(toString(this) + ": 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 + " with SHF_LINK_ORDER should not refer a non-regular " "section: " + toString(LinkSec)); } } } // 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)) // 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) // don't have the attribute so we do not consider an implicit value of 0 // as a clash. return; unsigned VFPArgs = Attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args); ARMVFPArgKind Arg; switch (VFPArgs) { case ARMBuildAttrs::BaseAAPCS: Arg = ARMVFPArgKind::Base; break; case ARMBuildAttrs::HardFPAAPCS: Arg = ARMVFPArgKind::VFP; break; case ARMBuildAttrs::ToolChainFPPCS: // Tool chain specific convention that conforms to neither AAPCS variant. 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)); 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"); else Config->ARMVFPArgs = Arg; } // The ARM support in lld makes some use of instructions that are not available // on all ARM architectures. Namely: // - Use of BLX instruction for interworking between ARM and Thumb state. // - Use of the extended Thumb branch encoding in relocation. // - Use of the MOVT/MOVW instructions in Thumb Thunks. // The ARM Attributes section contains information about the architecture chosen // at compile time. We follow the convention that if at least one input object // is compiled with an architecture that supports these features then lld is // permitted to use them. static void updateSupportedARMFeatures(const ARMAttributeParser &Attributes) { if (!Attributes.hasAttribute(ARMBuildAttrs::CPU_arch)) return; auto Arch = Attributes.getAttributeValue(ARMBuildAttrs::CPU_arch); switch (Arch) { case ARMBuildAttrs::Pre_v4: case ARMBuildAttrs::v4: case ARMBuildAttrs::v4T: // Architectures prior to v5 do not support BLX instruction break; case ARMBuildAttrs::v5T: case ARMBuildAttrs::v5TE: case ARMBuildAttrs::v5TEJ: case ARMBuildAttrs::v6: case ARMBuildAttrs::v6KZ: case ARMBuildAttrs::v6K: Config->ARMHasBlx = true; // Architectures used in pre-Cortex processors do not support // The J1 = 1 J2 = 1 Thumb branch range extension, with the exception // of Architecture v6T2 (arm1156t2-s and arm1156t2f-s) that do. break; default: // All other Architectures have BLX and extended branch encoding Config->ARMHasBlx = true; Config->ARMJ1J2BranchEncoding = true; if (Arch != ARMBuildAttrs::v6_M && Arch != ARMBuildAttrs::v6S_M) // All Architectures used in Cortex processors with the exception // of v6-M and v6S-M have the MOVT and MOVW instructions. Config->ARMHasMovtMovw = true; break; } } template 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) return nullptr; if (!Target) fatal(toString(this) + ": unsupported relocation reference"); 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); } template InputSectionBase *ObjFile::createInputSection(const Elf_Shdr &Sec) { StringRef Name = getSectionName(Sec); switch (Sec.sh_type) { case SHT_ARM_ATTRIBUTES: { 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); // 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; } return &InputSection::Discarded; } case SHT_RELA: case SHT_REL: { // Find a relocation target section and associate this section with that. // Target may have been discarded if it is in a different section group // 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) 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); // We want to add a dependency to target, similar like we do for // -emit-relocs below. This is useful for the case when linker script // contains the "/DISCARD/". It is perhaps uncommon to use a script with // -r, but we faced it in the Linux kernel and have to handle such case // and not to crash. Target->DependentSections.push_back(RelocSec); return RelocSec; } if (Target->FirstRelocation) fatal(toString(this) + ": multiple relocation sections to one section are not supported"); // ELF spec allows mergeable sections with relocations, but they are // rare, and it is in practice hard to merge such sections by contents, // 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 (Sec.sh_type == SHT_RELA) { ArrayRef Rels = CHECK(this->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; } 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); // We will not emit relocation section if target was discarded. Target->DependentSections.push_back(RelocSec); return RelocSec; } return nullptr; } } // The GNU linker uses .note.GNU-stack section as a marker indicating // that the code in the object file does not expect that the stack is // executable (in terms of NX bit). If all input files have the marker, // the GNU linker adds a PT_GNU_STACK segment to tells the loader to // make the stack non-executable. Most object files have this section as // of 2017. // // But making the stack non-executable is a norm today for security // reasons. Failure to do so may result in a serious security issue. // Therefore, we make LLD always add PT_GNU_STACK unless it is // 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; // 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) { error("cannot mix split-stack and non-split-stack in a relocatable link"); 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; } // The linkonce feature is a sort of proto-comdat. Some glibc i386 object // files contain definitions of symbol "__x86.get_pc_thunk.bx" in linkonce // sections. Drop those sections to avoid duplicate symbol errors. // FIXME: This is glibc PR20543, we should remove this hack once that has been // fixed for a while. - if (Name.startswith(".gnu.linkonce.")) + 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; // 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 (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); } 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); if (this->StringTable.size() <= Sym->st_name) fatal(toString(this) + ": invalid symbol name offset"); StringRefZ Name = this->StringTable.data() + Sym->st_name; if (Sym->st_shndx == SHN_UNDEF) return make(this, Name, Binding, StOther, Type); return make(this, Name, Binding, StOther, Type, Value, Size, Sec); } 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); } 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); } } 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); } // 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) + ": could not get the member for symbol " + Sym.getName()); if (!Seen.insert(C.getChildOffset()).second) return nullptr; MemoryBufferRef MB = CHECK(C.getMemoryBufferRef(), toString(this) + ": could not get the buffer for the member defining symbol " + Sym.getName()); 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; } 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; } // 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) 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); // 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; } 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; } // Fully parse the shared object file. This must be called after parseSoName(). // // This function parses symbol versions. If a DSO has version information, // the file has a ".gnu.version_d" section which contains symbol version // definitions. Each symbol is associated to one version through a table in // ".gnu.version" section. That table is a parallel array for the symbol // table, and each table entry contains an index in ".gnu.version_d". // // The special index 0 is reserved for VERF_NDX_LOCAL and 1 is for // VER_NDX_GLOBAL. There's no table entry for these special versions in // ".gnu.version_d". // // 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); // 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; // 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]; // ELF spec requires that all local symbols precede weak or global // symbols in each symbol table, and the index of first non-local symbol // is stored to sh_info. If a local symbol appears after some non-local // symbol, that's a violation of the spec. StringRef Name = CHECK(Sym.getName(this->StringTable), this); if (Sym.getBinding() == STB_LOCAL) { warn("found local symbol '" + Name + "' in global part of symbol table in file " + toString(this)); continue; } if (Sym.isUndefined()) { Symbol *S = Symtab->addUndefined(Name, Sym.getBinding(), Sym.st_other, Sym.getType(), /*CanOmitFromDynSym=*/false, this); 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") continue; uint64_t Alignment = getAlignment(Sections, Sym); if (!(Versyms[I] & VERSYM_HIDDEN)) Symtab->addShared(Name, *this, Sym, Alignment, Idx); // Also add the symbol with the versioned name to handle undefined symbols // with explicit versions. 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 " + 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); } } 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()) { case Triple::aarch64: return EM_AARCH64; case Triple::amdgcn: case Triple::r600: return EM_AMDGPU; case Triple::arm: case Triple::thumb: return EM_ARM; case Triple::avr: return EM_AVR; case Triple::mips: case Triple::mipsel: case Triple::mips64: case Triple::mips64el: return EM_MIPS; case Triple::msp430: return EM_MSP430; case Triple::ppc: return EM_PPC; case Triple::ppc64: case Triple::ppc64le: return EM_PPC64; case Triple::x86: return T.isOSIAMCU() ? EM_IAMCU : EM_386; case Triple::x86_64: return EM_X86_64; default: 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; 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), this); Triple T(Obj->getTargetTriple()); EKind = getBitcodeELFKind(T); EMachine = getBitcodeMachineKind(MB.getBufferIdentifier(), T); } static uint8_t mapVisibility(GlobalValue::VisibilityTypes GvVisibility) { switch (GvVisibility) { case GlobalValue::DefaultVisibility: return STV_DEFAULT; case GlobalValue::HiddenVisibility: return STV_HIDDEN; case GlobalValue::ProtectedVisibility: return STV_PROTECTED; } llvm_unreachable("unknown visibility"); } 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); } template void BitcodeFile::parse(DenseSet &ComdatGroups) { std::vector KeptComdats; for (StringRef S : Obj->getComdatTable()) KeptComdats.push_back(ComdatGroups.insert(CachedHashStringRef(S)).second); for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) Symbols.push_back(createBitcodeSymbol(KeptComdats, ObjSym, *this)); } 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"); 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"); if (Size == ELFCLASS32) return (Endian == ELFDATA2LSB) ? ELF32LEKind : ELF32BEKind; return (Endian == ELFDATA2LSB) ? ELF64LEKind : ELF64BEKind; } 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); // 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); } InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName, uint64_t OffsetInArchive) { if (isBitcode(MB)) return make(MB, ArchiveName, OffsetInArchive); switch (getELFKind(MB)) { case ELF32LEKind: return make>(MB, ArchiveName); case ELF32BEKind: return make>(MB, ArchiveName); case ELF64LEKind: return make>(MB, ArchiveName); case ELF64BEKind: 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"); } } MemoryBufferRef LazyObjFile::getBuffer() { if (AddedToLink) return MemoryBufferRef(); AddedToLink = true; return MB; } InputFile *LazyObjFile::fetch() { MemoryBufferRef MBRef = getBuffer(); if (MBRef.getBuffer().empty()) return nullptr; InputFile *File = createObjectFile(MBRef, ArchiveName, OffsetInArchive); File->GroupId = GroupId; return File; } template void LazyObjFile::parse() { // 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); return; } if (getELFKind(this->MB) != Config->EKind) { error("incompatible file: " + this->MB.getBufferIdentifier()); return; } 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) continue; typename ELFT::SymRange Syms = CHECK(Obj.symbols(&Sec), this); uint32_t FirstGlobal = Sec.sh_info; StringRef StringTable = CHECK(Obj.getStringTableForSymtab(Sec, Sections), this); for (const typename ELFT::Sym &Sym : Syms.slice(FirstGlobal)) if (Sym.st_shndx != SHN_UNDEF) Symtab->addLazyObject(CHECK(Sym.getName(StringTable), this), *this); return; } } 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; } 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 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; Index: vendor/lld/dist-release_80/ELF/InputFiles.h =================================================================== --- vendor/lld/dist-release_80/ELF/InputFiles.h (revision 343801) +++ vendor/lld/dist-release_80/ELF/InputFiles.h (revision 343802) @@ -1,383 +1,382 @@ //===- InputFiles.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_INPUT_FILES_H #define LLD_ELF_INPUT_FILES_H #include "Config.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/LLVM.h" #include "lld/Common/Reproduce.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/IR/Comdat.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ELF.h" #include "llvm/Object/IRObjectFile.h" #include "llvm/Support/Threading.h" #include namespace llvm { class TarWriter; struct DILineInfo; namespace lto { class InputFile; } } // namespace llvm namespace lld { namespace elf { class InputFile; class InputSectionBase; } // Returns "", "foo.a(bar.o)" or "baz.o". std::string toString(const elf::InputFile *F); namespace elf { using llvm::object::Archive; class Symbol; // If -reproduce option is given, all input files are written // to this tar archive. extern std::unique_ptr Tar; // Opens a given file. llvm::Optional readFile(StringRef Path); // The root class of input files. class InputFile { public: enum Kind { ObjKind, SharedKind, LazyObjKind, ArchiveKind, BitcodeKind, BinaryKind, }; Kind kind() const { return FileKind; } bool isElf() const { Kind K = kind(); return K == ObjKind || K == SharedKind; } 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; } // 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; } // 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; // 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; // Cache for toString(). Only toString() should use this member. mutable std::string ToStringCache; 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 // 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; // Index of MIPS GOT built for this file. llvm::Optional MipsGotIndex; protected: InputFile(Kind K, MemoryBufferRef M); std::vector Sections; std::vector Symbols; private: const Kind FileKind; }; template 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(); } 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; Elf_Sym_Range getGlobalELFSyms(); Elf_Sym_Range getELFSyms() const { return ELFSyms; } protected: ArrayRef ELFSyms; uint32_t FirstGlobal = 0; ArrayRef SymtabSHNDX; StringRef StringTable; void initSymtab(ArrayRef Sections, const Elf_Shdr *Symtab); }; // .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); public: static bool classof(const InputFile *F) { return F->kind() == Base::ObjKind; } ArrayRef getLocalSymbols(); ArrayRef getGlobalSymbols(); ObjFile(MemoryBufferRef M, StringRef ArchiveName); void parse(llvm::DenseSet &ComdatGroups); Symbol &getSymbol(uint32_t SymbolIndex) const { if (SymbolIndex >= this->Symbols.size()) fatal(toString(this) + ": invalid symbol index"); return *this->Symbols[SymbolIndex]; } 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); // 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; // 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; // True if the file defines functions compiled with // -fsplit-stack. Usually 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; // Pointer to this input file's .llvm_addrsig section, if it has one. const Elf_Shdr *AddrsigSec = nullptr; // SHT_LLVM_CALL_GRAPH_PROFILE table ArrayRef CGProfile; private: void initializeSections(llvm::DenseSet &ComdatGroups); 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); // .shstrtab contents. 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; struct VarLoc { const llvm::DWARFDebugLine::LineTable *LT; unsigned File; unsigned Line; }; llvm::DenseMap VariableLoc; llvm::once_flag InitDwarfLine; }; // LazyObjFile is analogous to ArchiveFile in the sense that // the file contains lazy symbols. The difference is that // LazyObjFile wraps a single file instead of multiple files. // // This class is used for --start-lib and --end-lib options which // instruct the linker to link object files between them with the // archive file semantics. class LazyObjFile : public InputFile { public: 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; } template void parse(); MemoryBufferRef getBuffer(); InputFile *fetch(); bool AddedToLink = false; private: 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(); // 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 // more than once.) InputFile *fetch(const Archive::Symbol &Sym); private: 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; }; // .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; public: std::vector Verdefs; std::string SoName; static bool classof(const InputFile *F) { return F->kind() == Base::SharedKind; } SharedFile(MemoryBufferRef M, StringRef DefaultSoName); void parseSoName(); void parseRest(); uint32_t getAlignment(ArrayRef Sections, const Elf_Sym &Sym); std::vector parseVerdefs(); std::vector parseVersyms(); struct NeededVer { // The string table offset of the version name in the output file. size_t StrTab; // The version identifier for this version name. uint16_t Index; }; // Mapping from Elf_Verdef data structures to information about Elf_Vernaux // data structures in the output file. std::map VerdefMap; // Used for --as-needed bool IsNeeded; }; class BinaryFile : public InputFile { public: 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); inline bool isBitcode(MemoryBufferRef MB) { return identify_magic(MB.getBuffer()) == llvm::file_magic::bitcode; } 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; } // namespace elf } // namespace lld #endif Index: vendor/lld/dist-release_80/ELF/ScriptParser.cpp =================================================================== --- vendor/lld/dist-release_80/ELF/ScriptParser.cpp (revision 343801) +++ vendor/lld/dist-release_80/ELF/ScriptParser.cpp (revision 343802) @@ -1,1553 +1,1544 @@ //===- ScriptParser.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 recursive-descendent parser for linker scripts. // Parsed results are stored to Config and Script global objects. // //===----------------------------------------------------------------------===// #include "ScriptParser.h" #include "Config.h" #include "Driver.h" #include "InputSection.h" #include "LinkerScript.h" #include "OutputSections.h" #include "ScriptLexer.h" #include "Symbols.h" #include "Target.h" #include "lld/Common/Memory.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include #include #include using namespace llvm; using namespace llvm::ELF; using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; static bool isUnderSysroot(StringRef Path); namespace { class ScriptParser final : ScriptLexer { public: ScriptParser(MemoryBufferRef MB) : ScriptLexer(MB), IsUnderSysroot(isUnderSysroot(MB.getBufferIdentifier())) {} void readLinkerScript(); void readVersionScript(); void readDynamicList(); void readDefsym(StringRef Name); private: void addFile(StringRef Path); void readAsNeeded(); void readEntry(); void readExtern(); void readGroup(); void readInclude(); void readInput(); void readMemory(); void readOutput(); void readOutputArch(); void readOutputFormat(); void readPhdrs(); void readRegionAlias(); void readSearchDir(); void readSections(); void readTarget(); void readVersion(); void readVersionScriptCommand(); SymbolAssignment *readSymbolAssignment(StringRef Name); ByteCommand *readByteCommand(StringRef Tok); std::array readFill(); std::array parseFill(StringRef Tok); bool readSectionDirective(OutputSection *Cmd, StringRef Tok1, StringRef Tok2); void readSectionAddressType(OutputSection *Cmd); OutputSection *readOverlaySectionDescription(); OutputSection *readOutputSectionDescription(StringRef OutSec); std::vector readOverlay(); std::vector readOutputSectionPhdrs(); InputSectionDescription *readInputSectionDescription(StringRef Tok); StringMatcher readFilePatterns(); std::vector readInputSectionsList(); InputSectionDescription *readInputSectionRules(StringRef FilePattern); unsigned readPhdrType(); SortSectionPolicy readSortKind(); SymbolAssignment *readProvideHidden(bool Provide, bool Hidden); SymbolAssignment *readAssignment(StringRef Tok); - std::tuple readBfdName(); void readSort(); Expr readAssert(); Expr readConstant(); Expr getPageSize(); uint64_t readMemoryAssignment(StringRef, StringRef, StringRef); std::pair readMemoryAttributes(); Expr combine(StringRef Op, Expr L, Expr R); Expr readExpr(); Expr readExpr1(Expr Lhs, int MinPrec); StringRef readParenLiteral(); Expr readPrimary(); Expr readTernary(Expr Cond); Expr readParenExpr(); // For parsing version script. std::vector readVersionExtern(); void readAnonymousDeclaration(); void readVersionDeclaration(StringRef VerStr); std::pair, std::vector> readSymbols(); // True if a script being read is in a subdirectory specified by -sysroot. bool IsUnderSysroot; // A set to detect an INCLUDE() cycle. StringSet<> Seen; }; } // namespace static StringRef unquote(StringRef S) { if (S.startswith("\"")) return S.substr(1, S.size() - 2); return S; } static bool isUnderSysroot(StringRef Path) { if (Config->Sysroot == "") return false; for (; !Path.empty(); Path = sys::path::parent_path(Path)) if (sys::fs::equivalent(Config->Sysroot, Path)) return true; return false; } // Some operations only support one non absolute value. Move the // absolute one to the right hand side for convenience. static void moveAbsRight(ExprValue &A, ExprValue &B) { if (A.Sec == nullptr || (A.ForceAbsolute && !B.isAbsolute())) std::swap(A, B); if (!B.isAbsolute()) error(A.Loc + ": at least one side of the expression must be absolute"); } static ExprValue add(ExprValue A, ExprValue B) { moveAbsRight(A, B); return {A.Sec, A.ForceAbsolute, A.getSectionOffset() + B.getValue(), A.Loc}; } static ExprValue sub(ExprValue A, ExprValue B) { // The distance between two symbols in sections is absolute. if (!A.isAbsolute() && !B.isAbsolute()) return A.getValue() - B.getValue(); return {A.Sec, false, A.getSectionOffset() - B.getValue(), A.Loc}; } static ExprValue bitAnd(ExprValue A, ExprValue B) { moveAbsRight(A, B); return {A.Sec, A.ForceAbsolute, (A.getValue() & B.getValue()) - A.getSecAddr(), A.Loc}; } static ExprValue bitOr(ExprValue A, ExprValue B) { moveAbsRight(A, B); return {A.Sec, A.ForceAbsolute, (A.getValue() | B.getValue()) - A.getSecAddr(), A.Loc}; } void ScriptParser::readDynamicList() { Config->HasDynamicList = true; expect("{"); std::vector Locals; std::vector Globals; std::tie(Locals, Globals) = readSymbols(); expect(";"); if (!atEOF()) { setError("EOF expected, but got " + next()); return; } if (!Locals.empty()) { setError("\"local:\" scope not supported in --dynamic-list"); return; } for (SymbolVersion V : Globals) Config->DynamicList.push_back(V); } void ScriptParser::readVersionScript() { readVersionScriptCommand(); if (!atEOF()) setError("EOF expected, but got " + next()); } void ScriptParser::readVersionScriptCommand() { if (consume("{")) { readAnonymousDeclaration(); return; } while (!atEOF() && !errorCount() && peek() != "}") { StringRef VerStr = next(); if (VerStr == "{") { setError("anonymous version definition is used in " "combination with other version definitions"); return; } expect("{"); readVersionDeclaration(VerStr); } } void ScriptParser::readVersion() { expect("{"); readVersionScriptCommand(); expect("}"); } void ScriptParser::readLinkerScript() { while (!atEOF()) { StringRef Tok = next(); if (Tok == ";") continue; if (Tok == "ENTRY") { readEntry(); } else if (Tok == "EXTERN") { readExtern(); } else if (Tok == "GROUP") { readGroup(); } else if (Tok == "INCLUDE") { readInclude(); } else if (Tok == "INPUT") { readInput(); } else if (Tok == "MEMORY") { readMemory(); } else if (Tok == "OUTPUT") { readOutput(); } else if (Tok == "OUTPUT_ARCH") { readOutputArch(); } else if (Tok == "OUTPUT_FORMAT") { readOutputFormat(); } else if (Tok == "PHDRS") { readPhdrs(); } else if (Tok == "REGION_ALIAS") { readRegionAlias(); } else if (Tok == "SEARCH_DIR") { readSearchDir(); } else if (Tok == "SECTIONS") { readSections(); } else if (Tok == "TARGET") { readTarget(); } else if (Tok == "VERSION") { readVersion(); } else if (SymbolAssignment *Cmd = readAssignment(Tok)) { Script->SectionCommands.push_back(Cmd); } else { setError("unknown directive: " + Tok); } } } void ScriptParser::readDefsym(StringRef Name) { if (errorCount()) return; Expr E = readExpr(); if (!atEOF()) setError("EOF expected, but got " + next()); SymbolAssignment *Cmd = make(Name, E, getCurrentLocation()); Script->SectionCommands.push_back(Cmd); } void ScriptParser::addFile(StringRef S) { if (IsUnderSysroot && S.startswith("/")) { SmallString<128> PathData; StringRef Path = (Config->Sysroot + S).toStringRef(PathData); if (sys::fs::exists(Path)) { Driver->addFile(Saver.save(Path), /*WithLOption=*/false); return; } } if (S.startswith("/")) { Driver->addFile(S, /*WithLOption=*/false); } else if (S.startswith("=")) { if (Config->Sysroot.empty()) Driver->addFile(S.substr(1), /*WithLOption=*/false); else Driver->addFile(Saver.save(Config->Sysroot + "/" + S.substr(1)), /*WithLOption=*/false); } else if (S.startswith("-l")) { Driver->addLibrary(S.substr(2)); } else if (sys::fs::exists(S)) { Driver->addFile(S, /*WithLOption=*/false); } else { if (Optional Path = findFromSearchPaths(S)) Driver->addFile(Saver.save(*Path), /*WithLOption=*/true); else setError("unable to find " + S); } } void ScriptParser::readAsNeeded() { expect("("); bool Orig = Config->AsNeeded; Config->AsNeeded = true; while (!errorCount() && !consume(")")) addFile(unquote(next())); Config->AsNeeded = Orig; } void ScriptParser::readEntry() { // -e takes predecence over ENTRY(). expect("("); StringRef Tok = next(); if (Config->Entry.empty()) Config->Entry = Tok; expect(")"); } void ScriptParser::readExtern() { expect("("); while (!errorCount() && !consume(")")) Config->Undefined.push_back(next()); } void ScriptParser::readGroup() { bool Orig = InputFile::IsInGroup; InputFile::IsInGroup = true; readInput(); InputFile::IsInGroup = Orig; if (!Orig) ++InputFile::NextGroupId; } void ScriptParser::readInclude() { StringRef Tok = unquote(next()); if (!Seen.insert(Tok).second) { setError("there is a cycle in linker script INCLUDEs"); return; } if (Optional Path = searchScript(Tok)) { if (Optional MB = readFile(*Path)) tokenize(*MB); return; } setError("cannot find linker script " + Tok); } void ScriptParser::readInput() { expect("("); while (!errorCount() && !consume(")")) { if (consume("AS_NEEDED")) readAsNeeded(); else addFile(unquote(next())); } } void ScriptParser::readOutput() { // -o takes predecence over OUTPUT(). expect("("); StringRef Tok = next(); if (Config->OutputFile.empty()) Config->OutputFile = unquote(Tok); expect(")"); } void ScriptParser::readOutputArch() { // OUTPUT_ARCH is ignored for now. expect("("); while (!errorCount() && !consume(")")) skip(); } -std::tuple ScriptParser::readBfdName() { - StringRef S = unquote(next()); - if (S == "elf32-i386") - return std::make_tuple(ELF32LEKind, EM_386, false); - if (S == "elf32-iamcu") - return std::make_tuple(ELF32LEKind, EM_IAMCU, false); - if (S == "elf32-littlearm") - return std::make_tuple(ELF32LEKind, EM_ARM, false); - if (S == "elf32-x86-64") - return std::make_tuple(ELF32LEKind, EM_X86_64, false); - if (S == "elf64-littleaarch64") - return std::make_tuple(ELF64LEKind, EM_AARCH64, false); - if (S == "elf64-powerpc") - return std::make_tuple(ELF64BEKind, EM_PPC64, false); - if (S == "elf64-powerpcle") - return std::make_tuple(ELF64LEKind, EM_PPC64, false); - if (S == "elf64-x86-64") - return std::make_tuple(ELF64LEKind, EM_X86_64, false); - if (S == "elf32-tradbigmips") - return std::make_tuple(ELF32BEKind, EM_MIPS, false); - if (S == "elf32-ntradbigmips") - return std::make_tuple(ELF32BEKind, EM_MIPS, true); - if (S == "elf32-tradlittlemips") - return std::make_tuple(ELF32LEKind, EM_MIPS, false); - if (S == "elf32-ntradlittlemips") - return std::make_tuple(ELF32LEKind, EM_MIPS, true); - if (S == "elf64-tradbigmips") - return std::make_tuple(ELF64BEKind, EM_MIPS, false); - if (S == "elf64-tradlittlemips") - return std::make_tuple(ELF64LEKind, EM_MIPS, false); - - setError("unknown output format name: " + S); - return std::make_tuple(ELFNoneKind, EM_NONE, false); +static std::pair parseBfdName(StringRef S) { + return StringSwitch>(S) + .Case("elf32-i386", {ELF32LEKind, EM_386}) + .Case("elf32-iamcu", {ELF32LEKind, EM_IAMCU}) + .Case("elf32-littlearm", {ELF32LEKind, EM_ARM}) + .Case("elf32-x86-64", {ELF32LEKind, EM_X86_64}) + .Case("elf64-aarch64", {ELF64LEKind, EM_AARCH64}) + .Case("elf64-littleaarch64", {ELF64LEKind, EM_AARCH64}) + .Case("elf64-powerpc", {ELF64BEKind, EM_PPC64}) + .Case("elf64-powerpcle", {ELF64LEKind, EM_PPC64}) + .Case("elf64-x86-64", {ELF64LEKind, EM_X86_64}) + .Case("elf32-tradbigmips", {ELF32BEKind, EM_MIPS}) + .Case("elf32-ntradbigmips", {ELF32BEKind, EM_MIPS}) + .Case("elf32-tradlittlemips", {ELF32LEKind, EM_MIPS}) + .Case("elf32-ntradlittlemips", {ELF32LEKind, EM_MIPS}) + .Case("elf64-tradbigmips", {ELF64BEKind, EM_MIPS}) + .Case("elf64-tradlittlemips", {ELF64LEKind, EM_MIPS}) + .Default({ELFNoneKind, EM_NONE}); } // Parse OUTPUT_FORMAT(bfdname) or OUTPUT_FORMAT(bfdname, big, little). // Currently we ignore big and little parameters. void ScriptParser::readOutputFormat() { expect("("); - std::tuple BfdTuple = readBfdName(); - if (Config->EKind == ELFNoneKind) - std::tie(Config->EKind, Config->EMachine, Config->MipsN32Abi) = BfdTuple; + StringRef Name = unquote(next()); + StringRef S = Name; + if (S.consume_back("-freebsd")) + Config->OSABI = ELFOSABI_FREEBSD; + + std::tie(Config->EKind, Config->EMachine) = parseBfdName(S); + if (Config->EMachine == EM_NONE) + setError("unknown output format name: " + Name); + if (S == "elf32-ntradlittlemips" || S == "elf32-ntradbigmips") + Config->MipsN32Abi = true; if (consume(")")) return; expect(","); skip(); expect(","); skip(); expect(")"); } void ScriptParser::readPhdrs() { expect("{"); while (!errorCount() && !consume("}")) { PhdrsCommand Cmd; Cmd.Name = next(); Cmd.Type = readPhdrType(); while (!errorCount() && !consume(";")) { if (consume("FILEHDR")) Cmd.HasFilehdr = true; else if (consume("PHDRS")) Cmd.HasPhdrs = true; else if (consume("AT")) Cmd.LMAExpr = readParenExpr(); else if (consume("FLAGS")) Cmd.Flags = readParenExpr()().getValue(); else setError("unexpected header attribute: " + next()); } Script->PhdrsCommands.push_back(Cmd); } } void ScriptParser::readRegionAlias() { expect("("); StringRef Alias = unquote(next()); expect(","); StringRef Name = next(); expect(")"); if (Script->MemoryRegions.count(Alias)) setError("redefinition of memory region '" + Alias + "'"); if (!Script->MemoryRegions.count(Name)) setError("memory region '" + Name + "' is not defined"); Script->MemoryRegions.insert({Alias, Script->MemoryRegions[Name]}); } void ScriptParser::readSearchDir() { expect("("); StringRef Tok = next(); if (!Config->Nostdlib) Config->SearchPaths.push_back(unquote(Tok)); expect(")"); } // This reads an overlay description. Overlays are used to describe output // sections that use the same virtual memory range and normally would trigger // linker's sections sanity check failures. // https://sourceware.org/binutils/docs/ld/Overlay-Description.html#Overlay-Description std::vector ScriptParser::readOverlay() { // VA and LMA expressions are optional, though for simplicity of // implementation we assume they are not. That is what OVERLAY was designed // for first of all: to allow sections with overlapping VAs at different LMAs. Expr AddrExpr = readExpr(); expect(":"); expect("AT"); Expr LMAExpr = readParenExpr(); expect("{"); std::vector V; OutputSection *Prev = nullptr; while (!errorCount() && !consume("}")) { // VA is the same for all sections. The LMAs are consecutive in memory // starting from the base load address specified. OutputSection *OS = readOverlaySectionDescription(); OS->AddrExpr = AddrExpr; if (Prev) OS->LMAExpr = [=] { return Prev->getLMA() + Prev->Size; }; else OS->LMAExpr = LMAExpr; V.push_back(OS); Prev = OS; } // According to the specification, at the end of the overlay, the location // counter should be equal to the overlay base address plus size of the // largest section seen in the overlay. // Here we want to create the Dot assignment command to achieve that. Expr MoveDot = [=] { uint64_t Max = 0; for (BaseCommand *Cmd : V) Max = std::max(Max, cast(Cmd)->Size); return AddrExpr().getValue() + Max; }; V.push_back(make(".", MoveDot, getCurrentLocation())); return V; } void ScriptParser::readSections() { Script->HasSectionsCommand = true; // -no-rosegment is used to avoid placing read only non-executable sections in // their own segment. We do the same if SECTIONS command is present in linker // script. See comment for computeFlags(). Config->SingleRoRx = true; expect("{"); std::vector V; while (!errorCount() && !consume("}")) { StringRef Tok = next(); if (Tok == "OVERLAY") { for (BaseCommand *Cmd : readOverlay()) V.push_back(Cmd); continue; } else if (Tok == "INCLUDE") { readInclude(); continue; } if (BaseCommand *Cmd = readAssignment(Tok)) V.push_back(Cmd); else V.push_back(readOutputSectionDescription(Tok)); } if (!atEOF() && consume("INSERT")) { std::vector *Dest = nullptr; if (consume("AFTER")) Dest = &Script->InsertAfterCommands[next()]; else if (consume("BEFORE")) Dest = &Script->InsertBeforeCommands[next()]; else setError("expected AFTER/BEFORE, but got '" + next() + "'"); if (Dest) Dest->insert(Dest->end(), V.begin(), V.end()); return; } Script->SectionCommands.insert(Script->SectionCommands.end(), V.begin(), V.end()); } void ScriptParser::readTarget() { // TARGET(foo) is an alias for "--format foo". Unlike GNU linkers, // we accept only a limited set of BFD names (i.e. "elf" or "binary") // for --format. We recognize only /^elf/ and "binary" in the linker // script as well. expect("("); StringRef Tok = next(); expect(")"); if (Tok.startswith("elf")) Config->FormatBinary = false; else if (Tok == "binary") Config->FormatBinary = true; else setError("unknown target: " + Tok); } static int precedence(StringRef Op) { return StringSwitch(Op) .Cases("*", "/", "%", 8) .Cases("+", "-", 7) .Cases("<<", ">>", 6) .Cases("<", "<=", ">", ">=", "==", "!=", 5) .Case("&", 4) .Case("|", 3) .Case("&&", 2) .Case("||", 1) .Default(-1); } StringMatcher ScriptParser::readFilePatterns() { std::vector V; while (!errorCount() && !consume(")")) V.push_back(next()); return StringMatcher(V); } SortSectionPolicy ScriptParser::readSortKind() { if (consume("SORT") || consume("SORT_BY_NAME")) return SortSectionPolicy::Name; if (consume("SORT_BY_ALIGNMENT")) return SortSectionPolicy::Alignment; if (consume("SORT_BY_INIT_PRIORITY")) return SortSectionPolicy::Priority; if (consume("SORT_NONE")) return SortSectionPolicy::None; return SortSectionPolicy::Default; } // Reads SECTIONS command contents in the following form: // // ::= * // ::= ? // ::= "EXCLUDE_FILE" "(" + ")" // // For example, // // *(.foo EXCLUDE_FILE (a.o) .bar EXCLUDE_FILE (b.o) .baz) // // is parsed as ".foo", ".bar" with "a.o", and ".baz" with "b.o". // The semantics of that is section .foo in any file, section .bar in // any file but a.o, and section .baz in any file but b.o. std::vector ScriptParser::readInputSectionsList() { std::vector Ret; while (!errorCount() && peek() != ")") { StringMatcher ExcludeFilePat; if (consume("EXCLUDE_FILE")) { expect("("); ExcludeFilePat = readFilePatterns(); } std::vector V; while (!errorCount() && peek() != ")" && peek() != "EXCLUDE_FILE") V.push_back(next()); if (!V.empty()) Ret.push_back({std::move(ExcludeFilePat), StringMatcher(V)}); else setError("section pattern is expected"); } return Ret; } // Reads contents of "SECTIONS" directive. That directive contains a // list of glob patterns for input sections. The grammar is as follows. // // ::= // | "(" ")" // | "(" "(" ")" ")" // // ::= "SORT" | "SORT_BY_NAME" | "SORT_BY_ALIGNMENT" // | "SORT_BY_INIT_PRIORITY" | "SORT_NONE" // // is parsed by readInputSectionsList(). InputSectionDescription * ScriptParser::readInputSectionRules(StringRef FilePattern) { auto *Cmd = make(FilePattern); expect("("); while (!errorCount() && !consume(")")) { SortSectionPolicy Outer = readSortKind(); SortSectionPolicy Inner = SortSectionPolicy::Default; std::vector V; if (Outer != SortSectionPolicy::Default) { expect("("); Inner = readSortKind(); if (Inner != SortSectionPolicy::Default) { expect("("); V = readInputSectionsList(); expect(")"); } else { V = readInputSectionsList(); } expect(")"); } else { V = readInputSectionsList(); } for (SectionPattern &Pat : V) { Pat.SortInner = Inner; Pat.SortOuter = Outer; } std::move(V.begin(), V.end(), std::back_inserter(Cmd->SectionPatterns)); } return Cmd; } InputSectionDescription * ScriptParser::readInputSectionDescription(StringRef Tok) { // Input section wildcard can be surrounded by KEEP. // https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep if (Tok == "KEEP") { expect("("); StringRef FilePattern = next(); InputSectionDescription *Cmd = readInputSectionRules(FilePattern); expect(")"); Script->KeptSections.push_back(Cmd); return Cmd; } return readInputSectionRules(Tok); } void ScriptParser::readSort() { expect("("); expect("CONSTRUCTORS"); expect(")"); } Expr ScriptParser::readAssert() { expect("("); Expr E = readExpr(); expect(","); StringRef Msg = unquote(next()); expect(")"); return [=] { if (!E().getValue()) error(Msg); return Script->getDot(); }; } // Reads a FILL(expr) command. We handle the FILL command as an // alias for =fillexp section attribute, which is different from // what GNU linkers do. // https://sourceware.org/binutils/docs/ld/Output-Section-Data.html std::array ScriptParser::readFill() { expect("("); std::array V = parseFill(next()); expect(")"); return V; } // Tries to read the special directive for an output section definition which // can be one of following: "(NOLOAD)", "(COPY)", "(INFO)" or "(OVERLAY)". // Tok1 and Tok2 are next 2 tokens peeked. See comment for readSectionAddressType below. bool ScriptParser::readSectionDirective(OutputSection *Cmd, StringRef Tok1, StringRef Tok2) { if (Tok1 != "(") return false; if (Tok2 != "NOLOAD" && Tok2 != "COPY" && Tok2 != "INFO" && Tok2 != "OVERLAY") return false; expect("("); if (consume("NOLOAD")) { Cmd->Noload = true; } else { skip(); // This is "COPY", "INFO" or "OVERLAY". Cmd->NonAlloc = true; } expect(")"); return true; } // Reads an expression and/or the special directive for an output // section definition. Directive is one of following: "(NOLOAD)", // "(COPY)", "(INFO)" or "(OVERLAY)". // // An output section name can be followed by an address expression // and/or directive. This grammar is not LL(1) because "(" can be // interpreted as either the beginning of some expression or beginning // of directive. // // https://sourceware.org/binutils/docs/ld/Output-Section-Address.html // https://sourceware.org/binutils/docs/ld/Output-Section-Type.html void ScriptParser::readSectionAddressType(OutputSection *Cmd) { if (readSectionDirective(Cmd, peek(), peek2())) return; Cmd->AddrExpr = readExpr(); if (peek() == "(" && !readSectionDirective(Cmd, "(", peek2())) setError("unknown section directive: " + peek2()); } static Expr checkAlignment(Expr E, std::string &Loc) { return [=] { uint64_t Alignment = std::max((uint64_t)1, E().getValue()); if (!isPowerOf2_64(Alignment)) { error(Loc + ": alignment must be power of 2"); return (uint64_t)1; // Return a dummy value. } return Alignment; }; } OutputSection *ScriptParser::readOverlaySectionDescription() { OutputSection *Cmd = Script->createOutputSection(next(), getCurrentLocation()); Cmd->InOverlay = true; expect("{"); while (!errorCount() && !consume("}")) Cmd->SectionCommands.push_back(readInputSectionRules(next())); Cmd->Phdrs = readOutputSectionPhdrs(); return Cmd; } OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) { OutputSection *Cmd = Script->createOutputSection(OutSec, getCurrentLocation()); size_t SymbolsReferenced = Script->ReferencedSymbols.size(); if (peek() != ":") readSectionAddressType(Cmd); expect(":"); std::string Location = getCurrentLocation(); if (consume("AT")) Cmd->LMAExpr = readParenExpr(); if (consume("ALIGN")) Cmd->AlignExpr = checkAlignment(readParenExpr(), Location); if (consume("SUBALIGN")) Cmd->SubalignExpr = checkAlignment(readParenExpr(), Location); // Parse constraints. if (consume("ONLY_IF_RO")) Cmd->Constraint = ConstraintKind::ReadOnly; if (consume("ONLY_IF_RW")) Cmd->Constraint = ConstraintKind::ReadWrite; expect("{"); while (!errorCount() && !consume("}")) { StringRef Tok = next(); if (Tok == ";") { // Empty commands are allowed. Do nothing here. } else if (SymbolAssignment *Assign = readAssignment(Tok)) { Cmd->SectionCommands.push_back(Assign); } else if (ByteCommand *Data = readByteCommand(Tok)) { Cmd->SectionCommands.push_back(Data); } else if (Tok == "CONSTRUCTORS") { // CONSTRUCTORS is a keyword to make the linker recognize C++ ctors/dtors // by name. This is for very old file formats such as ECOFF/XCOFF. // For ELF, we should ignore. } else if (Tok == "FILL") { Cmd->Filler = readFill(); } else if (Tok == "SORT") { readSort(); } else if (Tok == "INCLUDE") { readInclude(); } else if (peek() == "(") { Cmd->SectionCommands.push_back(readInputSectionDescription(Tok)); } else { // We have a file name and no input sections description. It is not a // commonly used syntax, but still acceptable. In that case, all sections // from the file will be included. auto *ISD = make(Tok); ISD->SectionPatterns.push_back({{}, StringMatcher({"*"})}); Cmd->SectionCommands.push_back(ISD); } } if (consume(">")) Cmd->MemoryRegionName = next(); if (consume("AT")) { expect(">"); Cmd->LMARegionName = next(); } if (Cmd->LMAExpr && !Cmd->LMARegionName.empty()) error("section can't have both LMA and a load region"); Cmd->Phdrs = readOutputSectionPhdrs(); if (consume("=")) Cmd->Filler = parseFill(next()); else if (peek().startswith("=")) Cmd->Filler = parseFill(next().drop_front()); // Consume optional comma following output section command. consume(","); if (Script->ReferencedSymbols.size() > SymbolsReferenced) Cmd->ExpressionsUseSymbols = true; return Cmd; } // Parses a given string as a octal/decimal/hexadecimal number and // returns it as a big-endian number. Used for `=`. // https://sourceware.org/binutils/docs/ld/Output-Section-Fill.html // // When reading a hexstring, ld.bfd handles it as a blob of arbitrary // size, while ld.gold always handles it as a 32-bit big-endian number. // We are compatible with ld.gold because it's easier to implement. std::array ScriptParser::parseFill(StringRef Tok) { uint32_t V = 0; if (!to_integer(Tok, V)) setError("invalid filler expression: " + Tok); std::array Buf; write32be(Buf.data(), V); return Buf; } SymbolAssignment *ScriptParser::readProvideHidden(bool Provide, bool Hidden) { expect("("); SymbolAssignment *Cmd = readSymbolAssignment(next()); Cmd->Provide = Provide; Cmd->Hidden = Hidden; expect(")"); return Cmd; } SymbolAssignment *ScriptParser::readAssignment(StringRef Tok) { // Assert expression returns Dot, so this is equal to ".=." if (Tok == "ASSERT") return make(".", readAssert(), getCurrentLocation()); size_t OldPos = Pos; SymbolAssignment *Cmd = nullptr; if (peek() == "=" || peek() == "+=") Cmd = readSymbolAssignment(Tok); else if (Tok == "PROVIDE") Cmd = readProvideHidden(true, false); else if (Tok == "HIDDEN") Cmd = readProvideHidden(false, true); else if (Tok == "PROVIDE_HIDDEN") Cmd = readProvideHidden(true, true); if (Cmd) { Cmd->CommandString = Tok.str() + " " + llvm::join(Tokens.begin() + OldPos, Tokens.begin() + Pos, " "); expect(";"); } return Cmd; } SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef Name) { StringRef Op = next(); assert(Op == "=" || Op == "+="); Expr E = readExpr(); if (Op == "+=") { std::string Loc = getCurrentLocation(); E = [=] { return add(Script->getSymbolValue(Name, Loc), E()); }; } return make(Name, E, getCurrentLocation()); } // This is an operator-precedence parser to parse a linker // script expression. Expr ScriptParser::readExpr() { // Our lexer is context-aware. Set the in-expression bit so that // they apply different tokenization rules. bool Orig = InExpr; InExpr = true; Expr E = readExpr1(readPrimary(), 0); InExpr = Orig; return E; } Expr ScriptParser::combine(StringRef Op, Expr L, Expr R) { if (Op == "+") return [=] { return add(L(), R()); }; if (Op == "-") return [=] { return sub(L(), R()); }; if (Op == "*") return [=] { return L().getValue() * R().getValue(); }; if (Op == "/") { std::string Loc = getCurrentLocation(); return [=]() -> uint64_t { if (uint64_t RV = R().getValue()) return L().getValue() / RV; error(Loc + ": division by zero"); return 0; }; } if (Op == "%") { std::string Loc = getCurrentLocation(); return [=]() -> uint64_t { if (uint64_t RV = R().getValue()) return L().getValue() % RV; error(Loc + ": modulo by zero"); return 0; }; } if (Op == "<<") return [=] { return L().getValue() << R().getValue(); }; if (Op == ">>") return [=] { return L().getValue() >> R().getValue(); }; if (Op == "<") return [=] { return L().getValue() < R().getValue(); }; if (Op == ">") return [=] { return L().getValue() > R().getValue(); }; if (Op == ">=") return [=] { return L().getValue() >= R().getValue(); }; if (Op == "<=") return [=] { return L().getValue() <= R().getValue(); }; if (Op == "==") return [=] { return L().getValue() == R().getValue(); }; if (Op == "!=") return [=] { return L().getValue() != R().getValue(); }; if (Op == "||") return [=] { return L().getValue() || R().getValue(); }; if (Op == "&&") return [=] { return L().getValue() && R().getValue(); }; if (Op == "&") return [=] { return bitAnd(L(), R()); }; if (Op == "|") return [=] { return bitOr(L(), R()); }; llvm_unreachable("invalid operator"); } // This is a part of the operator-precedence parser. This function // assumes that the remaining token stream starts with an operator. Expr ScriptParser::readExpr1(Expr Lhs, int MinPrec) { while (!atEOF() && !errorCount()) { // Read an operator and an expression. if (consume("?")) return readTernary(Lhs); StringRef Op1 = peek(); if (precedence(Op1) < MinPrec) break; skip(); Expr Rhs = readPrimary(); // Evaluate the remaining part of the expression first if the // next operator has greater precedence than the previous one. // For example, if we have read "+" and "3", and if the next // operator is "*", then we'll evaluate 3 * ... part first. while (!atEOF()) { StringRef Op2 = peek(); if (precedence(Op2) <= precedence(Op1)) break; Rhs = readExpr1(Rhs, precedence(Op2)); } Lhs = combine(Op1, Lhs, Rhs); } return Lhs; } Expr ScriptParser::getPageSize() { std::string Location = getCurrentLocation(); return [=]() -> uint64_t { if (Target) return Target->PageSize; error(Location + ": unable to calculate page size"); return 4096; // Return a dummy value. }; } Expr ScriptParser::readConstant() { StringRef S = readParenLiteral(); if (S == "COMMONPAGESIZE") return getPageSize(); if (S == "MAXPAGESIZE") return [] { return Config->MaxPageSize; }; setError("unknown constant: " + S); return [] { return 0; }; } // Parses Tok as an integer. It recognizes hexadecimal (prefixed with // "0x" or suffixed with "H") and decimal numbers. Decimal numbers may // have "K" (Ki) or "M" (Mi) suffixes. static Optional parseInt(StringRef Tok) { // Hexadecimal uint64_t Val; if (Tok.startswith_lower("0x")) { if (!to_integer(Tok.substr(2), Val, 16)) return None; return Val; } if (Tok.endswith_lower("H")) { if (!to_integer(Tok.drop_back(), Val, 16)) return None; return Val; } // Decimal if (Tok.endswith_lower("K")) { if (!to_integer(Tok.drop_back(), Val, 10)) return None; return Val * 1024; } if (Tok.endswith_lower("M")) { if (!to_integer(Tok.drop_back(), Val, 10)) return None; return Val * 1024 * 1024; } if (!to_integer(Tok, Val, 10)) return None; return Val; } ByteCommand *ScriptParser::readByteCommand(StringRef Tok) { int Size = StringSwitch(Tok) .Case("BYTE", 1) .Case("SHORT", 2) .Case("LONG", 4) .Case("QUAD", 8) .Default(-1); if (Size == -1) return nullptr; size_t OldPos = Pos; Expr E = readParenExpr(); std::string CommandString = Tok.str() + " " + llvm::join(Tokens.begin() + OldPos, Tokens.begin() + Pos, " "); return make(E, Size, CommandString); } StringRef ScriptParser::readParenLiteral() { expect("("); bool Orig = InExpr; InExpr = false; StringRef Tok = next(); InExpr = Orig; expect(")"); return Tok; } static void checkIfExists(OutputSection *Cmd, StringRef Location) { if (Cmd->Location.empty() && Script->ErrorOnMissingSection) error(Location + ": undefined section " + Cmd->Name); } Expr ScriptParser::readPrimary() { if (peek() == "(") return readParenExpr(); if (consume("~")) { Expr E = readPrimary(); return [=] { return ~E().getValue(); }; } if (consume("!")) { Expr E = readPrimary(); return [=] { return !E().getValue(); }; } if (consume("-")) { Expr E = readPrimary(); return [=] { return -E().getValue(); }; } StringRef Tok = next(); std::string Location = getCurrentLocation(); // Built-in functions are parsed here. // https://sourceware.org/binutils/docs/ld/Builtin-Functions.html. if (Tok == "ABSOLUTE") { Expr Inner = readParenExpr(); return [=] { ExprValue I = Inner(); I.ForceAbsolute = true; return I; }; } if (Tok == "ADDR") { StringRef Name = readParenLiteral(); OutputSection *Sec = Script->getOrCreateOutputSection(Name); return [=]() -> ExprValue { checkIfExists(Sec, Location); return {Sec, false, 0, Location}; }; } if (Tok == "ALIGN") { expect("("); Expr E = readExpr(); if (consume(")")) { E = checkAlignment(E, Location); return [=] { return alignTo(Script->getDot(), E().getValue()); }; } expect(","); Expr E2 = checkAlignment(readExpr(), Location); expect(")"); return [=] { ExprValue V = E(); V.Alignment = E2().getValue(); return V; }; } if (Tok == "ALIGNOF") { StringRef Name = readParenLiteral(); OutputSection *Cmd = Script->getOrCreateOutputSection(Name); return [=] { checkIfExists(Cmd, Location); return Cmd->Alignment; }; } if (Tok == "ASSERT") return readAssert(); if (Tok == "CONSTANT") return readConstant(); if (Tok == "DATA_SEGMENT_ALIGN") { expect("("); Expr E = readExpr(); expect(","); readExpr(); expect(")"); return [=] { return alignTo(Script->getDot(), std::max((uint64_t)1, E().getValue())); }; } if (Tok == "DATA_SEGMENT_END") { expect("("); expect("."); expect(")"); return [] { return Script->getDot(); }; } if (Tok == "DATA_SEGMENT_RELRO_END") { // GNU linkers implements more complicated logic to handle // DATA_SEGMENT_RELRO_END. We instead ignore the arguments and // just align to the next page boundary for simplicity. expect("("); readExpr(); expect(","); readExpr(); expect(")"); Expr E = getPageSize(); return [=] { return alignTo(Script->getDot(), E().getValue()); }; } if (Tok == "DEFINED") { StringRef Name = readParenLiteral(); return [=] { return Symtab->find(Name) ? 1 : 0; }; } if (Tok == "LENGTH") { StringRef Name = readParenLiteral(); if (Script->MemoryRegions.count(Name) == 0) { setError("memory region not defined: " + Name); return [] { return 0; }; } return [=] { return Script->MemoryRegions[Name]->Length; }; } if (Tok == "LOADADDR") { StringRef Name = readParenLiteral(); OutputSection *Cmd = Script->getOrCreateOutputSection(Name); return [=] { checkIfExists(Cmd, Location); return Cmd->getLMA(); }; } if (Tok == "MAX" || Tok == "MIN") { expect("("); Expr A = readExpr(); expect(","); Expr B = readExpr(); expect(")"); if (Tok == "MIN") return [=] { return std::min(A().getValue(), B().getValue()); }; return [=] { return std::max(A().getValue(), B().getValue()); }; } if (Tok == "ORIGIN") { StringRef Name = readParenLiteral(); if (Script->MemoryRegions.count(Name) == 0) { setError("memory region not defined: " + Name); return [] { return 0; }; } return [=] { return Script->MemoryRegions[Name]->Origin; }; } if (Tok == "SEGMENT_START") { expect("("); skip(); expect(","); Expr E = readExpr(); expect(")"); return [=] { return E(); }; } if (Tok == "SIZEOF") { StringRef Name = readParenLiteral(); OutputSection *Cmd = Script->getOrCreateOutputSection(Name); // Linker script does not create an output section if its content is empty. // We want to allow SIZEOF(.foo) where .foo is a section which happened to // be empty. return [=] { return Cmd->Size; }; } if (Tok == "SIZEOF_HEADERS") return [=] { return elf::getHeaderSize(); }; // Tok is the dot. if (Tok == ".") return [=] { return Script->getSymbolValue(Tok, Location); }; // Tok is a literal number. if (Optional Val = parseInt(Tok)) return [=] { return *Val; }; // Tok is a symbol name. if (!isValidCIdentifier(Tok)) setError("malformed number: " + Tok); Script->ReferencedSymbols.push_back(Tok); return [=] { return Script->getSymbolValue(Tok, Location); }; } Expr ScriptParser::readTernary(Expr Cond) { Expr L = readExpr(); expect(":"); Expr R = readExpr(); return [=] { return Cond().getValue() ? L() : R(); }; } Expr ScriptParser::readParenExpr() { expect("("); Expr E = readExpr(); expect(")"); return E; } std::vector ScriptParser::readOutputSectionPhdrs() { std::vector Phdrs; while (!errorCount() && peek().startswith(":")) { StringRef Tok = next(); Phdrs.push_back((Tok.size() == 1) ? next() : Tok.substr(1)); } return Phdrs; } // Read a program header type name. The next token must be a // name of a program header type or a constant (e.g. "0x3"). unsigned ScriptParser::readPhdrType() { StringRef Tok = next(); if (Optional Val = parseInt(Tok)) return *Val; unsigned Ret = StringSwitch(Tok) .Case("PT_NULL", PT_NULL) .Case("PT_LOAD", PT_LOAD) .Case("PT_DYNAMIC", PT_DYNAMIC) .Case("PT_INTERP", PT_INTERP) .Case("PT_NOTE", PT_NOTE) .Case("PT_SHLIB", PT_SHLIB) .Case("PT_PHDR", PT_PHDR) .Case("PT_TLS", PT_TLS) .Case("PT_GNU_EH_FRAME", PT_GNU_EH_FRAME) .Case("PT_GNU_STACK", PT_GNU_STACK) .Case("PT_GNU_RELRO", PT_GNU_RELRO) .Case("PT_OPENBSD_RANDOMIZE", PT_OPENBSD_RANDOMIZE) .Case("PT_OPENBSD_WXNEEDED", PT_OPENBSD_WXNEEDED) .Case("PT_OPENBSD_BOOTDATA", PT_OPENBSD_BOOTDATA) .Default(-1); if (Ret == (unsigned)-1) { setError("invalid program header type: " + Tok); return PT_NULL; } return Ret; } // Reads an anonymous version declaration. void ScriptParser::readAnonymousDeclaration() { std::vector Locals; std::vector Globals; std::tie(Locals, Globals) = readSymbols(); for (SymbolVersion V : Locals) { if (V.Name == "*") Config->DefaultSymbolVersion = VER_NDX_LOCAL; else Config->VersionScriptLocals.push_back(V); } for (SymbolVersion V : Globals) Config->VersionScriptGlobals.push_back(V); expect(";"); } // Reads a non-anonymous version definition, // e.g. "VerStr { global: foo; bar; local: *; };". void ScriptParser::readVersionDeclaration(StringRef VerStr) { // Read a symbol list. std::vector Locals; std::vector Globals; std::tie(Locals, Globals) = readSymbols(); for (SymbolVersion V : Locals) { if (V.Name == "*") Config->DefaultSymbolVersion = VER_NDX_LOCAL; else Config->VersionScriptLocals.push_back(V); } // Create a new version definition and add that to the global symbols. VersionDefinition Ver; Ver.Name = VerStr; Ver.Globals = Globals; // User-defined version number starts from 2 because 0 and 1 are // reserved for VER_NDX_LOCAL and VER_NDX_GLOBAL, respectively. Ver.Id = Config->VersionDefinitions.size() + 2; Config->VersionDefinitions.push_back(Ver); // Each version may have a parent version. For example, "Ver2" // defined as "Ver2 { global: foo; local: *; } Ver1;" has "Ver1" // as a parent. This version hierarchy is, probably against your // instinct, purely for hint; the runtime doesn't care about it // at all. In LLD, we simply ignore it. if (peek() != ";") skip(); expect(";"); } static bool hasWildcard(StringRef S) { return S.find_first_of("?*[") != StringRef::npos; } // Reads a list of symbols, e.g. "{ global: foo; bar; local: *; };". std::pair, std::vector> ScriptParser::readSymbols() { std::vector Locals; std::vector Globals; std::vector *V = &Globals; while (!errorCount()) { if (consume("}")) break; if (consumeLabel("local")) { V = &Locals; continue; } if (consumeLabel("global")) { V = &Globals; continue; } if (consume("extern")) { std::vector Ext = readVersionExtern(); V->insert(V->end(), Ext.begin(), Ext.end()); } else { StringRef Tok = next(); V->push_back({unquote(Tok), false, hasWildcard(Tok)}); } expect(";"); } return {Locals, Globals}; } // Reads an "extern C++" directive, e.g., // "extern "C++" { ns::*; "f(int, double)"; };" // // The last semicolon is optional. E.g. this is OK: // "extern "C++" { ns::*; "f(int, double)" };" std::vector ScriptParser::readVersionExtern() { StringRef Tok = next(); bool IsCXX = Tok == "\"C++\""; if (!IsCXX && Tok != "\"C\"") setError("Unknown language"); expect("{"); std::vector Ret; while (!errorCount() && peek() != "}") { StringRef Tok = next(); bool HasWildcard = !Tok.startswith("\"") && hasWildcard(Tok); Ret.push_back({unquote(Tok), IsCXX, HasWildcard}); if (consume("}")) return Ret; expect(";"); } expect("}"); return Ret; } uint64_t ScriptParser::readMemoryAssignment(StringRef S1, StringRef S2, StringRef S3) { if (!consume(S1) && !consume(S2) && !consume(S3)) { setError("expected one of: " + S1 + ", " + S2 + ", or " + S3); return 0; } expect("="); return readExpr()().getValue(); } // Parse the MEMORY command as specified in: // https://sourceware.org/binutils/docs/ld/MEMORY.html // // MEMORY { name [(attr)] : ORIGIN = origin, LENGTH = len ... } void ScriptParser::readMemory() { expect("{"); while (!errorCount() && !consume("}")) { StringRef Tok = next(); if (Tok == "INCLUDE") { readInclude(); continue; } uint32_t Flags = 0; uint32_t NegFlags = 0; if (consume("(")) { std::tie(Flags, NegFlags) = readMemoryAttributes(); expect(")"); } expect(":"); uint64_t Origin = readMemoryAssignment("ORIGIN", "org", "o"); expect(","); uint64_t Length = readMemoryAssignment("LENGTH", "len", "l"); // Add the memory region to the region map. MemoryRegion *MR = make(Tok, Origin, Length, Flags, NegFlags); if (!Script->MemoryRegions.insert({Tok, MR}).second) setError("region '" + Tok + "' already defined"); } } // This function parses the attributes used to match against section // flags when placing output sections in a memory region. These flags // are only used when an explicit memory region name is not used. std::pair ScriptParser::readMemoryAttributes() { uint32_t Flags = 0; uint32_t NegFlags = 0; bool Invert = false; for (char C : next().lower()) { uint32_t Flag = 0; if (C == '!') Invert = !Invert; else if (C == 'w') Flag = SHF_WRITE; else if (C == 'x') Flag = SHF_EXECINSTR; else if (C == 'a') Flag = SHF_ALLOC; else if (C != 'r') setError("invalid memory region attribute"); if (Invert) NegFlags |= Flag; else Flags |= Flag; } return {Flags, NegFlags}; } void elf::readLinkerScript(MemoryBufferRef MB) { ScriptParser(MB).readLinkerScript(); } void elf::readVersionScript(MemoryBufferRef MB) { ScriptParser(MB).readVersionScript(); } void elf::readDynamicList(MemoryBufferRef MB) { ScriptParser(MB).readDynamicList(); } void elf::readDefsym(StringRef Name, MemoryBufferRef MB) { ScriptParser(MB).readDefsym(Name); } Index: vendor/lld/dist-release_80/ELF/SyntheticSections.cpp =================================================================== --- vendor/lld/dist-release_80/ELF/SyntheticSections.cpp (revision 343801) +++ vendor/lld/dist-release_80/ELF/SyntheticSections.cpp (revision 343802) @@ -1,3224 +1,3226 @@ //===- SyntheticSections.cpp ----------------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file contains linker-synthesized sections. Currently, // synthetic sections are created either output sections or input sections, // but we are rewriting code so that all synthetic sections are created as // input sections. // //===----------------------------------------------------------------------===// #include "SyntheticSections.h" #include "Bits.h" #include "Config.h" #include "InputFiles.h" #include "LinkerScript.h" #include "OutputSections.h" #include "SymbolTable.h" #include "Symbols.h" #include "Target.h" #include "Writer.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "lld/Common/Threads.h" #include "lld/Common/Version.h" #include "llvm/ADT/SetOperations.h" #include "llvm/ADT/StringExtras.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Endian.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MD5.h" #include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Support/SHA1.h" #include "llvm/Support/xxhash.h" #include #include using namespace llvm; using namespace llvm::dwarf; using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::support; using namespace lld; using namespace lld::elf; using llvm::support::endian::read32le; using llvm::support::endian::write32le; using llvm::support::endian::write64le; constexpr size_t MergeNoTailSection::NumShards; // Returns an LLD version string. static ArrayRef getVersion() { // Check LLD_VERSION first for ease of testing. // You can get consistent output by using the environment variable. // This is only for testing. StringRef S = getenv("LLD_VERSION"); if (S.empty()) S = Saver.save(Twine("Linker: ") + getLLDVersion()); // +1 to include the terminating '\0'. return {(const uint8_t *)S.data(), S.size() + 1}; } // Creates a .comment section containing LLD version info. // With this feature, you can identify LLD-generated binaries easily // by "readelf --string-dump .comment ". // The returned object is a mergeable string section. MergeInputSection *elf::createCommentSection() { return make(SHF_MERGE | SHF_STRINGS, SHT_PROGBITS, 1, getVersion(), ".comment"); } // .MIPS.abiflags section. template MipsAbiFlagsSection::MipsAbiFlagsSection(Elf_Mips_ABIFlags Flags) : SyntheticSection(SHF_ALLOC, SHT_MIPS_ABIFLAGS, 8, ".MIPS.abiflags"), Flags(Flags) { this->Entsize = sizeof(Elf_Mips_ABIFlags); } template void MipsAbiFlagsSection::writeTo(uint8_t *Buf) { memcpy(Buf, &Flags, sizeof(Flags)); } template MipsAbiFlagsSection *MipsAbiFlagsSection::create() { Elf_Mips_ABIFlags Flags = {}; bool Create = false; for (InputSectionBase *Sec : InputSections) { if (Sec->Type != SHT_MIPS_ABIFLAGS) continue; Sec->Live = false; Create = true; std::string Filename = toString(Sec->File); const size_t Size = Sec->data().size(); // Older version of BFD (such as the default FreeBSD linker) concatenate // .MIPS.abiflags instead of merging. To allow for this case (or potential // zero padding) we ignore everything after the first Elf_Mips_ABIFlags if (Size < sizeof(Elf_Mips_ABIFlags)) { error(Filename + ": invalid size of .MIPS.abiflags section: got " + Twine(Size) + " instead of " + Twine(sizeof(Elf_Mips_ABIFlags))); return nullptr; } auto *S = reinterpret_cast(Sec->data().data()); if (S->version != 0) { error(Filename + ": unexpected .MIPS.abiflags version " + Twine(S->version)); return nullptr; } // LLD checks ISA compatibility in calcMipsEFlags(). Here we just // select the highest number of ISA/Rev/Ext. Flags.isa_level = std::max(Flags.isa_level, S->isa_level); Flags.isa_rev = std::max(Flags.isa_rev, S->isa_rev); Flags.isa_ext = std::max(Flags.isa_ext, S->isa_ext); Flags.gpr_size = std::max(Flags.gpr_size, S->gpr_size); Flags.cpr1_size = std::max(Flags.cpr1_size, S->cpr1_size); Flags.cpr2_size = std::max(Flags.cpr2_size, S->cpr2_size); Flags.ases |= S->ases; Flags.flags1 |= S->flags1; Flags.flags2 |= S->flags2; Flags.fp_abi = elf::getMipsFpAbiFlag(Flags.fp_abi, S->fp_abi, Filename); }; if (Create) return make>(Flags); return nullptr; } // .MIPS.options section. template MipsOptionsSection::MipsOptionsSection(Elf_Mips_RegInfo Reginfo) : SyntheticSection(SHF_ALLOC, SHT_MIPS_OPTIONS, 8, ".MIPS.options"), Reginfo(Reginfo) { this->Entsize = sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo); } template void MipsOptionsSection::writeTo(uint8_t *Buf) { auto *Options = reinterpret_cast(Buf); Options->kind = ODK_REGINFO; Options->size = getSize(); if (!Config->Relocatable) Reginfo.ri_gp_value = In.MipsGot->getGp(); memcpy(Buf + sizeof(Elf_Mips_Options), &Reginfo, sizeof(Reginfo)); } template MipsOptionsSection *MipsOptionsSection::create() { // N64 ABI only. if (!ELFT::Is64Bits) return nullptr; std::vector Sections; for (InputSectionBase *Sec : InputSections) if (Sec->Type == SHT_MIPS_OPTIONS) Sections.push_back(Sec); if (Sections.empty()) return nullptr; Elf_Mips_RegInfo Reginfo = {}; for (InputSectionBase *Sec : Sections) { Sec->Live = false; std::string Filename = toString(Sec->File); ArrayRef D = Sec->data(); while (!D.empty()) { if (D.size() < sizeof(Elf_Mips_Options)) { error(Filename + ": invalid size of .MIPS.options section"); break; } auto *Opt = reinterpret_cast(D.data()); if (Opt->kind == ODK_REGINFO) { Reginfo.ri_gprmask |= Opt->getRegInfo().ri_gprmask; Sec->getFile()->MipsGp0 = Opt->getRegInfo().ri_gp_value; break; } if (!Opt->size) fatal(Filename + ": zero option descriptor size"); D = D.slice(Opt->size); } }; return make>(Reginfo); } // MIPS .reginfo section. template MipsReginfoSection::MipsReginfoSection(Elf_Mips_RegInfo Reginfo) : SyntheticSection(SHF_ALLOC, SHT_MIPS_REGINFO, 4, ".reginfo"), Reginfo(Reginfo) { this->Entsize = sizeof(Elf_Mips_RegInfo); } template void MipsReginfoSection::writeTo(uint8_t *Buf) { if (!Config->Relocatable) Reginfo.ri_gp_value = In.MipsGot->getGp(); memcpy(Buf, &Reginfo, sizeof(Reginfo)); } template MipsReginfoSection *MipsReginfoSection::create() { // Section should be alive for O32 and N32 ABIs only. if (ELFT::Is64Bits) return nullptr; std::vector Sections; for (InputSectionBase *Sec : InputSections) if (Sec->Type == SHT_MIPS_REGINFO) Sections.push_back(Sec); if (Sections.empty()) return nullptr; Elf_Mips_RegInfo Reginfo = {}; for (InputSectionBase *Sec : Sections) { Sec->Live = false; if (Sec->data().size() != sizeof(Elf_Mips_RegInfo)) { error(toString(Sec->File) + ": invalid size of .reginfo section"); return nullptr; } auto *R = reinterpret_cast(Sec->data().data()); Reginfo.ri_gprmask |= R->ri_gprmask; Sec->getFile()->MipsGp0 = R->ri_gp_value; }; return make>(Reginfo); } InputSection *elf::createInterpSection() { // StringSaver guarantees that the returned string ends with '\0'. StringRef S = Saver.save(Config->DynamicLinker); ArrayRef Contents = {(const uint8_t *)S.data(), S.size() + 1}; auto *Sec = make(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, Contents, ".interp"); Sec->Live = true; return Sec; } Defined *elf::addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value, uint64_t Size, InputSectionBase &Section) { auto *S = make(Section.File, Name, STB_LOCAL, STV_DEFAULT, Type, Value, Size, &Section); if (In.SymTab) In.SymTab->addSymbol(S); return S; } static size_t getHashSize() { switch (Config->BuildId) { case BuildIdKind::Fast: return 8; case BuildIdKind::Md5: case BuildIdKind::Uuid: return 16; case BuildIdKind::Sha1: return 20; case BuildIdKind::Hexstring: return Config->BuildIdVector.size(); default: llvm_unreachable("unknown BuildIdKind"); } } BuildIdSection::BuildIdSection() : SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"), HashSize(getHashSize()) {} void BuildIdSection::writeTo(uint8_t *Buf) { write32(Buf, 4); // Name size write32(Buf + 4, HashSize); // Content size write32(Buf + 8, NT_GNU_BUILD_ID); // Type memcpy(Buf + 12, "GNU", 4); // Name string HashBuf = Buf + 16; } // Split one uint8 array into small pieces of uint8 arrays. static std::vector> split(ArrayRef Arr, size_t ChunkSize) { std::vector> Ret; while (Arr.size() > ChunkSize) { Ret.push_back(Arr.take_front(ChunkSize)); Arr = Arr.drop_front(ChunkSize); } if (!Arr.empty()) Ret.push_back(Arr); return Ret; } // Computes a hash value of Data using a given hash function. // In order to utilize multiple cores, we first split data into 1MB // chunks, compute a hash for each chunk, and then compute a hash value // of the hash values. void BuildIdSection::computeHash( llvm::ArrayRef Data, std::function Arr)> HashFn) { std::vector> Chunks = split(Data, 1024 * 1024); std::vector Hashes(Chunks.size() * HashSize); // Compute hash values. parallelForEachN(0, Chunks.size(), [&](size_t I) { HashFn(Hashes.data() + I * HashSize, Chunks[I]); }); // Write to the final output buffer. HashFn(HashBuf, Hashes); } BssSection::BssSection(StringRef Name, uint64_t Size, uint32_t Alignment) : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_NOBITS, Alignment, Name) { this->Bss = true; this->Size = Size; } void BuildIdSection::writeBuildId(ArrayRef Buf) { switch (Config->BuildId) { case BuildIdKind::Fast: computeHash(Buf, [](uint8_t *Dest, ArrayRef Arr) { write64le(Dest, xxHash64(Arr)); }); break; case BuildIdKind::Md5: computeHash(Buf, [](uint8_t *Dest, ArrayRef Arr) { memcpy(Dest, MD5::hash(Arr).data(), 16); }); break; case BuildIdKind::Sha1: computeHash(Buf, [](uint8_t *Dest, ArrayRef Arr) { memcpy(Dest, SHA1::hash(Arr).data(), 20); }); break; case BuildIdKind::Uuid: if (auto EC = getRandomBytes(HashBuf, HashSize)) error("entropy source failure: " + EC.message()); break; case BuildIdKind::Hexstring: memcpy(HashBuf, Config->BuildIdVector.data(), Config->BuildIdVector.size()); break; default: llvm_unreachable("unknown BuildIdKind"); } } EhFrameSection::EhFrameSection() : SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 1, ".eh_frame") {} // Search for an existing CIE record or create a new one. // CIE records from input object files are uniquified by their contents // and where their relocations point to. template CieRecord *EhFrameSection::addCie(EhSectionPiece &Cie, ArrayRef Rels) { Symbol *Personality = nullptr; unsigned FirstRelI = Cie.FirstRelocation; if (FirstRelI != (unsigned)-1) Personality = &Cie.Sec->template getFile()->getRelocTargetSym(Rels[FirstRelI]); // Search for an existing CIE by CIE contents/relocation target pair. CieRecord *&Rec = CieMap[{Cie.data(), Personality}]; // If not found, create a new one. if (!Rec) { Rec = make(); Rec->Cie = &Cie; CieRecords.push_back(Rec); } return Rec; } // There is one FDE per function. Returns true if a given FDE // points to a live function. template bool EhFrameSection::isFdeLive(EhSectionPiece &Fde, ArrayRef Rels) { auto *Sec = cast(Fde.Sec); unsigned FirstRelI = Fde.FirstRelocation; // An FDE should point to some function because FDEs are to describe // functions. That's however not always the case due to an issue of // ld.gold with -r. ld.gold may discard only functions and leave their // corresponding FDEs, which results in creating bad .eh_frame sections. // To deal with that, we ignore such FDEs. if (FirstRelI == (unsigned)-1) return false; const RelTy &Rel = Rels[FirstRelI]; Symbol &B = Sec->template getFile()->getRelocTargetSym(Rel); // FDEs for garbage-collected or merged-by-ICF sections are dead. if (auto *D = dyn_cast(&B)) if (SectionBase *Sec = D->Section) return Sec->Live; return false; } // .eh_frame is a sequence of CIE or FDE records. In general, there // is one CIE record per input object file which is followed by // a list of FDEs. This function searches an existing CIE or create a new // one and associates FDEs to the CIE. template void EhFrameSection::addSectionAux(EhInputSection *Sec, ArrayRef Rels) { OffsetToCie.clear(); for (EhSectionPiece &Piece : Sec->Pieces) { // The empty record is the end marker. if (Piece.Size == 4) return; size_t Offset = Piece.InputOff; uint32_t ID = read32(Piece.data().data() + 4); if (ID == 0) { OffsetToCie[Offset] = addCie(Piece, Rels); continue; } uint32_t CieOffset = Offset + 4 - ID; CieRecord *Rec = OffsetToCie[CieOffset]; if (!Rec) fatal(toString(Sec) + ": invalid CIE reference"); if (!isFdeLive(Piece, Rels)) continue; Rec->Fdes.push_back(&Piece); NumFdes++; } } template void EhFrameSection::addSection(InputSectionBase *C) { auto *Sec = cast(C); Sec->Parent = this; Alignment = std::max(Alignment, Sec->Alignment); Sections.push_back(Sec); for (auto *DS : Sec->DependentSections) DependentSections.push_back(DS); if (Sec->Pieces.empty()) return; if (Sec->AreRelocsRela) addSectionAux(Sec, Sec->template relas()); else addSectionAux(Sec, Sec->template rels()); } static void writeCieFde(uint8_t *Buf, ArrayRef D) { memcpy(Buf, D.data(), D.size()); size_t Aligned = alignTo(D.size(), Config->Wordsize); // Zero-clear trailing padding if it exists. memset(Buf + D.size(), 0, Aligned - D.size()); // Fix the size field. -4 since size does not include the size field itself. write32(Buf, Aligned - 4); } void EhFrameSection::finalizeContents() { assert(!this->Size); // Not finalized. size_t Off = 0; for (CieRecord *Rec : CieRecords) { Rec->Cie->OutputOff = Off; Off += alignTo(Rec->Cie->Size, Config->Wordsize); for (EhSectionPiece *Fde : Rec->Fdes) { Fde->OutputOff = Off; Off += alignTo(Fde->Size, Config->Wordsize); } } // The LSB standard does not allow a .eh_frame section with zero // Call Frame Information records. glibc unwind-dw2-fde.c // classify_object_over_fdes expects there is a CIE record length 0 as a // terminator. Thus we add one unconditionally. Off += 4; this->Size = Off; } // Returns data for .eh_frame_hdr. .eh_frame_hdr is a binary search table // to get an FDE from an address to which FDE is applied. This function // returns a list of such pairs. std::vector EhFrameSection::getFdeData() const { uint8_t *Buf = getParent()->Loc + OutSecOff; std::vector Ret; uint64_t VA = In.EhFrameHdr->getVA(); for (CieRecord *Rec : CieRecords) { uint8_t Enc = getFdeEncoding(Rec->Cie); for (EhSectionPiece *Fde : Rec->Fdes) { uint64_t Pc = getFdePc(Buf, Fde->OutputOff, Enc); uint64_t FdeVA = getParent()->Addr + Fde->OutputOff; if (!isInt<32>(Pc - VA)) fatal(toString(Fde->Sec) + ": PC offset is too large: 0x" + Twine::utohexstr(Pc - VA)); Ret.push_back({uint32_t(Pc - VA), uint32_t(FdeVA - VA)}); } } // Sort the FDE list by their PC and uniqueify. Usually there is only // one FDE for a PC (i.e. function), but if ICF merges two functions // into one, there can be more than one FDEs pointing to the address. auto Less = [](const FdeData &A, const FdeData &B) { return A.PcRel < B.PcRel; }; std::stable_sort(Ret.begin(), Ret.end(), Less); auto Eq = [](const FdeData &A, const FdeData &B) { return A.PcRel == B.PcRel; }; Ret.erase(std::unique(Ret.begin(), Ret.end(), Eq), Ret.end()); return Ret; } static uint64_t readFdeAddr(uint8_t *Buf, int Size) { switch (Size) { case DW_EH_PE_udata2: return read16(Buf); case DW_EH_PE_sdata2: return (int16_t)read16(Buf); case DW_EH_PE_udata4: return read32(Buf); case DW_EH_PE_sdata4: return (int32_t)read32(Buf); case DW_EH_PE_udata8: case DW_EH_PE_sdata8: return read64(Buf); case DW_EH_PE_absptr: return readUint(Buf); } fatal("unknown FDE size encoding"); } // Returns the VA to which a given FDE (on a mmap'ed buffer) is applied to. // We need it to create .eh_frame_hdr section. uint64_t EhFrameSection::getFdePc(uint8_t *Buf, size_t FdeOff, uint8_t Enc) const { // The starting address to which this FDE applies is // stored at FDE + 8 byte. size_t Off = FdeOff + 8; uint64_t Addr = readFdeAddr(Buf + Off, Enc & 0xf); if ((Enc & 0x70) == DW_EH_PE_absptr) return Addr; if ((Enc & 0x70) == DW_EH_PE_pcrel) return Addr + getParent()->Addr + Off; fatal("unknown FDE size relative encoding"); } void EhFrameSection::writeTo(uint8_t *Buf) { // Write CIE and FDE records. for (CieRecord *Rec : CieRecords) { size_t CieOffset = Rec->Cie->OutputOff; writeCieFde(Buf + CieOffset, Rec->Cie->data()); for (EhSectionPiece *Fde : Rec->Fdes) { size_t Off = Fde->OutputOff; writeCieFde(Buf + Off, Fde->data()); // FDE's second word should have the offset to an associated CIE. // Write it. write32(Buf + Off + 4, Off + 4 - CieOffset); } } // Apply relocations. .eh_frame section contents are not contiguous // in the output buffer, but relocateAlloc() still works because // getOffset() takes care of discontiguous section pieces. for (EhInputSection *S : Sections) S->relocateAlloc(Buf, nullptr); } GotSection::GotSection() : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, Target->GotEntrySize, ".got") { // PPC64 saves the ElfSym::GlobalOffsetTable .TOC. as the first entry in the // .got. If there are no references to .TOC. in the symbol table, // ElfSym::GlobalOffsetTable will not be defined and we won't need to save // .TOC. in the .got. When it is defined, we increase NumEntries by the number // of entries used to emit ElfSym::GlobalOffsetTable. if (ElfSym::GlobalOffsetTable && !Target->GotBaseSymInGotPlt) NumEntries += Target->GotHeaderEntriesNum; } void GotSection::addEntry(Symbol &Sym) { Sym.GotIndex = NumEntries; ++NumEntries; } bool GotSection::addDynTlsEntry(Symbol &Sym) { if (Sym.GlobalDynIndex != -1U) return false; Sym.GlobalDynIndex = NumEntries; // Global Dynamic TLS entries take two GOT slots. NumEntries += 2; return true; } // Reserves TLS entries for a TLS module ID and a TLS block offset. // In total it takes two GOT slots. bool GotSection::addTlsIndex() { if (TlsIndexOff != uint32_t(-1)) return false; TlsIndexOff = NumEntries * Config->Wordsize; NumEntries += 2; return true; } uint64_t GotSection::getGlobalDynAddr(const Symbol &B) const { return this->getVA() + B.GlobalDynIndex * Config->Wordsize; } uint64_t GotSection::getGlobalDynOffset(const Symbol &B) const { return B.GlobalDynIndex * Config->Wordsize; } void GotSection::finalizeContents() { Size = NumEntries * Config->Wordsize; } bool GotSection::empty() const { // We need to emit a GOT even if it's empty if there's a relocation that is // relative to GOT(such as GOTOFFREL) or there's a symbol that points to a GOT // (i.e. _GLOBAL_OFFSET_TABLE_) that the target defines relative to the .got. return NumEntries == 0 && !HasGotOffRel && !(ElfSym::GlobalOffsetTable && !Target->GotBaseSymInGotPlt); } void GotSection::writeTo(uint8_t *Buf) { // Buf points to the start of this section's buffer, // whereas InputSectionBase::relocateAlloc() expects its argument // to point to the start of the output section. Target->writeGotHeader(Buf); relocateAlloc(Buf - OutSecOff, Buf - OutSecOff + Size); } static uint64_t getMipsPageAddr(uint64_t Addr) { return (Addr + 0x8000) & ~0xffff; } static uint64_t getMipsPageCount(uint64_t Size) { return (Size + 0xfffe) / 0xffff + 1; } MipsGotSection::MipsGotSection() : SyntheticSection(SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL, SHT_PROGBITS, 16, ".got") {} void MipsGotSection::addEntry(InputFile &File, Symbol &Sym, int64_t Addend, RelExpr Expr) { FileGot &G = getGot(File); if (Expr == R_MIPS_GOT_LOCAL_PAGE) { if (const OutputSection *OS = Sym.getOutputSection()) G.PagesMap.insert({OS, {}}); else G.Local16.insert({{nullptr, getMipsPageAddr(Sym.getVA(Addend))}, 0}); } else if (Sym.isTls()) G.Tls.insert({&Sym, 0}); else if (Sym.IsPreemptible && Expr == R_ABS) G.Relocs.insert({&Sym, 0}); else if (Sym.IsPreemptible) G.Global.insert({&Sym, 0}); else if (Expr == R_MIPS_GOT_OFF32) G.Local32.insert({{&Sym, Addend}, 0}); else G.Local16.insert({{&Sym, Addend}, 0}); } void MipsGotSection::addDynTlsEntry(InputFile &File, Symbol &Sym) { getGot(File).DynTlsSymbols.insert({&Sym, 0}); } void MipsGotSection::addTlsIndex(InputFile &File) { getGot(File).DynTlsSymbols.insert({nullptr, 0}); } size_t MipsGotSection::FileGot::getEntriesNum() const { return getPageEntriesNum() + Local16.size() + Global.size() + Relocs.size() + Tls.size() + DynTlsSymbols.size() * 2; } size_t MipsGotSection::FileGot::getPageEntriesNum() const { size_t Num = 0; for (const std::pair &P : PagesMap) Num += P.second.Count; return Num; } size_t MipsGotSection::FileGot::getIndexedEntriesNum() const { size_t Count = getPageEntriesNum() + Local16.size() + Global.size(); // If there are relocation-only entries in the GOT, TLS entries // are allocated after them. TLS entries should be addressable // by 16-bit index so count both reloc-only and TLS entries. if (!Tls.empty() || !DynTlsSymbols.empty()) Count += Relocs.size() + Tls.size() + DynTlsSymbols.size() * 2; return Count; } MipsGotSection::FileGot &MipsGotSection::getGot(InputFile &F) { if (!F.MipsGotIndex.hasValue()) { Gots.emplace_back(); Gots.back().File = &F; F.MipsGotIndex = Gots.size() - 1; } return Gots[*F.MipsGotIndex]; } uint64_t MipsGotSection::getPageEntryOffset(const InputFile *F, const Symbol &Sym, int64_t Addend) const { const FileGot &G = Gots[*F->MipsGotIndex]; uint64_t Index = 0; if (const OutputSection *OutSec = Sym.getOutputSection()) { uint64_t SecAddr = getMipsPageAddr(OutSec->Addr); uint64_t SymAddr = getMipsPageAddr(Sym.getVA(Addend)); Index = G.PagesMap.lookup(OutSec).FirstIndex + (SymAddr - SecAddr) / 0xffff; } else { Index = G.Local16.lookup({nullptr, getMipsPageAddr(Sym.getVA(Addend))}); } return Index * Config->Wordsize; } uint64_t MipsGotSection::getSymEntryOffset(const InputFile *F, const Symbol &S, int64_t Addend) const { const FileGot &G = Gots[*F->MipsGotIndex]; Symbol *Sym = const_cast(&S); if (Sym->isTls()) return G.Tls.lookup(Sym) * Config->Wordsize; if (Sym->IsPreemptible) return G.Global.lookup(Sym) * Config->Wordsize; return G.Local16.lookup({Sym, Addend}) * Config->Wordsize; } uint64_t MipsGotSection::getTlsIndexOffset(const InputFile *F) const { const FileGot &G = Gots[*F->MipsGotIndex]; return G.DynTlsSymbols.lookup(nullptr) * Config->Wordsize; } uint64_t MipsGotSection::getGlobalDynOffset(const InputFile *F, const Symbol &S) const { const FileGot &G = Gots[*F->MipsGotIndex]; Symbol *Sym = const_cast(&S); return G.DynTlsSymbols.lookup(Sym) * Config->Wordsize; } const Symbol *MipsGotSection::getFirstGlobalEntry() const { if (Gots.empty()) return nullptr; const FileGot &PrimGot = Gots.front(); if (!PrimGot.Global.empty()) return PrimGot.Global.front().first; if (!PrimGot.Relocs.empty()) return PrimGot.Relocs.front().first; return nullptr; } unsigned MipsGotSection::getLocalEntriesNum() const { if (Gots.empty()) return HeaderEntriesNum; return HeaderEntriesNum + Gots.front().getPageEntriesNum() + Gots.front().Local16.size(); } bool MipsGotSection::tryMergeGots(FileGot &Dst, FileGot &Src, bool IsPrimary) { FileGot Tmp = Dst; set_union(Tmp.PagesMap, Src.PagesMap); set_union(Tmp.Local16, Src.Local16); set_union(Tmp.Global, Src.Global); set_union(Tmp.Relocs, Src.Relocs); set_union(Tmp.Tls, Src.Tls); set_union(Tmp.DynTlsSymbols, Src.DynTlsSymbols); size_t Count = IsPrimary ? HeaderEntriesNum : 0; Count += Tmp.getIndexedEntriesNum(); if (Count * Config->Wordsize > Config->MipsGotSize) return false; std::swap(Tmp, Dst); return true; } void MipsGotSection::finalizeContents() { updateAllocSize(); } bool MipsGotSection::updateAllocSize() { Size = HeaderEntriesNum * Config->Wordsize; for (const FileGot &G : Gots) Size += G.getEntriesNum() * Config->Wordsize; return false; } template void MipsGotSection::build() { if (Gots.empty()) return; std::vector MergedGots(1); // For each GOT move non-preemptible symbols from the `Global` // to `Local16` list. Preemptible symbol might become non-preemptible // one if, for example, it gets a related copy relocation. for (FileGot &Got : Gots) { for (auto &P: Got.Global) if (!P.first->IsPreemptible) Got.Local16.insert({{P.first, 0}, 0}); Got.Global.remove_if([&](const std::pair &P) { return !P.first->IsPreemptible; }); } // For each GOT remove "reloc-only" entry if there is "global" // entry for the same symbol. And add local entries which indexed // using 32-bit value at the end of 16-bit entries. for (FileGot &Got : Gots) { Got.Relocs.remove_if([&](const std::pair &P) { return Got.Global.count(P.first); }); set_union(Got.Local16, Got.Local32); Got.Local32.clear(); } // Evaluate number of "reloc-only" entries in the resulting GOT. // To do that put all unique "reloc-only" and "global" entries // from all GOTs to the future primary GOT. FileGot *PrimGot = &MergedGots.front(); for (FileGot &Got : Gots) { set_union(PrimGot->Relocs, Got.Global); set_union(PrimGot->Relocs, Got.Relocs); Got.Relocs.clear(); } // Evaluate number of "page" entries in each GOT. for (FileGot &Got : Gots) { for (std::pair &P : Got.PagesMap) { const OutputSection *OS = P.first; uint64_t SecSize = 0; for (BaseCommand *Cmd : OS->SectionCommands) { if (auto *ISD = dyn_cast(Cmd)) for (InputSection *IS : ISD->Sections) { uint64_t Off = alignTo(SecSize, IS->Alignment); SecSize = Off + IS->getSize(); } } P.second.Count = getMipsPageCount(SecSize); } } // Merge GOTs. Try to join as much as possible GOTs but do not exceed // maximum GOT size. At first, try to fill the primary GOT because // the primary GOT can be accessed in the most effective way. If it // is not possible, try to fill the last GOT in the list, and finally // create a new GOT if both attempts failed. for (FileGot &SrcGot : Gots) { InputFile *File = SrcGot.File; if (tryMergeGots(MergedGots.front(), SrcGot, true)) { File->MipsGotIndex = 0; } else { // If this is the first time we failed to merge with the primary GOT, // MergedGots.back() will also be the primary GOT. We must make sure not // to try to merge again with IsPrimary=false, as otherwise, if the // inputs are just right, we could allow the primary GOT to become 1 or 2 // words too big due to ignoring the header size. if (MergedGots.size() == 1 || !tryMergeGots(MergedGots.back(), SrcGot, false)) { MergedGots.emplace_back(); std::swap(MergedGots.back(), SrcGot); } File->MipsGotIndex = MergedGots.size() - 1; } } std::swap(Gots, MergedGots); // Reduce number of "reloc-only" entries in the primary GOT // by substracting "global" entries exist in the primary GOT. PrimGot = &Gots.front(); PrimGot->Relocs.remove_if([&](const std::pair &P) { return PrimGot->Global.count(P.first); }); // Calculate indexes for each GOT entry. size_t Index = HeaderEntriesNum; for (FileGot &Got : Gots) { Got.StartIndex = &Got == PrimGot ? 0 : Index; for (std::pair &P : Got.PagesMap) { // For each output section referenced by GOT page relocations calculate // and save into PagesMap an upper bound of MIPS GOT entries required // to store page addresses of local symbols. We assume the worst case - // each 64kb page of the output section has at least one GOT relocation // against it. And take in account the case when the section intersects // page boundaries. P.second.FirstIndex = Index; Index += P.second.Count; } for (auto &P: Got.Local16) P.second = Index++; for (auto &P: Got.Global) P.second = Index++; for (auto &P: Got.Relocs) P.second = Index++; for (auto &P: Got.Tls) P.second = Index++; for (auto &P: Got.DynTlsSymbols) { P.second = Index; Index += 2; } } // Update Symbol::GotIndex field to use this // value later in the `sortMipsSymbols` function. for (auto &P : PrimGot->Global) P.first->GotIndex = P.second; for (auto &P : PrimGot->Relocs) P.first->GotIndex = P.second; // Create dynamic relocations. for (FileGot &Got : Gots) { // Create dynamic relocations for TLS entries. for (std::pair &P : Got.Tls) { Symbol *S = P.first; uint64_t Offset = P.second * Config->Wordsize; if (S->IsPreemptible) In.RelaDyn->addReloc(Target->TlsGotRel, this, Offset, S); } for (std::pair &P : Got.DynTlsSymbols) { Symbol *S = P.first; uint64_t Offset = P.second * Config->Wordsize; if (S == nullptr) { if (!Config->Pic) continue; In.RelaDyn->addReloc(Target->TlsModuleIndexRel, this, Offset, S); } else { // When building a shared library we still need a dynamic relocation // for the module index. Therefore only checking for // S->IsPreemptible is not sufficient (this happens e.g. for // thread-locals that have been marked as local through a linker script) if (!S->IsPreemptible && !Config->Pic) continue; In.RelaDyn->addReloc(Target->TlsModuleIndexRel, this, Offset, S); // However, we can skip writing the TLS offset reloc for non-preemptible // symbols since it is known even in shared libraries if (!S->IsPreemptible) continue; Offset += Config->Wordsize; In.RelaDyn->addReloc(Target->TlsOffsetRel, this, Offset, S); } } // Do not create dynamic relocations for non-TLS // entries in the primary GOT. if (&Got == PrimGot) continue; // Dynamic relocations for "global" entries. for (const std::pair &P : Got.Global) { uint64_t Offset = P.second * Config->Wordsize; In.RelaDyn->addReloc(Target->RelativeRel, this, Offset, P.first); } if (!Config->Pic) continue; // Dynamic relocations for "local" entries in case of PIC. for (const std::pair &L : Got.PagesMap) { size_t PageCount = L.second.Count; for (size_t PI = 0; PI < PageCount; ++PI) { uint64_t Offset = (L.second.FirstIndex + PI) * Config->Wordsize; In.RelaDyn->addReloc({Target->RelativeRel, this, Offset, L.first, int64_t(PI * 0x10000)}); } } for (const std::pair &P : Got.Local16) { uint64_t Offset = P.second * Config->Wordsize; In.RelaDyn->addReloc({Target->RelativeRel, this, Offset, true, P.first.first, P.first.second}); } } } bool MipsGotSection::empty() const { // We add the .got section to the result for dynamic MIPS target because // its address and properties are mentioned in the .dynamic section. return Config->Relocatable; } uint64_t MipsGotSection::getGp(const InputFile *F) const { // For files without related GOT or files refer a primary GOT // returns "common" _gp value. For secondary GOTs calculate // individual _gp values. if (!F || !F->MipsGotIndex.hasValue() || *F->MipsGotIndex == 0) return ElfSym::MipsGp->getVA(0); return getVA() + Gots[*F->MipsGotIndex].StartIndex * Config->Wordsize + 0x7ff0; } void MipsGotSection::writeTo(uint8_t *Buf) { // Set the MSB of the second GOT slot. This is not required by any // MIPS ABI documentation, though. // // There is a comment in glibc saying that "The MSB of got[1] of a // gnu object is set to identify gnu objects," and in GNU gold it // says "the second entry will be used by some runtime loaders". // But how this field is being used is unclear. // // We are not really willing to mimic other linkers behaviors // without understanding why they do that, but because all files // generated by GNU tools have this special GOT value, and because // we've been doing this for years, it is probably a safe bet to // keep doing this for now. We really need to revisit this to see // if we had to do this. writeUint(Buf + Config->Wordsize, (uint64_t)1 << (Config->Wordsize * 8 - 1)); for (const FileGot &G : Gots) { auto Write = [&](size_t I, const Symbol *S, int64_t A) { uint64_t VA = A; if (S) { VA = S->getVA(A); if (S->StOther & STO_MIPS_MICROMIPS) VA |= 1; } writeUint(Buf + I * Config->Wordsize, VA); }; // Write 'page address' entries to the local part of the GOT. for (const std::pair &L : G.PagesMap) { size_t PageCount = L.second.Count; uint64_t FirstPageAddr = getMipsPageAddr(L.first->Addr); for (size_t PI = 0; PI < PageCount; ++PI) Write(L.second.FirstIndex + PI, nullptr, FirstPageAddr + PI * 0x10000); } // Local, global, TLS, reloc-only entries. // If TLS entry has a corresponding dynamic relocations, leave it // initialized by zero. Write down adjusted TLS symbol's values otherwise. // To calculate the adjustments use offsets for thread-local storage. // https://www.linux-mips.org/wiki/NPTL for (const std::pair &P : G.Local16) Write(P.second, P.first.first, P.first.second); // Write VA to the primary GOT only. For secondary GOTs that // will be done by REL32 dynamic relocations. if (&G == &Gots.front()) for (const std::pair &P : G.Global) Write(P.second, P.first, 0); for (const std::pair &P : G.Relocs) Write(P.second, P.first, 0); for (const std::pair &P : G.Tls) Write(P.second, P.first, P.first->IsPreemptible ? 0 : -0x7000); for (const std::pair &P : G.DynTlsSymbols) { if (P.first == nullptr && !Config->Pic) Write(P.second, nullptr, 1); else if (P.first && !P.first->IsPreemptible) { // If we are emitting PIC code with relocations we mustn't write // anything to the GOT here. When using Elf_Rel relocations the value // one will be treated as an addend and will cause crashes at runtime if (!Config->Pic) Write(P.second, nullptr, 1); Write(P.second + 1, P.first, -0x8000); } } } } // On PowerPC the .plt section is used to hold the table of function addresses // instead of the .got.plt, and the type is SHT_NOBITS similar to a .bss // section. I don't know why we have a BSS style type for the section but it is // consitent across both 64-bit PowerPC ABIs as well as the 32-bit PowerPC ABI. GotPltSection::GotPltSection() : SyntheticSection(SHF_ALLOC | SHF_WRITE, Config->EMachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS, Target->GotPltEntrySize, Config->EMachine == EM_PPC64 ? ".plt" : ".got.plt") {} void GotPltSection::addEntry(Symbol &Sym) { assert(Sym.PltIndex == Entries.size()); Entries.push_back(&Sym); } size_t GotPltSection::getSize() const { return (Target->GotPltHeaderEntriesNum + Entries.size()) * Target->GotPltEntrySize; } void GotPltSection::writeTo(uint8_t *Buf) { Target->writeGotPltHeader(Buf); Buf += Target->GotPltHeaderEntriesNum * Target->GotPltEntrySize; for (const Symbol *B : Entries) { Target->writeGotPlt(Buf, *B); Buf += Config->Wordsize; } } bool GotPltSection::empty() const { // We need to emit a GOT.PLT even if it's empty if there's a symbol that // references the _GLOBAL_OFFSET_TABLE_ and the Target defines the symbol // relative to the .got.plt section. return Entries.empty() && !(ElfSym::GlobalOffsetTable && Target->GotBaseSymInGotPlt); } static StringRef getIgotPltName() { // On ARM the IgotPltSection is part of the GotSection. if (Config->EMachine == EM_ARM) return ".got"; // On PowerPC64 the GotPltSection is renamed to '.plt' so the IgotPltSection // needs to be named the same. if (Config->EMachine == EM_PPC64) return ".plt"; return ".got.plt"; } // On PowerPC64 the GotPltSection type is SHT_NOBITS so we have to follow suit // with the IgotPltSection. IgotPltSection::IgotPltSection() : SyntheticSection(SHF_ALLOC | SHF_WRITE, Config->EMachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS, Target->GotPltEntrySize, getIgotPltName()) {} void IgotPltSection::addEntry(Symbol &Sym) { Sym.IsInIgot = true; assert(Sym.PltIndex == Entries.size()); Entries.push_back(&Sym); } size_t IgotPltSection::getSize() const { return Entries.size() * Target->GotPltEntrySize; } void IgotPltSection::writeTo(uint8_t *Buf) { for (const Symbol *B : Entries) { Target->writeIgotPlt(Buf, *B); Buf += Config->Wordsize; } } StringTableSection::StringTableSection(StringRef Name, bool Dynamic) : SyntheticSection(Dynamic ? (uint64_t)SHF_ALLOC : 0, SHT_STRTAB, 1, Name), Dynamic(Dynamic) { // ELF string tables start with a NUL byte. addString(""); } // Adds a string to the string table. If HashIt is true we hash and check for // duplicates. It is optional because the name of global symbols are already // uniqued and hashing them again has a big cost for a small value: uniquing // them with some other string that happens to be the same. unsigned StringTableSection::addString(StringRef S, bool HashIt) { if (HashIt) { auto R = StringMap.insert(std::make_pair(S, this->Size)); if (!R.second) return R.first->second; } unsigned Ret = this->Size; this->Size = this->Size + S.size() + 1; Strings.push_back(S); return Ret; } void StringTableSection::writeTo(uint8_t *Buf) { for (StringRef S : Strings) { memcpy(Buf, S.data(), S.size()); Buf[S.size()] = '\0'; Buf += S.size() + 1; } } // Returns the number of version definition entries. Because the first entry // is for the version definition itself, it is the number of versioned symbols // plus one. Note that we don't support multiple versions yet. static unsigned getVerDefNum() { return Config->VersionDefinitions.size() + 1; } template DynamicSection::DynamicSection() : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_DYNAMIC, Config->Wordsize, ".dynamic") { this->Entsize = ELFT::Is64Bits ? 16 : 8; // .dynamic section is not writable on MIPS and on Fuchsia OS // which passes -z rodynamic. // See "Special Section" in Chapter 4 in the following document: // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf if (Config->EMachine == EM_MIPS || Config->ZRodynamic) this->Flags = SHF_ALLOC; // Add strings to .dynstr early so that .dynstr's size will be // fixed early. for (StringRef S : Config->FilterList) addInt(DT_FILTER, In.DynStrTab->addString(S)); for (StringRef S : Config->AuxiliaryList) addInt(DT_AUXILIARY, In.DynStrTab->addString(S)); if (!Config->Rpath.empty()) addInt(Config->EnableNewDtags ? DT_RUNPATH : DT_RPATH, In.DynStrTab->addString(Config->Rpath)); for (InputFile *File : SharedFiles) { SharedFile *F = cast>(File); if (F->IsNeeded) addInt(DT_NEEDED, In.DynStrTab->addString(F->SoName)); } if (!Config->SoName.empty()) addInt(DT_SONAME, In.DynStrTab->addString(Config->SoName)); } template void DynamicSection::add(int32_t Tag, std::function Fn) { Entries.push_back({Tag, Fn}); } template void DynamicSection::addInt(int32_t Tag, uint64_t Val) { Entries.push_back({Tag, [=] { return Val; }}); } template void DynamicSection::addInSec(int32_t Tag, InputSection *Sec) { Entries.push_back({Tag, [=] { return Sec->getVA(0); }}); } template void DynamicSection::addInSecRelative(int32_t Tag, InputSection *Sec) { size_t TagOffset = Entries.size() * Entsize; Entries.push_back( {Tag, [=] { return Sec->getVA(0) - (getVA() + TagOffset); }}); } template void DynamicSection::addOutSec(int32_t Tag, OutputSection *Sec) { Entries.push_back({Tag, [=] { return Sec->Addr; }}); } template void DynamicSection::addSize(int32_t Tag, OutputSection *Sec) { Entries.push_back({Tag, [=] { return Sec->Size; }}); } template void DynamicSection::addSym(int32_t Tag, Symbol *Sym) { Entries.push_back({Tag, [=] { return Sym->getVA(); }}); } // A Linker script may assign the RELA relocation sections to the same // output section. When this occurs we cannot just use the OutputSection // Size. Moreover the [DT_JMPREL, DT_JMPREL + DT_PLTRELSZ) is permitted to // overlap with the [DT_RELA, DT_RELA + DT_RELASZ). static uint64_t addPltRelSz() { size_t Size = In.RelaPlt->getSize(); if (In.RelaIplt->getParent() == In.RelaPlt->getParent() && In.RelaIplt->Name == In.RelaPlt->Name) Size += In.RelaIplt->getSize(); return Size; } // Add remaining entries to complete .dynamic contents. template void DynamicSection::finalizeContents() { // Set DT_FLAGS and DT_FLAGS_1. uint32_t DtFlags = 0; uint32_t DtFlags1 = 0; if (Config->Bsymbolic) DtFlags |= DF_SYMBOLIC; if (Config->ZGlobal) DtFlags1 |= DF_1_GLOBAL; if (Config->ZInitfirst) DtFlags1 |= DF_1_INITFIRST; if (Config->ZInterpose) DtFlags1 |= DF_1_INTERPOSE; if (Config->ZNodefaultlib) DtFlags1 |= DF_1_NODEFLIB; if (Config->ZNodelete) DtFlags1 |= DF_1_NODELETE; if (Config->ZNodlopen) DtFlags1 |= DF_1_NOOPEN; if (Config->ZNow) { DtFlags |= DF_BIND_NOW; DtFlags1 |= DF_1_NOW; } if (Config->ZOrigin) { DtFlags |= DF_ORIGIN; DtFlags1 |= DF_1_ORIGIN; } if (!Config->ZText) DtFlags |= DF_TEXTREL; if (DtFlags) addInt(DT_FLAGS, DtFlags); if (DtFlags1) addInt(DT_FLAGS_1, DtFlags1); // DT_DEBUG is a pointer to debug informaion used by debuggers at runtime. We // need it for each process, so we don't write it for DSOs. The loader writes // the pointer into this entry. // // DT_DEBUG is the only .dynamic entry that needs to be written to. Some // systems (currently only Fuchsia OS) provide other means to give the // debugger this information. Such systems may choose make .dynamic read-only. // If the target is such a system (used -z rodynamic) don't write DT_DEBUG. if (!Config->Shared && !Config->Relocatable && !Config->ZRodynamic) addInt(DT_DEBUG, 0); if (OutputSection *Sec = In.DynStrTab->getParent()) this->Link = Sec->SectionIndex; if (!In.RelaDyn->empty()) { addInSec(In.RelaDyn->DynamicTag, In.RelaDyn); addSize(In.RelaDyn->SizeDynamicTag, In.RelaDyn->getParent()); bool IsRela = Config->IsRela; addInt(IsRela ? DT_RELAENT : DT_RELENT, IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel)); // MIPS dynamic loader does not support RELCOUNT tag. // The problem is in the tight relation between dynamic // relocations and GOT. So do not emit this tag on MIPS. if (Config->EMachine != EM_MIPS) { size_t NumRelativeRels = In.RelaDyn->getRelativeRelocCount(); if (Config->ZCombreloc && NumRelativeRels) addInt(IsRela ? DT_RELACOUNT : DT_RELCOUNT, NumRelativeRels); } } if (In.RelrDyn && !In.RelrDyn->Relocs.empty()) { addInSec(Config->UseAndroidRelrTags ? DT_ANDROID_RELR : DT_RELR, In.RelrDyn); addSize(Config->UseAndroidRelrTags ? DT_ANDROID_RELRSZ : DT_RELRSZ, In.RelrDyn->getParent()); addInt(Config->UseAndroidRelrTags ? DT_ANDROID_RELRENT : DT_RELRENT, sizeof(Elf_Relr)); } // .rel[a].plt section usually consists of two parts, containing plt and // iplt relocations. It is possible to have only iplt relocations in the // output. In that case RelaPlt is empty and have zero offset, the same offset // as RelaIplt have. And we still want to emit proper dynamic tags for that // case, so here we always use RelaPlt as marker for the begining of // .rel[a].plt section. if (In.RelaPlt->getParent()->Live) { addInSec(DT_JMPREL, In.RelaPlt); Entries.push_back({DT_PLTRELSZ, addPltRelSz}); switch (Config->EMachine) { case EM_MIPS: addInSec(DT_MIPS_PLTGOT, In.GotPlt); break; case EM_SPARCV9: addInSec(DT_PLTGOT, In.Plt); break; default: addInSec(DT_PLTGOT, In.GotPlt); break; } addInt(DT_PLTREL, Config->IsRela ? DT_RELA : DT_REL); } addInSec(DT_SYMTAB, In.DynSymTab); addInt(DT_SYMENT, sizeof(Elf_Sym)); addInSec(DT_STRTAB, In.DynStrTab); addInt(DT_STRSZ, In.DynStrTab->getSize()); if (!Config->ZText) addInt(DT_TEXTREL, 0); if (In.GnuHashTab) addInSec(DT_GNU_HASH, In.GnuHashTab); if (In.HashTab) addInSec(DT_HASH, In.HashTab); if (Out::PreinitArray) { addOutSec(DT_PREINIT_ARRAY, Out::PreinitArray); addSize(DT_PREINIT_ARRAYSZ, Out::PreinitArray); } if (Out::InitArray) { addOutSec(DT_INIT_ARRAY, Out::InitArray); addSize(DT_INIT_ARRAYSZ, Out::InitArray); } if (Out::FiniArray) { addOutSec(DT_FINI_ARRAY, Out::FiniArray); addSize(DT_FINI_ARRAYSZ, Out::FiniArray); } if (Symbol *B = Symtab->find(Config->Init)) if (B->isDefined()) addSym(DT_INIT, B); if (Symbol *B = Symtab->find(Config->Fini)) if (B->isDefined()) addSym(DT_FINI, B); bool HasVerNeed = InX::VerNeed->getNeedNum() != 0; if (HasVerNeed || In.VerDef) addInSec(DT_VERSYM, InX::VerSym); if (In.VerDef) { addInSec(DT_VERDEF, In.VerDef); addInt(DT_VERDEFNUM, getVerDefNum()); } if (HasVerNeed) { addInSec(DT_VERNEED, InX::VerNeed); addInt(DT_VERNEEDNUM, InX::VerNeed->getNeedNum()); } if (Config->EMachine == EM_MIPS) { addInt(DT_MIPS_RLD_VERSION, 1); addInt(DT_MIPS_FLAGS, RHF_NOTPOT); addInt(DT_MIPS_BASE_ADDRESS, Target->getImageBase()); addInt(DT_MIPS_SYMTABNO, In.DynSymTab->getNumSymbols()); add(DT_MIPS_LOCAL_GOTNO, [] { return In.MipsGot->getLocalEntriesNum(); }); if (const Symbol *B = In.MipsGot->getFirstGlobalEntry()) addInt(DT_MIPS_GOTSYM, B->DynsymIndex); else addInt(DT_MIPS_GOTSYM, In.DynSymTab->getNumSymbols()); addInSec(DT_PLTGOT, In.MipsGot); if (In.MipsRldMap) { if (!Config->Pie) addInSec(DT_MIPS_RLD_MAP, In.MipsRldMap); // Store the offset to the .rld_map section // relative to the address of the tag. addInSecRelative(DT_MIPS_RLD_MAP_REL, In.MipsRldMap); } } // Glink dynamic tag is required by the V2 abi if the plt section isn't empty. if (Config->EMachine == EM_PPC64 && !In.Plt->empty()) { // The Glink tag points to 32 bytes before the first lazy symbol resolution // stub, which starts directly after the header. Entries.push_back({DT_PPC64_GLINK, [=] { unsigned Offset = Target->PltHeaderSize - 32; return In.Plt->getVA(0) + Offset; }}); } addInt(DT_NULL, 0); getParent()->Link = this->Link; this->Size = Entries.size() * this->Entsize; } template void DynamicSection::writeTo(uint8_t *Buf) { auto *P = reinterpret_cast(Buf); for (std::pair> &KV : Entries) { P->d_tag = KV.first; P->d_un.d_val = KV.second(); ++P; } } uint64_t DynamicReloc::getOffset() const { return InputSec->getVA(OffsetInSec); } int64_t DynamicReloc::computeAddend() const { if (UseSymVA) return Sym->getVA(Addend); if (!OutputSec) return Addend; // See the comment in the DynamicReloc ctor. return getMipsPageAddr(OutputSec->Addr) + Addend; } uint32_t DynamicReloc::getSymIndex() const { if (Sym && !UseSymVA) return Sym->DynsymIndex; return 0; } RelocationBaseSection::RelocationBaseSection(StringRef Name, uint32_t Type, int32_t DynamicTag, int32_t SizeDynamicTag) : SyntheticSection(SHF_ALLOC, Type, Config->Wordsize, Name), DynamicTag(DynamicTag), SizeDynamicTag(SizeDynamicTag) {} void RelocationBaseSection::addReloc(RelType DynType, InputSectionBase *IS, uint64_t OffsetInSec, Symbol *Sym) { addReloc({DynType, IS, OffsetInSec, false, Sym, 0}); } void RelocationBaseSection::addReloc(RelType DynType, InputSectionBase *InputSec, uint64_t OffsetInSec, Symbol *Sym, int64_t Addend, RelExpr Expr, RelType Type) { // Write the addends to the relocated address if required. We skip // it if the written value would be zero. if (Config->WriteAddends && (Expr != R_ADDEND || Addend != 0)) InputSec->Relocations.push_back({Expr, Type, OffsetInSec, Addend, Sym}); addReloc({DynType, InputSec, OffsetInSec, Expr != R_ADDEND, Sym, Addend}); } void RelocationBaseSection::addReloc(const DynamicReloc &Reloc) { if (Reloc.Type == Target->RelativeRel) ++NumRelativeRelocs; Relocs.push_back(Reloc); } void RelocationBaseSection::finalizeContents() { // When linking glibc statically, .rel{,a}.plt contains R_*_IRELATIVE // relocations due to IFUNC (e.g. strcpy). sh_link will be set to 0 in that // case. InputSection *SymTab = Config->Relocatable ? In.SymTab : In.DynSymTab; if (SymTab && SymTab->getParent()) getParent()->Link = SymTab->getParent()->SectionIndex; else getParent()->Link = 0; - if (In.RelaIplt == this || In.RelaPlt == this) + if (In.RelaPlt == this) getParent()->Info = In.GotPlt->getParent()->SectionIndex; + if (In.RelaIplt == this) + getParent()->Info = In.IgotPlt->getParent()->SectionIndex; } RelrBaseSection::RelrBaseSection() : SyntheticSection(SHF_ALLOC, Config->UseAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR, Config->Wordsize, ".relr.dyn") {} template static void encodeDynamicReloc(typename ELFT::Rela *P, const DynamicReloc &Rel) { if (Config->IsRela) P->r_addend = Rel.computeAddend(); P->r_offset = Rel.getOffset(); P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->IsMips64EL); } template RelocationSection::RelocationSection(StringRef Name, bool Sort) : RelocationBaseSection(Name, Config->IsRela ? SHT_RELA : SHT_REL, Config->IsRela ? DT_RELA : DT_REL, Config->IsRela ? DT_RELASZ : DT_RELSZ), Sort(Sort) { this->Entsize = Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); } static bool compRelocations(const DynamicReloc &A, const DynamicReloc &B) { bool AIsRel = A.Type == Target->RelativeRel; bool BIsRel = B.Type == Target->RelativeRel; if (AIsRel != BIsRel) return AIsRel; return A.getSymIndex() < B.getSymIndex(); } template void RelocationSection::writeTo(uint8_t *Buf) { if (Sort) std::stable_sort(Relocs.begin(), Relocs.end(), compRelocations); for (const DynamicReloc &Rel : Relocs) { encodeDynamicReloc(reinterpret_cast(Buf), Rel); Buf += Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); } } template unsigned RelocationSection::getRelocOffset() { return this->Entsize * Relocs.size(); } template AndroidPackedRelocationSection::AndroidPackedRelocationSection( StringRef Name) : RelocationBaseSection( Name, Config->IsRela ? SHT_ANDROID_RELA : SHT_ANDROID_REL, Config->IsRela ? DT_ANDROID_RELA : DT_ANDROID_REL, Config->IsRela ? DT_ANDROID_RELASZ : DT_ANDROID_RELSZ) { this->Entsize = 1; } template bool AndroidPackedRelocationSection::updateAllocSize() { // This function computes the contents of an Android-format packed relocation // section. // // This format compresses relocations by using relocation groups to factor out // fields that are common between relocations and storing deltas from previous // relocations in SLEB128 format (which has a short representation for small // numbers). A good example of a relocation type with common fields is // R_*_RELATIVE, which is normally used to represent function pointers in // vtables. In the REL format, each relative relocation has the same r_info // field, and is only different from other relative relocations in terms of // the r_offset field. By sorting relocations by offset, grouping them by // r_info and representing each relocation with only the delta from the // previous offset, each 8-byte relocation can be compressed to as little as 1 // byte (or less with run-length encoding). This relocation packer was able to // reduce the size of the relocation section in an Android Chromium DSO from // 2,911,184 bytes to 174,693 bytes, or 6% of the original size. // // A relocation section consists of a header containing the literal bytes // 'APS2' followed by a sequence of SLEB128-encoded integers. The first two // elements are the total number of relocations in the section and an initial // r_offset value. The remaining elements define a sequence of relocation // groups. Each relocation group starts with a header consisting of the // following elements: // // - the number of relocations in the relocation group // - flags for the relocation group // - (if RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG is set) the r_offset delta // for each relocation in the group. // - (if RELOCATION_GROUPED_BY_INFO_FLAG is set) the value of the r_info // field for each relocation in the group. // - (if RELOCATION_GROUP_HAS_ADDEND_FLAG and // RELOCATION_GROUPED_BY_ADDEND_FLAG are set) the r_addend delta for // each relocation in the group. // // Following the relocation group header are descriptions of each of the // relocations in the group. They consist of the following elements: // // - (if RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG is not set) the r_offset // delta for this relocation. // - (if RELOCATION_GROUPED_BY_INFO_FLAG is not set) the value of the r_info // field for this relocation. // - (if RELOCATION_GROUP_HAS_ADDEND_FLAG is set and // RELOCATION_GROUPED_BY_ADDEND_FLAG is not set) the r_addend delta for // this relocation. size_t OldSize = RelocData.size(); RelocData = {'A', 'P', 'S', '2'}; raw_svector_ostream OS(RelocData); auto Add = [&](int64_t V) { encodeSLEB128(V, OS); }; // The format header includes the number of relocations and the initial // offset (we set this to zero because the first relocation group will // perform the initial adjustment). Add(Relocs.size()); Add(0); std::vector Relatives, NonRelatives; for (const DynamicReloc &Rel : Relocs) { Elf_Rela R; encodeDynamicReloc(&R, Rel); if (R.getType(Config->IsMips64EL) == Target->RelativeRel) Relatives.push_back(R); else NonRelatives.push_back(R); } llvm::sort(Relatives, [](const Elf_Rel &A, const Elf_Rel &B) { return A.r_offset < B.r_offset; }); // Try to find groups of relative relocations which are spaced one word // apart from one another. These generally correspond to vtable entries. The // format allows these groups to be encoded using a sort of run-length // encoding, but each group will cost 7 bytes in addition to the offset from // the previous group, so it is only profitable to do this for groups of // size 8 or larger. std::vector UngroupedRelatives; std::vector> RelativeGroups; for (auto I = Relatives.begin(), E = Relatives.end(); I != E;) { std::vector Group; do { Group.push_back(*I++); } while (I != E && (I - 1)->r_offset + Config->Wordsize == I->r_offset); if (Group.size() < 8) UngroupedRelatives.insert(UngroupedRelatives.end(), Group.begin(), Group.end()); else RelativeGroups.emplace_back(std::move(Group)); } unsigned HasAddendIfRela = Config->IsRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0; uint64_t Offset = 0; uint64_t Addend = 0; // Emit the run-length encoding for the groups of adjacent relative // relocations. Each group is represented using two groups in the packed // format. The first is used to set the current offset to the start of the // group (and also encodes the first relocation), and the second encodes the // remaining relocations. for (std::vector &G : RelativeGroups) { // The first relocation in the group. Add(1); Add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG | RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela); Add(G[0].r_offset - Offset); Add(Target->RelativeRel); if (Config->IsRela) { Add(G[0].r_addend - Addend); Addend = G[0].r_addend; } // The remaining relocations. Add(G.size() - 1); Add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG | RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela); Add(Config->Wordsize); Add(Target->RelativeRel); if (Config->IsRela) { for (auto I = G.begin() + 1, E = G.end(); I != E; ++I) { Add(I->r_addend - Addend); Addend = I->r_addend; } } Offset = G.back().r_offset; } // Now the ungrouped relatives. if (!UngroupedRelatives.empty()) { Add(UngroupedRelatives.size()); Add(RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela); Add(Target->RelativeRel); for (Elf_Rela &R : UngroupedRelatives) { Add(R.r_offset - Offset); Offset = R.r_offset; if (Config->IsRela) { Add(R.r_addend - Addend); Addend = R.r_addend; } } } // Finally the non-relative relocations. llvm::sort(NonRelatives, [](const Elf_Rela &A, const Elf_Rela &B) { return A.r_offset < B.r_offset; }); if (!NonRelatives.empty()) { Add(NonRelatives.size()); Add(HasAddendIfRela); for (Elf_Rela &R : NonRelatives) { Add(R.r_offset - Offset); Offset = R.r_offset; Add(R.r_info); if (Config->IsRela) { Add(R.r_addend - Addend); Addend = R.r_addend; } } } // Don't allow the section to shrink; otherwise the size of the section can // oscillate infinitely. if (RelocData.size() < OldSize) RelocData.append(OldSize - RelocData.size(), 0); // Returns whether the section size changed. We need to keep recomputing both // section layout and the contents of this section until the size converges // because changing this section's size can affect section layout, which in // turn can affect the sizes of the LEB-encoded integers stored in this // section. return RelocData.size() != OldSize; } template RelrSection::RelrSection() { this->Entsize = Config->Wordsize; } template bool RelrSection::updateAllocSize() { // This function computes the contents of an SHT_RELR packed relocation // section. // // Proposal for adding SHT_RELR sections to generic-abi is here: // https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg // // The encoded sequence of Elf64_Relr entries in a SHT_RELR section looks // like [ AAAAAAAA BBBBBBB1 BBBBBBB1 ... AAAAAAAA BBBBBB1 ... ] // // i.e. start with an address, followed by any number of bitmaps. The address // entry encodes 1 relocation. The subsequent bitmap entries encode up to 63 // relocations each, at subsequent offsets following the last address entry. // // The bitmap entries must have 1 in the least significant bit. The assumption // here is that an address cannot have 1 in lsb. Odd addresses are not // supported. // // Excluding the least significant bit in the bitmap, each non-zero bit in // the bitmap represents a relocation to be applied to a corresponding machine // word that follows the base address word. The second least significant bit // represents the machine word immediately following the initial address, and // each bit that follows represents the next word, in linear order. As such, // a single bitmap can encode up to 31 relocations in a 32-bit object, and // 63 relocations in a 64-bit object. // // This encoding has a couple of interesting properties: // 1. Looking at any entry, it is clear whether it's an address or a bitmap: // even means address, odd means bitmap. // 2. Just a simple list of addresses is a valid encoding. size_t OldSize = RelrRelocs.size(); RelrRelocs.clear(); // Same as Config->Wordsize but faster because this is a compile-time // constant. const size_t Wordsize = sizeof(typename ELFT::uint); // Number of bits to use for the relocation offsets bitmap. // Must be either 63 or 31. const size_t NBits = Wordsize * 8 - 1; // Get offsets for all relative relocations and sort them. std::vector Offsets; for (const RelativeReloc &Rel : Relocs) Offsets.push_back(Rel.getOffset()); llvm::sort(Offsets.begin(), Offsets.end()); // For each leading relocation, find following ones that can be folded // as a bitmap and fold them. for (size_t I = 0, E = Offsets.size(); I < E;) { // Add a leading relocation. RelrRelocs.push_back(Elf_Relr(Offsets[I])); uint64_t Base = Offsets[I] + Wordsize; ++I; // Find foldable relocations to construct bitmaps. while (I < E) { uint64_t Bitmap = 0; while (I < E) { uint64_t Delta = Offsets[I] - Base; // If it is too far, it cannot be folded. if (Delta >= NBits * Wordsize) break; // If it is not a multiple of wordsize away, it cannot be folded. if (Delta % Wordsize) break; // Fold it. Bitmap |= 1ULL << (Delta / Wordsize); ++I; } if (!Bitmap) break; RelrRelocs.push_back(Elf_Relr((Bitmap << 1) | 1)); Base += NBits * Wordsize; } } return RelrRelocs.size() != OldSize; } SymbolTableBaseSection::SymbolTableBaseSection(StringTableSection &StrTabSec) : SyntheticSection(StrTabSec.isDynamic() ? (uint64_t)SHF_ALLOC : 0, StrTabSec.isDynamic() ? SHT_DYNSYM : SHT_SYMTAB, Config->Wordsize, StrTabSec.isDynamic() ? ".dynsym" : ".symtab"), StrTabSec(StrTabSec) {} // Orders symbols according to their positions in the GOT, // in compliance with MIPS ABI rules. // See "Global Offset Table" in Chapter 5 in the following document // for detailed description: // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf static bool sortMipsSymbols(const SymbolTableEntry &L, const SymbolTableEntry &R) { // Sort entries related to non-local preemptible symbols by GOT indexes. // All other entries go to the beginning of a dynsym in arbitrary order. if (L.Sym->isInGot() && R.Sym->isInGot()) return L.Sym->GotIndex < R.Sym->GotIndex; if (!L.Sym->isInGot() && !R.Sym->isInGot()) return false; return !L.Sym->isInGot(); } void SymbolTableBaseSection::finalizeContents() { if (OutputSection *Sec = StrTabSec.getParent()) getParent()->Link = Sec->SectionIndex; if (this->Type != SHT_DYNSYM) { sortSymTabSymbols(); return; } // If it is a .dynsym, there should be no local symbols, but we need // to do a few things for the dynamic linker. // Section's Info field has the index of the first non-local symbol. // Because the first symbol entry is a null entry, 1 is the first. getParent()->Info = 1; if (In.GnuHashTab) { // NB: It also sorts Symbols to meet the GNU hash table requirements. In.GnuHashTab->addSymbols(Symbols); } else if (Config->EMachine == EM_MIPS) { std::stable_sort(Symbols.begin(), Symbols.end(), sortMipsSymbols); } size_t I = 0; for (const SymbolTableEntry &S : Symbols) S.Sym->DynsymIndex = ++I; } // The ELF spec requires that all local symbols precede global symbols, so we // sort symbol entries in this function. (For .dynsym, we don't do that because // symbols for dynamic linking are inherently all globals.) // // Aside from above, we put local symbols in groups starting with the STT_FILE // symbol. That is convenient for purpose of identifying where are local symbols // coming from. void SymbolTableBaseSection::sortSymTabSymbols() { // Move all local symbols before global symbols. auto E = std::stable_partition( Symbols.begin(), Symbols.end(), [](const SymbolTableEntry &S) { return S.Sym->isLocal() || S.Sym->computeBinding() == STB_LOCAL; }); size_t NumLocals = E - Symbols.begin(); getParent()->Info = NumLocals + 1; // We want to group the local symbols by file. For that we rebuild the local // part of the symbols vector. We do not need to care about the STT_FILE // symbols, they are already naturally placed first in each group. That // happens because STT_FILE is always the first symbol in the object and hence // precede all other local symbols we add for a file. MapVector> Arr; for (const SymbolTableEntry &S : llvm::make_range(Symbols.begin(), E)) Arr[S.Sym->File].push_back(S); auto I = Symbols.begin(); for (std::pair> &P : Arr) for (SymbolTableEntry &Entry : P.second) *I++ = Entry; } void SymbolTableBaseSection::addSymbol(Symbol *B) { // Adding a local symbol to a .dynsym is a bug. assert(this->Type != SHT_DYNSYM || !B->isLocal()); bool HashIt = B->isLocal(); Symbols.push_back({B, StrTabSec.addString(B->getName(), HashIt)}); } size_t SymbolTableBaseSection::getSymbolIndex(Symbol *Sym) { // Initializes symbol lookup tables lazily. This is used only // for -r or -emit-relocs. llvm::call_once(OnceFlag, [&] { SymbolIndexMap.reserve(Symbols.size()); size_t I = 0; for (const SymbolTableEntry &E : Symbols) { if (E.Sym->Type == STT_SECTION) SectionIndexMap[E.Sym->getOutputSection()] = ++I; else SymbolIndexMap[E.Sym] = ++I; } }); // Section symbols are mapped based on their output sections // to maintain their semantics. if (Sym->Type == STT_SECTION) return SectionIndexMap.lookup(Sym->getOutputSection()); return SymbolIndexMap.lookup(Sym); } template SymbolTableSection::SymbolTableSection(StringTableSection &StrTabSec) : SymbolTableBaseSection(StrTabSec) { this->Entsize = sizeof(Elf_Sym); } static BssSection *getCommonSec(Symbol *Sym) { if (!Config->DefineCommon) if (auto *D = dyn_cast(Sym)) return dyn_cast_or_null(D->Section); return nullptr; } static uint32_t getSymSectionIndex(Symbol *Sym) { if (getCommonSec(Sym)) return SHN_COMMON; if (!isa(Sym) || Sym->NeedsPltAddr) return SHN_UNDEF; if (const OutputSection *OS = Sym->getOutputSection()) return OS->SectionIndex >= SHN_LORESERVE ? (uint32_t)SHN_XINDEX : OS->SectionIndex; return SHN_ABS; } // Write the internal symbol table contents to the output symbol table. template void SymbolTableSection::writeTo(uint8_t *Buf) { // The first entry is a null entry as per the ELF spec. memset(Buf, 0, sizeof(Elf_Sym)); Buf += sizeof(Elf_Sym); auto *ESym = reinterpret_cast(Buf); for (SymbolTableEntry &Ent : Symbols) { Symbol *Sym = Ent.Sym; // Set st_info and st_other. ESym->st_other = 0; if (Sym->isLocal()) { ESym->setBindingAndType(STB_LOCAL, Sym->Type); } else { ESym->setBindingAndType(Sym->computeBinding(), Sym->Type); ESym->setVisibility(Sym->Visibility); } ESym->st_name = Ent.StrTabOffset; ESym->st_shndx = getSymSectionIndex(Ent.Sym); // Copy symbol size if it is a defined symbol. st_size is not significant // for undefined symbols, so whether copying it or not is up to us if that's // the case. We'll leave it as zero because by not setting a value, we can // get the exact same outputs for two sets of input files that differ only // in undefined symbol size in DSOs. if (ESym->st_shndx == SHN_UNDEF) ESym->st_size = 0; else ESym->st_size = Sym->getSize(); // st_value is usually an address of a symbol, but that has a // special meaining for uninstantiated common symbols (this can // occur if -r is given). if (BssSection *CommonSec = getCommonSec(Ent.Sym)) ESym->st_value = CommonSec->Alignment; else ESym->st_value = Sym->getVA(); ++ESym; } // On MIPS we need to mark symbol which has a PLT entry and requires // pointer equality by STO_MIPS_PLT flag. That is necessary to help // dynamic linker distinguish such symbols and MIPS lazy-binding stubs. // https://sourceware.org/ml/binutils/2008-07/txt00000.txt if (Config->EMachine == EM_MIPS) { auto *ESym = reinterpret_cast(Buf); for (SymbolTableEntry &Ent : Symbols) { Symbol *Sym = Ent.Sym; if (Sym->isInPlt() && Sym->NeedsPltAddr) ESym->st_other |= STO_MIPS_PLT; if (isMicroMips()) { // Set STO_MIPS_MICROMIPS flag and less-significant bit for // a defined microMIPS symbol and symbol should point to its // PLT entry (in case of microMIPS, PLT entries always contain // microMIPS code). if (Sym->isDefined() && ((Sym->StOther & STO_MIPS_MICROMIPS) || Sym->NeedsPltAddr)) { if (StrTabSec.isDynamic()) ESym->st_value |= 1; ESym->st_other |= STO_MIPS_MICROMIPS; } } if (Config->Relocatable) if (auto *D = dyn_cast(Sym)) if (isMipsPIC(D)) ESym->st_other |= STO_MIPS_PIC; ++ESym; } } } SymtabShndxSection::SymtabShndxSection() : SyntheticSection(0, SHT_SYMTAB_SHNDX, 4, ".symtab_shndxr") { this->Entsize = 4; } void SymtabShndxSection::writeTo(uint8_t *Buf) { // We write an array of 32 bit values, where each value has 1:1 association // with an entry in .symtab. If the corresponding entry contains SHN_XINDEX, // we need to write actual index, otherwise, we must write SHN_UNDEF(0). Buf += 4; // Ignore .symtab[0] entry. for (const SymbolTableEntry &Entry : In.SymTab->getSymbols()) { if (getSymSectionIndex(Entry.Sym) == SHN_XINDEX) write32(Buf, Entry.Sym->getOutputSection()->SectionIndex); Buf += 4; } } bool SymtabShndxSection::empty() const { // SHT_SYMTAB can hold symbols with section indices values up to // SHN_LORESERVE. If we need more, we want to use extension SHT_SYMTAB_SHNDX // section. Problem is that we reveal the final section indices a bit too // late, and we do not know them here. For simplicity, we just always create // a .symtab_shndxr section when the amount of output sections is huge. size_t Size = 0; for (BaseCommand *Base : Script->SectionCommands) if (isa(Base)) ++Size; return Size < SHN_LORESERVE; } void SymtabShndxSection::finalizeContents() { getParent()->Link = In.SymTab->getParent()->SectionIndex; } size_t SymtabShndxSection::getSize() const { return In.SymTab->getNumSymbols() * 4; } // .hash and .gnu.hash sections contain on-disk hash tables that map // symbol names to their dynamic symbol table indices. Their purpose // is to help the dynamic linker resolve symbols quickly. If ELF files // don't have them, the dynamic linker has to do linear search on all // dynamic symbols, which makes programs slower. Therefore, a .hash // section is added to a DSO by default. A .gnu.hash is added if you // give the -hash-style=gnu or -hash-style=both option. // // The Unix semantics of resolving dynamic symbols is somewhat expensive. // Each ELF file has a list of DSOs that the ELF file depends on and a // list of dynamic symbols that need to be resolved from any of the // DSOs. That means resolving all dynamic symbols takes O(m)*O(n) // where m is the number of DSOs and n is the number of dynamic // symbols. For modern large programs, both m and n are large. So // making each step faster by using hash tables substiantially // improves time to load programs. // // (Note that this is not the only way to design the shared library. // For instance, the Windows DLL takes a different approach. On // Windows, each dynamic symbol has a name of DLL from which the symbol // has to be resolved. That makes the cost of symbol resolution O(n). // This disables some hacky techniques you can use on Unix such as // LD_PRELOAD, but this is arguably better semantics than the Unix ones.) // // Due to historical reasons, we have two different hash tables, .hash // and .gnu.hash. They are for the same purpose, and .gnu.hash is a new // and better version of .hash. .hash is just an on-disk hash table, but // .gnu.hash has a bloom filter in addition to a hash table to skip // DSOs very quickly. If you are sure that your dynamic linker knows // about .gnu.hash, you want to specify -hash-style=gnu. Otherwise, a // safe bet is to specify -hash-style=both for backward compatibilty. GnuHashTableSection::GnuHashTableSection() : SyntheticSection(SHF_ALLOC, SHT_GNU_HASH, Config->Wordsize, ".gnu.hash") { } void GnuHashTableSection::finalizeContents() { if (OutputSection *Sec = In.DynSymTab->getParent()) getParent()->Link = Sec->SectionIndex; // Computes bloom filter size in word size. We want to allocate 12 // bits for each symbol. It must be a power of two. if (Symbols.empty()) { MaskWords = 1; } else { uint64_t NumBits = Symbols.size() * 12; MaskWords = NextPowerOf2(NumBits / (Config->Wordsize * 8)); } Size = 16; // Header Size += Config->Wordsize * MaskWords; // Bloom filter Size += NBuckets * 4; // Hash buckets Size += Symbols.size() * 4; // Hash values } void GnuHashTableSection::writeTo(uint8_t *Buf) { // The output buffer is not guaranteed to be zero-cleared because we pre- // fill executable sections with trap instructions. This is a precaution // for that case, which happens only when -no-rosegment is given. memset(Buf, 0, Size); // Write a header. write32(Buf, NBuckets); write32(Buf + 4, In.DynSymTab->getNumSymbols() - Symbols.size()); write32(Buf + 8, MaskWords); write32(Buf + 12, Shift2); Buf += 16; // Write a bloom filter and a hash table. writeBloomFilter(Buf); Buf += Config->Wordsize * MaskWords; writeHashTable(Buf); } // This function writes a 2-bit bloom filter. This bloom filter alone // usually filters out 80% or more of all symbol lookups [1]. // The dynamic linker uses the hash table only when a symbol is not // filtered out by a bloom filter. // // [1] Ulrich Drepper (2011), "How To Write Shared Libraries" (Ver. 4.1.2), // p.9, https://www.akkadia.org/drepper/dsohowto.pdf void GnuHashTableSection::writeBloomFilter(uint8_t *Buf) { unsigned C = Config->Is64 ? 64 : 32; for (const Entry &Sym : Symbols) { // When C = 64, we choose a word with bits [6:...] and set 1 to two bits in // the word using bits [0:5] and [26:31]. size_t I = (Sym.Hash / C) & (MaskWords - 1); uint64_t Val = readUint(Buf + I * Config->Wordsize); Val |= uint64_t(1) << (Sym.Hash % C); Val |= uint64_t(1) << ((Sym.Hash >> Shift2) % C); writeUint(Buf + I * Config->Wordsize, Val); } } void GnuHashTableSection::writeHashTable(uint8_t *Buf) { uint32_t *Buckets = reinterpret_cast(Buf); uint32_t OldBucket = -1; uint32_t *Values = Buckets + NBuckets; for (auto I = Symbols.begin(), E = Symbols.end(); I != E; ++I) { // Write a hash value. It represents a sequence of chains that share the // same hash modulo value. The last element of each chain is terminated by // LSB 1. uint32_t Hash = I->Hash; bool IsLastInChain = (I + 1) == E || I->BucketIdx != (I + 1)->BucketIdx; Hash = IsLastInChain ? Hash | 1 : Hash & ~1; write32(Values++, Hash); if (I->BucketIdx == OldBucket) continue; // Write a hash bucket. Hash buckets contain indices in the following hash // value table. write32(Buckets + I->BucketIdx, I->Sym->DynsymIndex); OldBucket = I->BucketIdx; } } static uint32_t hashGnu(StringRef Name) { uint32_t H = 5381; for (uint8_t C : Name) H = (H << 5) + H + C; return H; } // Add symbols to this symbol hash table. Note that this function // destructively sort a given vector -- which is needed because // GNU-style hash table places some sorting requirements. void GnuHashTableSection::addSymbols(std::vector &V) { // We cannot use 'auto' for Mid because GCC 6.1 cannot deduce // its type correctly. std::vector::iterator Mid = std::stable_partition(V.begin(), V.end(), [](const SymbolTableEntry &S) { return !S.Sym->isDefined(); }); // We chose load factor 4 for the on-disk hash table. For each hash // collision, the dynamic linker will compare a uint32_t hash value. // Since the integer comparison is quite fast, we believe we can // make the load factor even larger. 4 is just a conservative choice. // // Note that we don't want to create a zero-sized hash table because // Android loader as of 2018 doesn't like a .gnu.hash containing such // table. If that's the case, we create a hash table with one unused // dummy slot. NBuckets = std::max((V.end() - Mid) / 4, 1); if (Mid == V.end()) return; for (SymbolTableEntry &Ent : llvm::make_range(Mid, V.end())) { Symbol *B = Ent.Sym; uint32_t Hash = hashGnu(B->getName()); uint32_t BucketIdx = Hash % NBuckets; Symbols.push_back({B, Ent.StrTabOffset, Hash, BucketIdx}); } std::stable_sort( Symbols.begin(), Symbols.end(), [](const Entry &L, const Entry &R) { return L.BucketIdx < R.BucketIdx; }); V.erase(Mid, V.end()); for (const Entry &Ent : Symbols) V.push_back({Ent.Sym, Ent.StrTabOffset}); } HashTableSection::HashTableSection() : SyntheticSection(SHF_ALLOC, SHT_HASH, 4, ".hash") { this->Entsize = 4; } void HashTableSection::finalizeContents() { if (OutputSection *Sec = In.DynSymTab->getParent()) getParent()->Link = Sec->SectionIndex; unsigned NumEntries = 2; // nbucket and nchain. NumEntries += In.DynSymTab->getNumSymbols(); // The chain entries. // Create as many buckets as there are symbols. NumEntries += In.DynSymTab->getNumSymbols(); this->Size = NumEntries * 4; } void HashTableSection::writeTo(uint8_t *Buf) { // See comment in GnuHashTableSection::writeTo. memset(Buf, 0, Size); unsigned NumSymbols = In.DynSymTab->getNumSymbols(); uint32_t *P = reinterpret_cast(Buf); write32(P++, NumSymbols); // nbucket write32(P++, NumSymbols); // nchain uint32_t *Buckets = P; uint32_t *Chains = P + NumSymbols; for (const SymbolTableEntry &S : In.DynSymTab->getSymbols()) { Symbol *Sym = S.Sym; StringRef Name = Sym->getName(); unsigned I = Sym->DynsymIndex; uint32_t Hash = hashSysV(Name) % NumSymbols; Chains[I] = Buckets[Hash]; write32(Buckets + Hash, I); } } // On PowerPC64 the lazy symbol resolvers go into the `global linkage table` // in the .glink section, rather then the typical .plt section. PltSection::PltSection(bool IsIplt) : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, Config->EMachine == EM_PPC64 ? ".glink" : ".plt"), HeaderSize(!IsIplt || Config->ZRetpolineplt ? Target->PltHeaderSize : 0), IsIplt(IsIplt) { // The PLT needs to be writable on SPARC as the dynamic linker will // modify the instructions in the PLT entries. if (Config->EMachine == EM_SPARCV9) this->Flags |= SHF_WRITE; } void PltSection::writeTo(uint8_t *Buf) { // At beginning of PLT or retpoline IPLT, we have code to call the dynamic // linker to resolve dynsyms at runtime. Write such code. if (HeaderSize > 0) Target->writePltHeader(Buf); size_t Off = HeaderSize; // The IPlt is immediately after the Plt, account for this in RelOff unsigned PltOff = getPltRelocOff(); for (auto &I : Entries) { const Symbol *B = I.first; unsigned RelOff = I.second + PltOff; uint64_t Got = B->getGotPltVA(); uint64_t Plt = this->getVA() + Off; Target->writePlt(Buf + Off, Got, Plt, B->PltIndex, RelOff); Off += Target->PltEntrySize; } } template void PltSection::addEntry(Symbol &Sym) { Sym.PltIndex = Entries.size(); RelocationBaseSection *PltRelocSection = In.RelaPlt; if (IsIplt) { PltRelocSection = In.RelaIplt; Sym.IsInIplt = true; } unsigned RelOff = static_cast *>(PltRelocSection)->getRelocOffset(); Entries.push_back(std::make_pair(&Sym, RelOff)); } size_t PltSection::getSize() const { return HeaderSize + Entries.size() * Target->PltEntrySize; } // Some architectures such as additional symbols in the PLT section. For // example ARM uses mapping symbols to aid disassembly void PltSection::addSymbols() { // The PLT may have symbols defined for the Header, the IPLT has no header if (!IsIplt) Target->addPltHeaderSymbols(*this); size_t Off = HeaderSize; for (size_t I = 0; I < Entries.size(); ++I) { Target->addPltSymbols(*this, Off); Off += Target->PltEntrySize; } } unsigned PltSection::getPltRelocOff() const { return IsIplt ? In.Plt->getSize() : 0; } // The string hash function for .gdb_index. static uint32_t computeGdbHash(StringRef S) { uint32_t H = 0; for (uint8_t C : S) H = H * 67 + toLower(C) - 113; return H; } GdbIndexSection::GdbIndexSection() : SyntheticSection(0, SHT_PROGBITS, 1, ".gdb_index") {} // Returns the desired size of an on-disk hash table for a .gdb_index section. // There's a tradeoff between size and collision rate. We aim 75% utilization. size_t GdbIndexSection::computeSymtabSize() const { return std::max(NextPowerOf2(Symbols.size() * 4 / 3), 1024); } // Compute the output section size. void GdbIndexSection::initOutputSize() { Size = sizeof(GdbIndexHeader) + computeSymtabSize() * 8; for (GdbChunk &Chunk : Chunks) Size += Chunk.CompilationUnits.size() * 16 + Chunk.AddressAreas.size() * 20; // Add the constant pool size if exists. if (!Symbols.empty()) { GdbSymbol &Sym = Symbols.back(); Size += Sym.NameOff + Sym.Name.size() + 1; } } static std::vector getDebugInfoSections() { std::vector Ret; for (InputSectionBase *S : InputSections) if (InputSection *IS = dyn_cast(S)) if (IS->Name == ".debug_info") Ret.push_back(IS); return Ret; } static std::vector readCuList(DWARFContext &Dwarf) { std::vector Ret; for (std::unique_ptr &Cu : Dwarf.compile_units()) Ret.push_back({Cu->getOffset(), Cu->getLength() + 4}); return Ret; } static std::vector readAddressAreas(DWARFContext &Dwarf, InputSection *Sec) { std::vector Ret; uint32_t CuIdx = 0; for (std::unique_ptr &Cu : Dwarf.compile_units()) { Expected Ranges = Cu->collectAddressRanges(); if (!Ranges) { error(toString(Sec) + ": " + toString(Ranges.takeError())); return {}; } ArrayRef Sections = Sec->File->getSections(); for (DWARFAddressRange &R : *Ranges) { InputSectionBase *S = Sections[R.SectionIndex]; if (!S || S == &InputSection::Discarded || !S->Live) continue; // Range list with zero size has no effect. if (R.LowPC == R.HighPC) continue; auto *IS = cast(S); uint64_t Offset = IS->getOffsetInFile(); Ret.push_back({IS, R.LowPC - Offset, R.HighPC - Offset, CuIdx}); } ++CuIdx; } return Ret; } template static std::vector readPubNamesAndTypes(const LLDDwarfObj &Obj, const std::vector &CUs) { const DWARFSection &PubNames = Obj.getGnuPubNamesSection(); const DWARFSection &PubTypes = Obj.getGnuPubTypesSection(); std::vector Ret; for (const DWARFSection *Pub : {&PubNames, &PubTypes}) { DWARFDebugPubTable Table(Obj, *Pub, Config->IsLE, true); for (const DWARFDebugPubTable::Set &Set : Table.getData()) { // The value written into the constant pool is Kind << 24 | CuIndex. As we // don't know how many compilation units precede this object to compute // CuIndex, we compute (Kind << 24 | CuIndexInThisObject) instead, and add // the number of preceding compilation units later. uint32_t I = lower_bound(CUs, Set.Offset, [](GdbIndexSection::CuEntry CU, uint32_t Offset) { return CU.CuOffset < Offset; }) - CUs.begin(); for (const DWARFDebugPubTable::Entry &Ent : Set.Entries) Ret.push_back({{Ent.Name, computeGdbHash(Ent.Name)}, (Ent.Descriptor.toBits() << 24) | I}); } } return Ret; } // Create a list of symbols from a given list of symbol names and types // by uniquifying them by name. static std::vector createSymbols(ArrayRef> NameAttrs, const std::vector &Chunks) { typedef GdbIndexSection::GdbSymbol GdbSymbol; typedef GdbIndexSection::NameAttrEntry NameAttrEntry; // For each chunk, compute the number of compilation units preceding it. uint32_t CuIdx = 0; std::vector CuIdxs(Chunks.size()); for (uint32_t I = 0, E = Chunks.size(); I != E; ++I) { CuIdxs[I] = CuIdx; CuIdx += Chunks[I].CompilationUnits.size(); } // The number of symbols we will handle in this function is of the order // of millions for very large executables, so we use multi-threading to // speed it up. size_t NumShards = 32; size_t Concurrency = 1; if (ThreadsEnabled) Concurrency = std::min(PowerOf2Floor(hardware_concurrency()), NumShards); // A sharded map to uniquify symbols by name. std::vector> Map(NumShards); size_t Shift = 32 - countTrailingZeros(NumShards); // Instantiate GdbSymbols while uniqufying them by name. std::vector> Symbols(NumShards); parallelForEachN(0, Concurrency, [&](size_t ThreadId) { uint32_t I = 0; for (ArrayRef Entries : NameAttrs) { for (const NameAttrEntry &Ent : Entries) { size_t ShardId = Ent.Name.hash() >> Shift; if ((ShardId & (Concurrency - 1)) != ThreadId) continue; uint32_t V = Ent.CuIndexAndAttrs + CuIdxs[I]; size_t &Idx = Map[ShardId][Ent.Name]; if (Idx) { Symbols[ShardId][Idx - 1].CuVector.push_back(V); continue; } Idx = Symbols[ShardId].size() + 1; Symbols[ShardId].push_back({Ent.Name, {V}, 0, 0}); } ++I; } }); size_t NumSymbols = 0; for (ArrayRef V : Symbols) NumSymbols += V.size(); // The return type is a flattened vector, so we'll copy each vector // contents to Ret. std::vector Ret; Ret.reserve(NumSymbols); for (std::vector &Vec : Symbols) for (GdbSymbol &Sym : Vec) Ret.push_back(std::move(Sym)); // CU vectors and symbol names are adjacent in the output file. // We can compute their offsets in the output file now. size_t Off = 0; for (GdbSymbol &Sym : Ret) { Sym.CuVectorOff = Off; Off += (Sym.CuVector.size() + 1) * 4; } for (GdbSymbol &Sym : Ret) { Sym.NameOff = Off; Off += Sym.Name.size() + 1; } return Ret; } // Returns a newly-created .gdb_index section. template GdbIndexSection *GdbIndexSection::create() { std::vector Sections = getDebugInfoSections(); // .debug_gnu_pub{names,types} are useless in executables. // They are present in input object files solely for creating // a .gdb_index. So we can remove them from the output. for (InputSectionBase *S : InputSections) if (S->Name == ".debug_gnu_pubnames" || S->Name == ".debug_gnu_pubtypes") S->Live = false; std::vector Chunks(Sections.size()); std::vector> NameAttrs(Sections.size()); parallelForEachN(0, Sections.size(), [&](size_t I) { ObjFile *File = Sections[I]->getFile(); DWARFContext Dwarf(make_unique>(File)); Chunks[I].Sec = Sections[I]; Chunks[I].CompilationUnits = readCuList(Dwarf); Chunks[I].AddressAreas = readAddressAreas(Dwarf, Sections[I]); NameAttrs[I] = readPubNamesAndTypes( static_cast &>(Dwarf.getDWARFObj()), Chunks[I].CompilationUnits); }); auto *Ret = make(); Ret->Chunks = std::move(Chunks); Ret->Symbols = createSymbols(NameAttrs, Ret->Chunks); Ret->initOutputSize(); return Ret; } void GdbIndexSection::writeTo(uint8_t *Buf) { // Write the header. auto *Hdr = reinterpret_cast(Buf); uint8_t *Start = Buf; Hdr->Version = 7; Buf += sizeof(*Hdr); // Write the CU list. Hdr->CuListOff = Buf - Start; for (GdbChunk &Chunk : Chunks) { for (CuEntry &Cu : Chunk.CompilationUnits) { write64le(Buf, Chunk.Sec->OutSecOff + Cu.CuOffset); write64le(Buf + 8, Cu.CuLength); Buf += 16; } } // Write the address area. Hdr->CuTypesOff = Buf - Start; Hdr->AddressAreaOff = Buf - Start; uint32_t CuOff = 0; for (GdbChunk &Chunk : Chunks) { for (AddressEntry &E : Chunk.AddressAreas) { uint64_t BaseAddr = E.Section->getVA(0); write64le(Buf, BaseAddr + E.LowAddress); write64le(Buf + 8, BaseAddr + E.HighAddress); write32le(Buf + 16, E.CuIndex + CuOff); Buf += 20; } CuOff += Chunk.CompilationUnits.size(); } // Write the on-disk open-addressing hash table containing symbols. Hdr->SymtabOff = Buf - Start; size_t SymtabSize = computeSymtabSize(); uint32_t Mask = SymtabSize - 1; for (GdbSymbol &Sym : Symbols) { uint32_t H = Sym.Name.hash(); uint32_t I = H & Mask; uint32_t Step = ((H * 17) & Mask) | 1; while (read32le(Buf + I * 8)) I = (I + Step) & Mask; write32le(Buf + I * 8, Sym.NameOff); write32le(Buf + I * 8 + 4, Sym.CuVectorOff); } Buf += SymtabSize * 8; // Write the string pool. Hdr->ConstantPoolOff = Buf - Start; parallelForEach(Symbols, [&](GdbSymbol &Sym) { memcpy(Buf + Sym.NameOff, Sym.Name.data(), Sym.Name.size()); }); // Write the CU vectors. for (GdbSymbol &Sym : Symbols) { write32le(Buf, Sym.CuVector.size()); Buf += 4; for (uint32_t Val : Sym.CuVector) { write32le(Buf, Val); Buf += 4; } } } bool GdbIndexSection::empty() const { return Chunks.empty(); } EhFrameHeader::EhFrameHeader() : SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 4, ".eh_frame_hdr") {} // .eh_frame_hdr contains a binary search table of pointers to FDEs. // Each entry of the search table consists of two values, // the starting PC from where FDEs covers, and the FDE's address. // It is sorted by PC. void EhFrameHeader::writeTo(uint8_t *Buf) { typedef EhFrameSection::FdeData FdeData; std::vector Fdes = In.EhFrame->getFdeData(); Buf[0] = 1; Buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4; Buf[2] = DW_EH_PE_udata4; Buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; write32(Buf + 4, In.EhFrame->getParent()->Addr - this->getVA() - 4); write32(Buf + 8, Fdes.size()); Buf += 12; for (FdeData &Fde : Fdes) { write32(Buf, Fde.PcRel); write32(Buf + 4, Fde.FdeVARel); Buf += 8; } } size_t EhFrameHeader::getSize() const { // .eh_frame_hdr has a 12 bytes header followed by an array of FDEs. return 12 + In.EhFrame->NumFdes * 8; } bool EhFrameHeader::empty() const { return In.EhFrame->empty(); } VersionDefinitionSection::VersionDefinitionSection() : SyntheticSection(SHF_ALLOC, SHT_GNU_verdef, sizeof(uint32_t), ".gnu.version_d") {} static StringRef getFileDefName() { if (!Config->SoName.empty()) return Config->SoName; return Config->OutputFile; } void VersionDefinitionSection::finalizeContents() { FileDefNameOff = In.DynStrTab->addString(getFileDefName()); for (VersionDefinition &V : Config->VersionDefinitions) V.NameOff = In.DynStrTab->addString(V.Name); if (OutputSection *Sec = In.DynStrTab->getParent()) getParent()->Link = Sec->SectionIndex; // sh_info should be set to the number of definitions. This fact is missed in // documentation, but confirmed by binutils community: // https://sourceware.org/ml/binutils/2014-11/msg00355.html getParent()->Info = getVerDefNum(); } void VersionDefinitionSection::writeOne(uint8_t *Buf, uint32_t Index, StringRef Name, size_t NameOff) { uint16_t Flags = Index == 1 ? VER_FLG_BASE : 0; // Write a verdef. write16(Buf, 1); // vd_version write16(Buf + 2, Flags); // vd_flags write16(Buf + 4, Index); // vd_ndx write16(Buf + 6, 1); // vd_cnt write32(Buf + 8, hashSysV(Name)); // vd_hash write32(Buf + 12, 20); // vd_aux write32(Buf + 16, 28); // vd_next // Write a veraux. write32(Buf + 20, NameOff); // vda_name write32(Buf + 24, 0); // vda_next } void VersionDefinitionSection::writeTo(uint8_t *Buf) { writeOne(Buf, 1, getFileDefName(), FileDefNameOff); for (VersionDefinition &V : Config->VersionDefinitions) { Buf += EntrySize; writeOne(Buf, V.Id, V.Name, V.NameOff); } // Need to terminate the last version definition. write32(Buf + 16, 0); // vd_next } size_t VersionDefinitionSection::getSize() const { return EntrySize * getVerDefNum(); } // .gnu.version is a table where each entry is 2 byte long. template VersionTableSection::VersionTableSection() : SyntheticSection(SHF_ALLOC, SHT_GNU_versym, sizeof(uint16_t), ".gnu.version") { this->Entsize = 2; } template void VersionTableSection::finalizeContents() { // At the moment of june 2016 GNU docs does not mention that sh_link field // should be set, but Sun docs do. Also readelf relies on this field. getParent()->Link = In.DynSymTab->getParent()->SectionIndex; } template size_t VersionTableSection::getSize() const { return (In.DynSymTab->getSymbols().size() + 1) * 2; } template void VersionTableSection::writeTo(uint8_t *Buf) { Buf += 2; for (const SymbolTableEntry &S : In.DynSymTab->getSymbols()) { write16(Buf, S.Sym->VersionId); Buf += 2; } } template bool VersionTableSection::empty() const { return !In.VerDef && InX::VerNeed->empty(); } template VersionNeedSection::VersionNeedSection() : SyntheticSection(SHF_ALLOC, SHT_GNU_verneed, sizeof(uint32_t), ".gnu.version_r") { // Identifiers in verneed section start at 2 because 0 and 1 are reserved // for VER_NDX_LOCAL and VER_NDX_GLOBAL. // First identifiers are reserved by verdef section if it exist. NextIndex = getVerDefNum() + 1; } template void VersionNeedSection::addSymbol(Symbol *SS) { auto &File = cast>(*SS->File); if (SS->VerdefIndex == VER_NDX_GLOBAL) { SS->VersionId = VER_NDX_GLOBAL; return; } // If we don't already know that we need an Elf_Verneed for this DSO, prepare // to create one by adding it to our needed list and creating a dynstr entry // for the soname. if (File.VerdefMap.empty()) Needed.push_back({&File, In.DynStrTab->addString(File.SoName)}); const typename ELFT::Verdef *Ver = File.Verdefs[SS->VerdefIndex]; typename SharedFile::NeededVer &NV = File.VerdefMap[Ver]; // If we don't already know that we need an Elf_Vernaux for this Elf_Verdef, // prepare to create one by allocating a version identifier and creating a // dynstr entry for the version name. if (NV.Index == 0) { NV.StrTab = In.DynStrTab->addString(File.getStringTable().data() + Ver->getAux()->vda_name); NV.Index = NextIndex++; } SS->VersionId = NV.Index; } template void VersionNeedSection::writeTo(uint8_t *Buf) { // The Elf_Verneeds need to appear first, followed by the Elf_Vernauxs. auto *Verneed = reinterpret_cast(Buf); auto *Vernaux = reinterpret_cast(Verneed + Needed.size()); for (std::pair *, size_t> &P : Needed) { // Create an Elf_Verneed for this DSO. Verneed->vn_version = 1; Verneed->vn_cnt = P.first->VerdefMap.size(); Verneed->vn_file = P.second; Verneed->vn_aux = reinterpret_cast(Vernaux) - reinterpret_cast(Verneed); Verneed->vn_next = sizeof(Elf_Verneed); ++Verneed; // Create the Elf_Vernauxs for this Elf_Verneed. The loop iterates over // VerdefMap, which will only contain references to needed version // definitions. Each Elf_Vernaux is based on the information contained in // the Elf_Verdef in the source DSO. This loop iterates over a std::map of // pointers, but is deterministic because the pointers refer to Elf_Verdef // data structures within a single input file. for (auto &NV : P.first->VerdefMap) { Vernaux->vna_hash = NV.first->vd_hash; Vernaux->vna_flags = 0; Vernaux->vna_other = NV.second.Index; Vernaux->vna_name = NV.second.StrTab; Vernaux->vna_next = sizeof(Elf_Vernaux); ++Vernaux; } Vernaux[-1].vna_next = 0; } Verneed[-1].vn_next = 0; } template void VersionNeedSection::finalizeContents() { if (OutputSection *Sec = In.DynStrTab->getParent()) getParent()->Link = Sec->SectionIndex; getParent()->Info = Needed.size(); } template size_t VersionNeedSection::getSize() const { unsigned Size = Needed.size() * sizeof(Elf_Verneed); for (const std::pair *, size_t> &P : Needed) Size += P.first->VerdefMap.size() * sizeof(Elf_Vernaux); return Size; } template bool VersionNeedSection::empty() const { return getNeedNum() == 0; } void MergeSyntheticSection::addSection(MergeInputSection *MS) { MS->Parent = this; Sections.push_back(MS); } MergeTailSection::MergeTailSection(StringRef Name, uint32_t Type, uint64_t Flags, uint32_t Alignment) : MergeSyntheticSection(Name, Type, Flags, Alignment), Builder(StringTableBuilder::RAW, Alignment) {} size_t MergeTailSection::getSize() const { return Builder.getSize(); } void MergeTailSection::writeTo(uint8_t *Buf) { Builder.write(Buf); } void MergeTailSection::finalizeContents() { // Add all string pieces to the string table builder to create section // contents. for (MergeInputSection *Sec : Sections) for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) if (Sec->Pieces[I].Live) Builder.add(Sec->getData(I)); // Fix the string table content. After this, the contents will never change. Builder.finalize(); // finalize() fixed tail-optimized strings, so we can now get // offsets of strings. Get an offset for each string and save it // to a corresponding StringPiece for easy access. for (MergeInputSection *Sec : Sections) for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) if (Sec->Pieces[I].Live) Sec->Pieces[I].OutputOff = Builder.getOffset(Sec->getData(I)); } void MergeNoTailSection::writeTo(uint8_t *Buf) { for (size_t I = 0; I < NumShards; ++I) Shards[I].write(Buf + ShardOffsets[I]); } // This function is very hot (i.e. it can take several seconds to finish) // because sometimes the number of inputs is in an order of magnitude of // millions. So, we use multi-threading. // // For any strings S and T, we know S is not mergeable with T if S's hash // value is different from T's. If that's the case, we can safely put S and // T into different string builders without worrying about merge misses. // We do it in parallel. void MergeNoTailSection::finalizeContents() { // Initializes string table builders. for (size_t I = 0; I < NumShards; ++I) Shards.emplace_back(StringTableBuilder::RAW, Alignment); // Concurrency level. Must be a power of 2 to avoid expensive modulo // operations in the following tight loop. size_t Concurrency = 1; if (ThreadsEnabled) Concurrency = std::min(PowerOf2Floor(hardware_concurrency()), NumShards); // Add section pieces to the builders. parallelForEachN(0, Concurrency, [&](size_t ThreadId) { for (MergeInputSection *Sec : Sections) { for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) { size_t ShardId = getShardId(Sec->Pieces[I].Hash); if ((ShardId & (Concurrency - 1)) == ThreadId && Sec->Pieces[I].Live) Sec->Pieces[I].OutputOff = Shards[ShardId].add(Sec->getData(I)); } } }); // Compute an in-section offset for each shard. size_t Off = 0; for (size_t I = 0; I < NumShards; ++I) { Shards[I].finalizeInOrder(); if (Shards[I].getSize() > 0) Off = alignTo(Off, Alignment); ShardOffsets[I] = Off; Off += Shards[I].getSize(); } Size = Off; // So far, section pieces have offsets from beginning of shards, but // we want offsets from beginning of the whole section. Fix them. parallelForEach(Sections, [&](MergeInputSection *Sec) { for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) if (Sec->Pieces[I].Live) Sec->Pieces[I].OutputOff += ShardOffsets[getShardId(Sec->Pieces[I].Hash)]; }); } static MergeSyntheticSection *createMergeSynthetic(StringRef Name, uint32_t Type, uint64_t Flags, uint32_t Alignment) { bool ShouldTailMerge = (Flags & SHF_STRINGS) && Config->Optimize >= 2; if (ShouldTailMerge) return make(Name, Type, Flags, Alignment); return make(Name, Type, Flags, Alignment); } template void elf::splitSections() { // splitIntoPieces needs to be called on each MergeInputSection // before calling finalizeContents(). parallelForEach(InputSections, [](InputSectionBase *Sec) { if (auto *S = dyn_cast(Sec)) S->splitIntoPieces(); else if (auto *Eh = dyn_cast(Sec)) Eh->split(); }); } // This function scans over the inputsections to create mergeable // synthetic sections. // // It removes MergeInputSections from the input section array and adds // new synthetic sections at the location of the first input section // that it replaces. It then finalizes each synthetic section in order // to compute an output offset for each piece of each input section. void elf::mergeSections() { std::vector MergeSections; for (InputSectionBase *&S : InputSections) { MergeInputSection *MS = dyn_cast(S); if (!MS) continue; // We do not want to handle sections that are not alive, so just remove // them instead of trying to merge. if (!MS->Live) { S = nullptr; continue; } StringRef OutsecName = getOutputSectionName(MS); uint32_t Alignment = std::max(MS->Alignment, MS->Entsize); auto I = llvm::find_if(MergeSections, [=](MergeSyntheticSection *Sec) { // While we could create a single synthetic section for two different // values of Entsize, it is better to take Entsize into consideration. // // With a single synthetic section no two pieces with different Entsize // could be equal, so we may as well have two sections. // // Using Entsize in here also allows us to propagate it to the synthetic // section. return Sec->Name == OutsecName && Sec->Flags == MS->Flags && Sec->Entsize == MS->Entsize && Sec->Alignment == Alignment; }); if (I == MergeSections.end()) { MergeSyntheticSection *Syn = createMergeSynthetic(OutsecName, MS->Type, MS->Flags, Alignment); MergeSections.push_back(Syn); I = std::prev(MergeSections.end()); S = Syn; Syn->Entsize = MS->Entsize; } else { S = nullptr; } (*I)->addSection(MS); } for (auto *MS : MergeSections) MS->finalizeContents(); std::vector &V = InputSections; V.erase(std::remove(V.begin(), V.end(), nullptr), V.end()); } MipsRldMapSection::MipsRldMapSection() : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, Config->Wordsize, ".rld_map") {} ARMExidxSentinelSection::ARMExidxSentinelSection() : SyntheticSection(SHF_ALLOC | SHF_LINK_ORDER, SHT_ARM_EXIDX, Config->Wordsize, ".ARM.exidx") {} // Write a terminating sentinel entry to the end of the .ARM.exidx table. // This section will have been sorted last in the .ARM.exidx table. // This table entry will have the form: // | PREL31 upper bound of code that has exception tables | EXIDX_CANTUNWIND | // The sentinel must have the PREL31 value of an address higher than any // address described by any other table entry. void ARMExidxSentinelSection::writeTo(uint8_t *Buf) { assert(Highest); uint64_t S = Highest->getVA(Highest->getSize()); uint64_t P = getVA(); Target->relocateOne(Buf, R_ARM_PREL31, S - P); write32le(Buf + 4, 1); } // The sentinel has to be removed if there are no other .ARM.exidx entries. bool ARMExidxSentinelSection::empty() const { for (InputSection *IS : getInputSections(getParent())) if (!isa(IS)) return false; return true; } bool ARMExidxSentinelSection::classof(const SectionBase *D) { return D->kind() == InputSectionBase::Synthetic && D->Type == SHT_ARM_EXIDX; } ThunkSection::ThunkSection(OutputSection *OS, uint64_t Off) : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, Config->Wordsize, ".text.thunk") { this->Parent = OS; this->OutSecOff = Off; } void ThunkSection::addThunk(Thunk *T) { Thunks.push_back(T); T->addSymbols(*this); } void ThunkSection::writeTo(uint8_t *Buf) { for (Thunk *T : Thunks) T->writeTo(Buf + T->Offset); } InputSection *ThunkSection::getTargetInputSection() const { if (Thunks.empty()) return nullptr; const Thunk *T = Thunks.front(); return T->getTargetInputSection(); } bool ThunkSection::assignOffsets() { uint64_t Off = 0; for (Thunk *T : Thunks) { Off = alignTo(Off, T->Alignment); T->setOffset(Off); uint32_t Size = T->size(); T->getThunkTargetSym()->Size = Size; Off += Size; } bool Changed = Off != Size; Size = Off; return Changed; } // If linking position-dependent code then the table will store the addresses // directly in the binary so the section has type SHT_PROGBITS. If linking // position-independent code the section has type SHT_NOBITS since it will be // allocated and filled in by the dynamic linker. PPC64LongBranchTargetSection::PPC64LongBranchTargetSection() : SyntheticSection(SHF_ALLOC | SHF_WRITE, Config->Pic ? SHT_NOBITS : SHT_PROGBITS, 8, ".branch_lt") {} void PPC64LongBranchTargetSection::addEntry(Symbol &Sym) { assert(Sym.PPC64BranchltIndex == 0xffff); Sym.PPC64BranchltIndex = Entries.size(); Entries.push_back(&Sym); } size_t PPC64LongBranchTargetSection::getSize() const { return Entries.size() * 8; } void PPC64LongBranchTargetSection::writeTo(uint8_t *Buf) { assert(Target->GotPltEntrySize == 8); // If linking non-pic we have the final addresses of the targets and they get // written to the table directly. For pic the dynamic linker will allocate // the section and fill it it. if (Config->Pic) return; for (const Symbol *Sym : Entries) { assert(Sym->getVA()); // Need calls to branch to the local entry-point since a long-branch // must be a local-call. write64(Buf, Sym->getVA() + getPPC64GlobalEntryToLocalEntryOffset(Sym->StOther)); Buf += Target->GotPltEntrySize; } } bool PPC64LongBranchTargetSection::empty() const { // `removeUnusedSyntheticSections()` is called before thunk allocation which // is too early to determine if this section will be empty or not. We need // Finalized to keep the section alive until after thunk creation. Finalized // only gets set to true once `finalizeSections()` is called after thunk // creation. Becuase of this, if we don't create any long-branch thunks we end // up with an empty .branch_lt section in the binary. return Finalized && Entries.empty(); } InStruct elf::In; template GdbIndexSection *GdbIndexSection::create(); template GdbIndexSection *GdbIndexSection::create(); template GdbIndexSection *GdbIndexSection::create(); template GdbIndexSection *GdbIndexSection::create(); template void elf::splitSections(); template void elf::splitSections(); template void elf::splitSections(); template void elf::splitSections(); template void EhFrameSection::addSection(InputSectionBase *); template void EhFrameSection::addSection(InputSectionBase *); template void EhFrameSection::addSection(InputSectionBase *); template void EhFrameSection::addSection(InputSectionBase *); template void PltSection::addEntry(Symbol &Sym); template void PltSection::addEntry(Symbol &Sym); template void PltSection::addEntry(Symbol &Sym); template void PltSection::addEntry(Symbol &Sym); template void MipsGotSection::build(); template void MipsGotSection::build(); template void MipsGotSection::build(); template void MipsGotSection::build(); template class elf::MipsAbiFlagsSection; template class elf::MipsAbiFlagsSection; template class elf::MipsAbiFlagsSection; template class elf::MipsAbiFlagsSection; template class elf::MipsOptionsSection; template class elf::MipsOptionsSection; template class elf::MipsOptionsSection; template class elf::MipsOptionsSection; template class elf::MipsReginfoSection; template class elf::MipsReginfoSection; template class elf::MipsReginfoSection; template class elf::MipsReginfoSection; template class elf::DynamicSection; template class elf::DynamicSection; template class elf::DynamicSection; template class elf::DynamicSection; template class elf::RelocationSection; template class elf::RelocationSection; template class elf::RelocationSection; template class elf::RelocationSection; template class elf::AndroidPackedRelocationSection; template class elf::AndroidPackedRelocationSection; template class elf::AndroidPackedRelocationSection; template class elf::AndroidPackedRelocationSection; template class elf::RelrSection; template class elf::RelrSection; template class elf::RelrSection; template class elf::RelrSection; template class elf::SymbolTableSection; template class elf::SymbolTableSection; template class elf::SymbolTableSection; template class elf::SymbolTableSection; template class elf::VersionTableSection; template class elf::VersionTableSection; template class elf::VersionTableSection; template class elf::VersionTableSection; template class elf::VersionNeedSection; template class elf::VersionNeedSection; template class elf::VersionNeedSection; template class elf::VersionNeedSection; Index: vendor/lld/dist-release_80/MinGW/Options.td =================================================================== --- vendor/lld/dist-release_80/MinGW/Options.td (revision 343801) +++ vendor/lld/dist-release_80/MinGW/Options.td (revision 343802) @@ -1,80 +1,86 @@ include "llvm/Option/OptParser.td" class F: Flag<["--", "-"], name>; class J: Joined<["--", "-"], name>; class S: Separate<["--", "-"], name>; def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"">, HelpText<"Add a directory to the library search path">; def dynamicbase: F<"dynamicbase">, HelpText<"Enable ASLR">; def entry: S<"entry">, MetaVarName<"">, HelpText<"Name of entry point symbol">; def export_all_symbols: F<"export-all-symbols">, HelpText<"Export all symbols even if a def file or dllexport attributes are used">; def gc_sections: F<"gc-sections">, HelpText<"Remove unused sections">; def icf: J<"icf=">, HelpText<"Identical code folding">; def image_base: S<"image-base">, HelpText<"Base address of the program">; def kill_at: F<"kill-at">, HelpText<"Remove @n from exported symbols">; def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"">, HelpText<"Root name of library to use">; def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">; def map: S<"Map">, HelpText<"Output a linker map">; def map_eq: J<"Map=">, Alias; def no_whole_archive: F<"no-whole-archive">, HelpText<"No longer include all object files for following archives">; def large_address_aware: Flag<["--"], "large-address-aware">, HelpText<"Enable large addresses">; def no_gc_sections: F<"no-gc-sections">, HelpText<"Don't remove unused sections">; def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"">, HelpText<"Path to file to write output">; def out_implib: Separate<["--"], "out-implib">, HelpText<"Import library name">; def out_implib_eq: Joined<["--"], "out-implib=">, Alias; def output_def: S<"output-def">, HelpText<"Output def file">; def shared: F<"shared">, HelpText<"Build a shared object">; def subs: S<"subsystem">, HelpText<"Specify subsystem">; def stack: S<"stack">; def strip_all: F<"strip-all">, HelpText<"Omit all symbol information from the output binary">; def strip_debug: F<"strip-debug">, HelpText<"Omit all debug information, but keep symbol information">; def whole_archive: F<"whole-archive">, HelpText<"Include all object files for following archives">; def verbose: F<"verbose">, HelpText<"Verbose mode">; def require_defined: S<"require-defined">, HelpText<"Force symbol to be added to symbol table as an undefined one">; def require_defined_eq: J<"require-defined=">, Alias; // LLD specific options def _HASH_HASH_HASH : Flag<["-"], "###">, HelpText<"Print (but do not run) the commands to run for this compilation">; def mllvm: S<"mllvm">; def pdb: S<"pdb">, HelpText<"Specify output PDB debug information file">; def Xlink : J<"Xlink=">, MetaVarName<"">, HelpText<"Pass to the COFF linker">; // Currently stubs to avoid errors def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries">; def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">; def O: Joined<["-"], "O">, HelpText<"Optimize output file size">; def build_id: F<"build-id">; def disable_auto_image_base: F<"disable-auto-image-base">; def enable_auto_image_base: F<"enable-auto-image-base">; def enable_auto_import: F<"enable-auto-import">; def end_group: F<"end-group">; def full_shutdown: Flag<["--"], "full-shutdown">; def high_entropy_va: F<"high-entropy-va">, HelpText<"Enable 64-bit ASLR">; def major_image_version: S<"major-image-version">; def minor_image_version: S<"minor-image-version">; def no_seh: F<"no-seh">; def nxcompat: F<"nxcompat">, HelpText<"Enable data execution prevention">; def pic_executable: F<"pic-executable">; def sysroot: J<"sysroot">, HelpText<"Sysroot">; def start_group: F<"start-group">; def tsaware: F<"tsaware">, HelpText<"Create Terminal Server aware executable">; def v: Flag<["-"], "v">, HelpText<"Display the version number">; def version: F<"version">, HelpText<"Display the version number and exit">; // Alias def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias; def alias_strip_s: Flag<["-"], "s">, Alias; def alias_strip_S: Flag<["-"], "S">, Alias; + +// Ignored options +def: S<"plugin">; +def: J<"plugin=">; +def: S<"plugin-opt">; +def: J<"plugin-opt=">; Index: vendor/lld/dist-release_80/docs/ReleaseNotes.rst =================================================================== --- vendor/lld/dist-release_80/docs/ReleaseNotes.rst (revision 343801) +++ vendor/lld/dist-release_80/docs/ReleaseNotes.rst (revision 343802) @@ -1,80 +1,90 @@ ======================= lld 8.0.0 Release Notes ======================= .. contents:: :local: .. warning:: These are in-progress notes for the upcoming LLVM 8.0.0 release. Release notes for previous releases can be found on `the Download Page `_. Introduction ============ This document contains the release notes for the lld linker, release 8.0.0. Here we describe the status of lld, including major improvements from the previous release. All lld releases may be downloaded from the `LLVM releases web site `_. Non-comprehensive list of changes in this release ================================================= ELF Improvements ---------------- * lld now supports RISC-V. (`r339364 `_) * Default image base address has changed from 65536 to 2 MiB for i386 and 4 MiB for AArch64 to make lld-generated executables work better with automatic superpage promotion. FreeBSD can promote contiguous non-superpages to a superpage if they are aligned to the superpage size. (`r342746 `_) * lld/Hexagon can now link Linux kernel and musl libc for Qualcomm Hexagon ISA. * Initial MSP430 ISA support has landed. * The following flags have been added: ``-z interpose``, ``-z global`` +* lld now uses the ``sigrie`` instruction as a trap instruction for + MIPS targets. + COFF Improvements ----------------- * PDB GUID is set to hash of PDB contents instead to a random byte sequence for build reproducibility. * The following flags have been added: ``/force:multiple`` * lld now can link against import libraries produced by GNU tools. * lld can create thunks for ARM, to allow linking images over 16 MB. MinGW Improvements ------------------ * lld can now automatically import data variables from DLLs without the use of the dllimport attribute. * lld can now use existing normal MinGW sysroots with import libraries and CRT startup object files for GNU binutils. lld can handle most object files produced by GCC, and thus works as a drop-in replacement for ld.bfd in such environments. (There are known issues with linking crtend.o from GCC in setups with DWARF exceptions though, where object files are linked in a different order than with GNU ld, inserting a DWARF exception table terminator too early.) + +* lld now supports COFF embedded directives for linking to nondefault + libraries, just like for the normal COFF target. + +* Actually generate a codeview build id signature, even if not creating a PDB. + Previously, the ``--build-id`` option did not actually generate a build id + unless ``--pdb`` was specified. MachO Improvements ------------------ * Item 1. WebAssembly Improvements ------------------------ * Add initial support for creating shared libraries (-shared). Note: The shared library format is still under active development and may undergo significant changes in future versions. See: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md Index: vendor/lld/dist-release_80/test/COFF/arm64-branch-range.test =================================================================== --- vendor/lld/dist-release_80/test/COFF/arm64-branch-range.test (revision 343801) +++ vendor/lld/dist-release_80/test/COFF/arm64-branch-range.test (nonexistent) @@ -1,16 +0,0 @@ -// REQUIRES: aarch64 - -// RUN: echo -e '.globl _start\n _start:\n bl too_far26\n' > %t.main26.s -// RUN: echo -e '.globl _start\n _start:\n b.ne too_far19\n' > %t.main19.s -// RUN: echo -e '.globl _start\n _start:\n tbz x0, #0, too_far14\n' > %t.main14.s - -// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %t.main26.s -o %t.main26.obj -// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %t.main19.s -o %t.main19.obj -// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %t.main14.s -o %t.main14.obj -// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/far-arm64-abs.s -o %t.far.obj - -// RUN: not lld-link -base:0x10000 -entry:_start -subsystem:console %t.main26.obj %t.far.obj -out:%t.exe 2>&1 | FileCheck %s -// RUN: not lld-link -base:0x10000 -entry:_start -subsystem:console %t.main19.obj %t.far.obj -out:%t.exe 2>&1 | FileCheck %s -// RUN: not lld-link -base:0x10000 -entry:_start -subsystem:console %t.main14.obj %t.far.obj -out:%t.exe 2>&1 | FileCheck %s - -// CHECK: relocation out of range Index: vendor/lld/dist-release_80/test/COFF/arm-thumb-thunks-pdb.s =================================================================== --- vendor/lld/dist-release_80/test/COFF/arm-thumb-thunks-pdb.s (nonexistent) +++ vendor/lld/dist-release_80/test/COFF/arm-thumb-thunks-pdb.s (revision 343802) @@ -0,0 +1,18 @@ +// REQUIRES: arm +// RUN: llvm-mc -filetype=obj -triple=thumbv7-windows %s -o %t.obj +// RUN: lld-link -entry:main -subsystem:console %t.obj -out:%t.exe -debug -pdb:%t.pdb -verbose 2>&1 | FileCheck %s --check-prefix=VERBOSE + +// VERBOSE: Added 1 thunks with margin {{.*}} in {{.*}} passes + + .syntax unified + .globl main + .globl func1 + .text +main: + bne func1 + bx lr + .section .text$a, "xr" + .space 0x100000 + .section .text$b, "xr" +func1: + bx lr Index: vendor/lld/dist-release_80/test/COFF/arm64-thunks.s =================================================================== --- vendor/lld/dist-release_80/test/COFF/arm64-thunks.s (nonexistent) +++ vendor/lld/dist-release_80/test/COFF/arm64-thunks.s (revision 343802) @@ -0,0 +1,27 @@ +// REQUIRES: aarch64 +// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %s -o %t.obj +// RUN: lld-link -entry:main -subsystem:console %t.obj -out:%t.exe -verbose 2>&1 | FileCheck -check-prefix=VERBOSE %s +// RUN: llvm-objdump -d %t.exe | FileCheck -check-prefix=DISASM %s + +// VERBOSE: Added 1 thunks with margin {{.*}} in 1 passes + + .globl main + .globl func1 + .text +main: + tbz w0, #0, func1 + ret + .section .text$a, "xr" + .space 0x8000 + .section .text$b, "xr" +func1: + ret + +// DISASM: 0000000140001000 .text: +// DISASM: 140001000: 40 00 00 36 tbz w0, #0, #8 <.text+0x8> +// DISASM: 140001004: c0 03 5f d6 ret +// DISASM: 140001008: 50 00 00 90 adrp x16, #32768 +// DISASM: 14000100c: 10 52 00 91 add x16, x16, #20 +// DISASM: 140001010: 00 02 1f d6 br x16 + +// DISASM: 140009014: c0 03 5f d6 ret Index: vendor/lld/dist-release_80/test/COFF/imports.test =================================================================== --- vendor/lld/dist-release_80/test/COFF/imports.test (revision 343801) +++ vendor/lld/dist-release_80/test/COFF/imports.test (revision 343802) @@ -1,36 +1,49 @@ # REQUIRES: x86 # Verify that the lld can handle .lib files and emit .idata sections. # # RUN: lld-link /out:%t.exe /entry:main /subsystem:console \ # RUN: %p/Inputs/hello64.obj %p/Inputs/std64.lib # RUN: llvm-objdump -d %t.exe | FileCheck -check-prefix=TEXT %s # RUN: llvm-readobj -coff-imports %t.exe | FileCheck -check-prefix=IMPORT %s # RUN: lld-link /out:%t.exe /entry:main /subsystem:console \ # RUN: %p/Inputs/hello64.obj %p/Inputs/std64.lib /include:ExitProcess # RUN: llvm-objdump -d %t.exe | FileCheck -check-prefix=TEXT %s # RUN: llvm-readobj -coff-imports %t.exe | FileCheck -check-prefix=IMPORT %s TEXT: Disassembly of section .text: TEXT-NEXT: .text: TEXT-NEXT: subq $40, %rsp TEXT-NEXT: movq $0, %rcx TEXT-NEXT: leaq 8180(%rip), %rdx TEXT-NEXT: leaq 8167(%rip), %r8 TEXT-NEXT: movl $0, %r9d TEXT-NEXT: callq 60 TEXT-NEXT: movl $0, %ecx TEXT-NEXT: callq 18 TEXT-NEXT: callq 29 TEXT: jmpq *4098(%rip) TEXT: jmpq *4090(%rip) TEXT: jmpq *4082(%rip) IMPORT: Import { IMPORT-NEXT: Name: std64.dll IMPORT-NEXT: ImportLookupTableRVA: 0x2028 IMPORT-NEXT: ImportAddressTableRVA: 0x2048 IMPORT-NEXT: Symbol: ExitProcess (0) IMPORT-NEXT: Symbol: (50) IMPORT-NEXT: Symbol: MessageBoxA (1) IMPORT-NEXT: } + +# RUN: lld-link /out:%t.exe /entry:main /subsystem:console /merge:.rdata=.text \ +# RUN: %p/Inputs/hello64.obj %p/Inputs/std64.lib /include:ExitProcess +# RUN: llvm-readobj -coff-imports %t.exe | FileCheck -check-prefix=MERGE %s + +MERGE: Import { +MERGE-NEXT: Name: std64.dll +MERGE-NEXT: ImportLookupTableRVA: 0x1090 +MERGE-NEXT: ImportAddressTableRVA: 0x10B0 +MERGE-NEXT: Symbol: ExitProcess (0) +MERGE-NEXT: Symbol: (50) +MERGE-NEXT: Symbol: MessageBoxA (1) +MERGE-NEXT: } Index: vendor/lld/dist-release_80/test/ELF/arm-gnu-ifunc.s =================================================================== --- vendor/lld/dist-release_80/test/ELF/arm-gnu-ifunc.s (revision 343801) +++ vendor/lld/dist-release_80/test/ELF/arm-gnu-ifunc.s (revision 343802) @@ -1,139 +1,142 @@ // REQUIRES: arm // RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o // RUN: ld.lld -static %t.o -o %tout // RUN: llvm-objdump -triple armv7a-none-linux-gnueabi -d %tout | FileCheck %s --check-prefix=DISASM // RUN: llvm-readobj -r -symbols -sections %tout | FileCheck %s .syntax unified .text .type foo STT_GNU_IFUNC .globl foo foo: bx lr .type bar STT_GNU_IFUNC .globl bar bar: bx lr .globl _start _start: bl foo bl bar movw r0,:lower16:__rel_iplt_start movt r0,:upper16:__rel_iplt_start movw r0,:lower16:__rel_iplt_end movt r0,:upper16:__rel_iplt_end // CHECK: Sections [ // CHECK: Section { // CHECK: Section { // CHECK: Name: .rel.dyn // CHECK-NEXT: Type: SHT_REL // CHECK-NEXT: Flags [ // CHECK-NEXT: SHF_ALLOC // CHECK-NEXT: ] // CHECK-NEXT: Address: 0x100F4 // CHECK-NEXT: Offset: 0xF4 // CHECK-NEXT: Size: 16 +// CHECK-NEXT: Link: +// CHECK-NEXT: Info: 4 // CHECK: Name: .plt // CHECK-NEXT: Type: SHT_PROGBITS // CHECK-NEXT: Flags [ // CHECK-NEXT: SHF_ALLOC // CHECK-NEXT: SHF_EXECINSTR // CHECK-NEXT: ] // CHECK-NEXT: Address: 0x11020 // CHECK-NEXT: Offset: 0x1020 // CHECK-NEXT: Size: 32 -// CHECK: Name: .got +// CHECK: Index: 4 +// CHECK-NEXT: Name: .got // CHECK-NEXT: Type: SHT_PROGBITS // CHECK-NEXT: Flags [ // CHECK-NEXT: SHF_ALLOC // CHECK-NEXT: SHF_WRITE // CHECK-NEXT: ] // CHECK-NEXT: Address: 0x12000 // CHECK-NEXT: Offset: 0x2000 // CHECK-NEXT: Size: 8 // CHECK: Relocations [ // CHECK-NEXT: Section (1) .rel.dyn { // CHECK-NEXT: 0x12000 R_ARM_IRELATIVE // CHECK-NEXT: 0x12004 R_ARM_IRELATIVE // CHECK-NEXT: } // CHECK-NEXT: ] // CHECK: Symbol { // CHECK: Name: __rel_iplt_end // CHECK-NEXT: Value: 0x10104 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Local // CHECK-NEXT: Type: None // CHECK-NEXT: Other [ // CHECK-NEXT: STV_HIDDEN // CHECK-NEXT: ] // CHECK-NEXT: Section: .rel.dyn // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: __rel_iplt_start // CHECK-NEXT: Value: 0x100F4 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Local // CHECK-NEXT: Type: None // CHECK-NEXT: Other [ // CHECK-NEXT: STV_HIDDEN // CHECK-NEXT: ] // CHECK-NEXT: Section: .rel.dyn // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: _start // CHECK-NEXT: Value: 0x11008 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Global // CHECK-NEXT: Type: None // CHECK-NEXT: Other: // CHECK-NEXT: Section: .text // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: bar // CHECK-NEXT: Value: 0x11004 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Global // CHECK-NEXT: Type: GNU_IFunc // CHECK-NEXT: Other: 0 // CHECK-NEXT: Section: .text // CHECK-NEXT: } // CHECK-NEXT: Symbol { // CHECK-NEXT: Name: foo // CHECK-NEXT: Value: 0x11000 // CHECK-NEXT: Size: 0 // CHECK-NEXT: Binding: Global // CHECK-NEXT: Type: GNU_IFunc // CHECK-NEXT: Other: 0 // CHECK-NEXT: Section: .text // CHECK-NEXT: } // DISASM: Disassembly of section .text: // DISASM-NEXT: foo: // DISASM-NEXT: 11000: 1e ff 2f e1 bx lr // DISASM: bar: // DISASM-NEXT: 11004: 1e ff 2f e1 bx lr // DISASM: _start: // DISASM-NEXT: 11008: 04 00 00 eb bl #16 // DISASM-NEXT: 1100c: 07 00 00 eb bl #28 // 1 * 65536 + 244 = 0x100f4 __rel_iplt_start // DISASM-NEXT: 11010: f4 00 00 e3 movw r0, #244 // DISASM-NEXT: 11014: 01 00 40 e3 movt r0, #1 // 1 * 65536 + 260 = 0x10104 __rel_iplt_end // DISASM-NEXT: 11018: 04 01 00 e3 movw r0, #260 // DISASM-NEXT: 1101c: 01 00 40 e3 movt r0, #1 // DISASM-NEXT: Disassembly of section .plt: // DISASM-NEXT: $a: // DISASM-NEXT: 11020: 00 c6 8f e2 add r12, pc, #0, #12 // DISASM-NEXT: 11024: 00 ca 8c e2 add r12, r12, #0, #20 // DISASM-NEXT: 11028: d8 ff bc e5 ldr pc, [r12, #4056]! // DISASM: $d: // DISASM-NEXT: 1102c: d4 d4 d4 d4 .word 0xd4d4d4d4 // DISASM: $a: // DISASM-NEXT: 11030: 00 c6 8f e2 add r12, pc, #0, #12 // DISASM-NEXT: 11034: 00 ca 8c e2 add r12, r12, #0, #20 // DISASM-NEXT: 11038: cc ff bc e5 ldr pc, [r12, #4044]! // DISASM: $d: // DISASM-NEXT: 1103c: d4 d4 d4 d4 .word 0xd4d4d4d4 Index: vendor/lld/dist-release_80/test/ELF/comdat-linkonce.s =================================================================== --- vendor/lld/dist-release_80/test/ELF/comdat-linkonce.s (revision 343801) +++ vendor/lld/dist-release_80/test/ELF/comdat-linkonce.s (revision 343802) @@ -1,10 +1,15 @@ // REQUIRES: x86 // RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o // RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/comdat.s -o %t2.o // RUN: ld.lld -shared %t.o %t2.o -o %t // RUN: ld.lld -shared %t2.o %t.o -o %t -.section .gnu.linkonce.t.zed +.section .gnu.linkonce.t.__x86.get_pc_thunk.bx .globl abc abc: +nop + +.section .gnu.linkonce.t.__i686.get_pc_thunk.bx +.globl def +def: nop Index: vendor/lld/dist-release_80/test/ELF/emulation-aarch64.s =================================================================== --- vendor/lld/dist-release_80/test/ELF/emulation-aarch64.s (revision 343801) +++ vendor/lld/dist-release_80/test/ELF/emulation-aarch64.s (revision 343802) @@ -1,34 +1,57 @@ # REQUIRES: aarch64 # RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-linux %s -o %taarch64 # RUN: ld.lld -m aarch64linux %taarch64 -o %t2aarch64 # RUN: llvm-readobj -file-headers %t2aarch64 | FileCheck --check-prefix=AARCH64 %s # RUN: ld.lld -m aarch64elf %taarch64 -o %t3aarch64 # RUN: llvm-readobj -file-headers %t3aarch64 | FileCheck --check-prefix=AARCH64 %s # RUN: ld.lld -m aarch64_elf64_le_vec %taarch64 -o %t4aarch64 # RUN: llvm-readobj -file-headers %t4aarch64 | FileCheck --check-prefix=AARCH64 %s # RUN: ld.lld %taarch64 -o %t5aarch64 # RUN: llvm-readobj -file-headers %t5aarch64 | FileCheck --check-prefix=AARCH64 %s # RUN: echo 'OUTPUT_FORMAT(elf64-littleaarch64)' > %t4aarch64.script # RUN: ld.lld %t4aarch64.script %taarch64 -o %t4aarch64 # RUN: llvm-readobj -file-headers %t4aarch64 | FileCheck --check-prefix=AARCH64 %s # AARCH64: ElfHeader { # AARCH64-NEXT: Ident { # AARCH64-NEXT: Magic: (7F 45 4C 46) # AARCH64-NEXT: Class: 64-bit (0x2) # AARCH64-NEXT: DataEncoding: LittleEndian (0x1) # AARCH64-NEXT: FileVersion: 1 # AARCH64-NEXT: OS/ABI: SystemV (0x0) # AARCH64-NEXT: ABIVersion: 0 # AARCH64-NEXT: Unused: (00 00 00 00 00 00 00) # AARCH64-NEXT: } # AARCH64-NEXT: Type: Executable (0x2) # AARCH64-NEXT: Machine: EM_AARCH64 (0xB7) # AARCH64-NEXT: Version: 1 # AARCH64-NEXT: Entry: # AARCH64-NEXT: ProgramHeaderOffset: 0x40 # AARCH64-NEXT: SectionHeaderOffset: # AARCH64-NEXT: Flags [ (0x0) # AARCH64-NEXT: ] +# RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-freebsd %s -o %taarch64fbsd +# RUN: echo 'OUTPUT_FORMAT(elf64-aarch64-freebsd)' > %taarch64fbsd.script +# RUN: ld.lld %taarch64fbsd.script %taarch64fbsd -o %t2aarch64fbsd +# RUN: llvm-readobj -file-headers %t2aarch64fbsd | FileCheck --check-prefix=AARCH64-FBSD %s +# AARCH64-FBSD: ElfHeader { +# AARCH64-FBSD-NEXT: Ident { +# AARCH64-FBSD-NEXT: Magic: (7F 45 4C 46) +# AARCH64-FBSD-NEXT: Class: 64-bit (0x2) +# AARCH64-FBSD-NEXT: DataEncoding: LittleEndian (0x1) +# AARCH64-FBSD-NEXT: FileVersion: 1 +# AARCH64-FBSD-NEXT: OS/ABI: FreeBSD (0x9) +# AARCH64-FBSD-NEXT: ABIVersion: 0 +# AARCH64-FBSD-NEXT: Unused: (00 00 00 00 00 00 00) +# AARCH64-FBSD-NEXT: } +# AARCH64-FBSD-NEXT: Type: Executable (0x2) +# AARCH64-FBSD-NEXT: Machine: EM_AARCH64 (0xB7) +# AARCH64-FBSD-NEXT: Version: 1 +# AARCH64-FBSD-NEXT: Entry: +# AARCH64-FBSD-NEXT: ProgramHeaderOffset: 0x40 +# AARCH64-FBSD-NEXT: SectionHeaderOffset: +# AARCH64-FBSD-NEXT: Flags [ (0x0) +# AARCH64-FBSD-NEXT: ] + .globl _start _start: Index: vendor/lld/dist-release_80/test/ELF/emulation-ppc.s =================================================================== --- vendor/lld/dist-release_80/test/ELF/emulation-ppc.s (revision 343801) +++ vendor/lld/dist-release_80/test/ELF/emulation-ppc.s (revision 343802) @@ -1,75 +1,107 @@ # REQUIRES: ppc # RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %tppc64 # RUN: ld.lld -m elf64ppc %tppc64 -o %t2ppc64 # RUN: llvm-readobj -file-headers %t2ppc64 | FileCheck --check-prefix=PPC64 %s # RUN: ld.lld %tppc64 -o %t3ppc64 # RUN: llvm-readobj -file-headers %t3ppc64 | FileCheck --check-prefix=PPC64 %s # RUN: echo 'OUTPUT_FORMAT(elf64-powerpc)' > %tppc64.script # RUN: ld.lld %tppc64.script %tppc64 -o %t4ppc64 # RUN: llvm-readobj -file-headers %t4ppc64 | FileCheck --check-prefix=PPC64 %s # PPC64: ElfHeader { # PPC64-NEXT: Ident { # PPC64-NEXT: Magic: (7F 45 4C 46) # PPC64-NEXT: Class: 64-bit (0x2) # PPC64-NEXT: DataEncoding: BigEndian (0x2) # PPC64-NEXT: FileVersion: 1 # PPC64-NEXT: OS/ABI: SystemV (0x0) # PPC64-NEXT: ABIVersion: 0 # PPC64-NEXT: Unused: (00 00 00 00 00 00 00) # PPC64-NEXT: } # PPC64-NEXT: Type: Executable (0x2) # PPC64-NEXT: Machine: EM_PPC64 (0x15) # PPC64-NEXT: Version: 1 # PPC64-NEXT: Entry: # PPC64-NEXT: ProgramHeaderOffset: 0x40 # PPC64-NEXT: SectionHeaderOffset: # PPC64-NEXT: Flags [ (0x2) # PPC64-NEXT: 0x2 # PPC64-NEXT: ] # PPC64-NEXT: HeaderSize: 64 # PPC64-NEXT: ProgramHeaderEntrySize: 56 # PPC64-NEXT: ProgramHeaderCount: # PPC64-NEXT: SectionHeaderEntrySize: 64 # PPC64-NEXT: SectionHeaderCount: # PPC64-NEXT: StringTableSectionIndex: # PPC64-NEXT: } +# RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-freebsd %s -o %tppc64fbsd +# RUN: echo 'OUTPUT_FORMAT(elf64-powerpc-freebsd)' > %tppc64fbsd.script +# RUN: ld.lld %tppc64fbsd.script %tppc64fbsd -o %t2ppc64fbsd +# RUN: llvm-readobj -file-headers %t2ppc64fbsd | FileCheck --check-prefix=PPC64-FBSD %s + +# PPC64-FBSD: ElfHeader { +# PPC64-FBSD-NEXT: Ident { +# PPC64-FBSD-NEXT: Magic: (7F 45 4C 46) +# PPC64-FBSD-NEXT: Class: 64-bit (0x2) +# PPC64-FBSD-NEXT: DataEncoding: BigEndian (0x2) +# PPC64-FBSD-NEXT: FileVersion: 1 +# PPC64-FBSD-NEXT: OS/ABI: FreeBSD (0x9) +# PPC64-FBSD-NEXT: ABIVersion: 0 +# PPC64-FBSD-NEXT: Unused: (00 00 00 00 00 00 00) +# PPC64-FBSD-NEXT: } +# PPC64-FBSD-NEXT: Type: Executable (0x2) +# PPC64-FBSD-NEXT: Machine: EM_PPC64 (0x15) +# PPC64-FBSD-NEXT: Version: 1 +# PPC64-FBSD-NEXT: Entry: +# PPC64-FBSD-NEXT: ProgramHeaderOffset: 0x40 +# PPC64-FBSD-NEXT: SectionHeaderOffset: +# PPC64-FBSD-NEXT: Flags [ (0x2) +# PPC64-FBSD-NEXT: 0x2 +# PPC64-FBSD-NEXT: ] +# PPC64-FBSD-NEXT: HeaderSize: 64 +# PPC64-FBSD-NEXT: ProgramHeaderEntrySize: 56 +# PPC64-FBSD-NEXT: ProgramHeaderCount: +# PPC64-FBSD-NEXT: SectionHeaderEntrySize: 64 +# PPC64-FBSD-NEXT: SectionHeaderCount: +# PPC64-FBSD-NEXT: StringTableSectionIndex: +# PPC64-FBSD-NEXT: } + # RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %s -o %tppc64le # RUN: ld.lld -m elf64lppc %tppc64le -o %t2ppc64le # RUN: llvm-readobj -file-headers %t2ppc64le | FileCheck --check-prefix=PPC64LE %s # RUN: ld.lld %tppc64le -o %t3ppc64le # RUN: llvm-readobj -file-headers %t3ppc64le | FileCheck --check-prefix=PPC64LE %s # RUN: echo 'OUTPUT_FORMAT(elf64-powerpcle)' > %tppc64le.script # RUN: ld.lld %tppc64le.script %tppc64le -o %t4ppc64le # RUN: llvm-readobj -file-headers %t4ppc64le | FileCheck --check-prefix=PPC64LE %s # PPC64LE: ElfHeader { # PPC64LE-NEXT: Ident { # PPC64LE-NEXT: Magic: (7F 45 4C 46) # PPC64LE-NEXT: Class: 64-bit (0x2) # PPC64LE-NEXT: DataEncoding: LittleEndian (0x1) # PPC64LE-NEXT: FileVersion: 1 # PPC64LE-NEXT: OS/ABI: SystemV (0x0) # PPC64LE-NEXT: ABIVersion: 0 # PPC64LE-NEXT: Unused: (00 00 00 00 00 00 00) # PPC64LE-NEXT: } # PPC64LE-NEXT: Type: Executable (0x2) # PPC64LE-NEXT: Machine: EM_PPC64 (0x15) # PPC64LE-NEXT: Version: 1 # PPC64LE-NEXT: Entry: # PPC64LE-NEXT: ProgramHeaderOffset: 0x40 # PPC64LE-NEXT: SectionHeaderOffset: # PPC64LE-NEXT: Flags [ (0x2) # PPC64LE-NEXT: 0x2 # PPC64LE-NEXT: ] # PPC64LE-NEXT: HeaderSize: 64 # PPC64LE-NEXT: ProgramHeaderEntrySize: 56 # PPC64LE-NEXT: ProgramHeaderCount: # PPC64LE-NEXT: SectionHeaderEntrySize: 64 # PPC64LE-NEXT: SectionHeaderCount: # PPC64LE-NEXT: StringTableSectionIndex: # PPC64LE-NEXT: } .globl _start _start: Index: vendor/lld/dist-release_80/test/ELF/emulation-x86.s =================================================================== --- vendor/lld/dist-release_80/test/ELF/emulation-x86.s (revision 343801) +++ vendor/lld/dist-release_80/test/ELF/emulation-x86.s (revision 343802) @@ -1,205 +1,211 @@ # REQUIRES: x86 # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-freebsd %s -o %tx64 # RUN: ld.lld -m elf_amd64_fbsd %tx64 -o %t2x64 # RUN: llvm-readobj -file-headers %t2x64 | FileCheck --check-prefix=AMD64 %s # RUN: ld.lld %tx64 -o %t3x64 # RUN: llvm-readobj -file-headers %t3x64 | FileCheck --check-prefix=AMD64 %s # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.sysv # RUN: ld.lld -m elf_amd64_fbsd %t.sysv -o %t.freebsd # RUN: llvm-readobj -file-headers %t.freebsd | FileCheck --check-prefix=AMD64 %s +# RUN: echo 'OUTPUT_FORMAT(elf64-x86-64-freebsd)' > %t4x64.script +# RUN: ld.lld %t4x64.script %tx64 -o %t4x64 +# RUN: llvm-readobj -file-headers %t4x64 | FileCheck --check-prefix=AMD64 %s # AMD64: ElfHeader { # AMD64-NEXT: Ident { # AMD64-NEXT: Magic: (7F 45 4C 46) # AMD64-NEXT: Class: 64-bit (0x2) # AMD64-NEXT: DataEncoding: LittleEndian (0x1) # AMD64-NEXT: FileVersion: 1 # AMD64-NEXT: OS/ABI: FreeBSD (0x9) # AMD64-NEXT: ABIVersion: 0 # AMD64-NEXT: Unused: (00 00 00 00 00 00 00) # AMD64-NEXT: } # AMD64-NEXT: Type: Executable (0x2) # AMD64-NEXT: Machine: EM_X86_64 (0x3E) # AMD64-NEXT: Version: 1 # AMD64-NEXT: Entry: # AMD64-NEXT: ProgramHeaderOffset: 0x40 # AMD64-NEXT: SectionHeaderOffset: # AMD64-NEXT: Flags [ (0x0) # AMD64-NEXT: ] # AMD64-NEXT: HeaderSize: 64 # AMD64-NEXT: ProgramHeaderEntrySize: 56 # AMD64-NEXT: ProgramHeaderCount: # AMD64-NEXT: SectionHeaderEntrySize: 64 # AMD64-NEXT: SectionHeaderCount: # AMD64-NEXT: StringTableSectionIndex: # AMD64-NEXT: } # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %tx64 # RUN: ld.lld -m elf_x86_64 %tx64 -o %t2x64 # RUN: llvm-readobj -file-headers %t2x64 | FileCheck --check-prefix=X86-64 %s # RUN: ld.lld %tx64 -o %t3x64 # RUN: llvm-readobj -file-headers %t3x64 | FileCheck --check-prefix=X86-64 %s # RUN: echo 'OUTPUT_FORMAT(elf64-x86-64)' > %t4x64.script # RUN: ld.lld %t4x64.script %tx64 -o %t4x64 # RUN: ld.lld %tx64 -o %t4x64 %t4x64.script # RUN: llvm-readobj -file-headers %t4x64 | FileCheck --check-prefix=X86-64 %s # X86-64: ElfHeader { # X86-64-NEXT: Ident { # X86-64-NEXT: Magic: (7F 45 4C 46) # X86-64-NEXT: Class: 64-bit (0x2) # X86-64-NEXT: DataEncoding: LittleEndian (0x1) # X86-64-NEXT: FileVersion: 1 # X86-64-NEXT: OS/ABI: SystemV (0x0) # X86-64-NEXT: ABIVersion: 0 # X86-64-NEXT: Unused: (00 00 00 00 00 00 00) # X86-64-NEXT: } # X86-64-NEXT: Type: Executable (0x2) # X86-64-NEXT: Machine: EM_X86_64 (0x3E) # X86-64-NEXT: Version: 1 # X86-64-NEXT: Entry: # X86-64-NEXT: ProgramHeaderOffset: 0x40 # X86-64-NEXT: SectionHeaderOffset: # X86-64-NEXT: Flags [ (0x0) # X86-64-NEXT: ] # X86-64-NEXT: HeaderSize: 64 # X86-64-NEXT: ProgramHeaderEntrySize: 56 # X86-64-NEXT: ProgramHeaderCount: # X86-64-NEXT: SectionHeaderEntrySize: 64 # X86-64-NEXT: SectionHeaderCount: # X86-64-NEXT: StringTableSectionIndex: # X86-64-NEXT: } # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux-gnux32 %s -o %tx32 # RUN: ld.lld -m elf32_x86_64 %tx32 -o %t2x32 # RUN: llvm-readobj -file-headers %t2x32 | FileCheck --check-prefix=X32 %s # RUN: ld.lld %tx32 -o %t3x32 # RUN: llvm-readobj -file-headers %t3x32 | FileCheck --check-prefix=X32 %s # RUN: echo 'OUTPUT_FORMAT(elf32-x86-64)' > %t4x32.script # RUN: ld.lld %t4x32.script %tx32 -o %t4x32 # RUN: llvm-readobj -file-headers %t4x32 | FileCheck --check-prefix=X32 %s # X32: ElfHeader { # X32-NEXT: Ident { # X32-NEXT: Magic: (7F 45 4C 46) # X32-NEXT: Class: 32-bit (0x1) # X32-NEXT: DataEncoding: LittleEndian (0x1) # X32-NEXT: FileVersion: 1 # X32-NEXT: OS/ABI: SystemV (0x0) # X32-NEXT: ABIVersion: 0 # X32-NEXT: Unused: (00 00 00 00 00 00 00) # X32-NEXT: } # X32-NEXT: Type: Executable (0x2) # X32-NEXT: Machine: EM_X86_64 (0x3E) # X32-NEXT: Version: 1 # X32-NEXT: Entry: # X32-NEXT: ProgramHeaderOffset: 0x34 # X32-NEXT: SectionHeaderOffset: # X32-NEXT: Flags [ (0x0) # X32-NEXT: ] # X32-NEXT: HeaderSize: 52 # X32-NEXT: ProgramHeaderEntrySize: 32 # X32-NEXT: ProgramHeaderCount: # X32-NEXT: SectionHeaderEntrySize: 40 # X32-NEXT: SectionHeaderCount: # X32-NEXT: StringTableSectionIndex: # X32-NEXT: } # RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %s -o %tx86 # RUN: ld.lld -m elf_i386 %tx86 -o %t2x86 # RUN: llvm-readobj -file-headers %t2x86 | FileCheck --check-prefix=X86 %s # RUN: ld.lld %tx86 -o %t3x86 # RUN: llvm-readobj -file-headers %t3x86 | FileCheck --check-prefix=X86 %s # RUN: echo 'OUTPUT_FORMAT(elf32-i386)' > %t4x86.script # RUN: ld.lld %t4x86.script %tx86 -o %t4x86 # RUN: llvm-readobj -file-headers %t4x86 | FileCheck --check-prefix=X86 %s # X86: ElfHeader { # X86-NEXT: Ident { # X86-NEXT: Magic: (7F 45 4C 46) # X86-NEXT: Class: 32-bit (0x1) # X86-NEXT: DataEncoding: LittleEndian (0x1) # X86-NEXT: FileVersion: 1 # X86-NEXT: OS/ABI: SystemV (0x0) # X86-NEXT: ABIVersion: 0 # X86-NEXT: Unused: (00 00 00 00 00 00 00) # X86-NEXT: } # X86-NEXT: Type: Executable (0x2) # X86-NEXT: Machine: EM_386 (0x3) # X86-NEXT: Version: 1 # X86-NEXT: Entry: # X86-NEXT: ProgramHeaderOffset: 0x34 # X86-NEXT: SectionHeaderOffset: # X86-NEXT: Flags [ (0x0) # X86-NEXT: ] # X86-NEXT: HeaderSize: 52 # X86-NEXT: ProgramHeaderEntrySize: 32 # X86-NEXT: ProgramHeaderCount: # X86-NEXT: SectionHeaderEntrySize: 40 # X86-NEXT: SectionHeaderCount: # X86-NEXT: StringTableSectionIndex: # X86-NEXT: } # RUN: llvm-mc -filetype=obj -triple=i686-unknown-freebsd %s -o %tx86fbsd -# RUN: ld.lld -m elf_i386_fbsd %tx86fbsd -o %t2x86_fbsd -# RUN: llvm-readobj -file-headers %t2x86_fbsd | FileCheck --check-prefix=X86FBSD %s +# RUN: ld.lld -m elf_i386_fbsd %tx86fbsd -o %t2x86fbsd +# RUN: llvm-readobj -file-headers %t2x86fbsd | FileCheck --check-prefix=X86FBSD %s # RUN: ld.lld %tx86fbsd -o %t3x86fbsd # RUN: llvm-readobj -file-headers %t3x86fbsd | FileCheck --check-prefix=X86FBSD %s +# RUN: echo 'OUTPUT_FORMAT(elf32-i386-freebsd)' > %t4x86fbsd.script +# RUN: ld.lld %t4x86fbsd.script %tx86fbsd -o %t4x86fbsd +# RUN: llvm-readobj -file-headers %t4x86fbsd | FileCheck --check-prefix=X86FBSD %s # X86FBSD: ElfHeader { # X86FBSD-NEXT: Ident { # X86FBSD-NEXT: Magic: (7F 45 4C 46) # X86FBSD-NEXT: Class: 32-bit (0x1) # X86FBSD-NEXT: DataEncoding: LittleEndian (0x1) # X86FBSD-NEXT: FileVersion: 1 # X86FBSD-NEXT: OS/ABI: FreeBSD (0x9) # X86FBSD-NEXT: ABIVersion: 0 # X86FBSD-NEXT: Unused: (00 00 00 00 00 00 00) # X86FBSD-NEXT: } # X86FBSD-NEXT: Type: Executable (0x2) # X86FBSD-NEXT: Machine: EM_386 (0x3) # X86FBSD-NEXT: Version: 1 # X86FBSD-NEXT: Entry: # X86FBSD-NEXT: ProgramHeaderOffset: 0x34 # X86FBSD-NEXT: SectionHeaderOffset: # X86FBSD-NEXT: Flags [ (0x0) # X86FBSD-NEXT: ] # X86FBSD-NEXT: HeaderSize: 52 # X86FBSD-NEXT: ProgramHeaderEntrySize: 32 # X86FBSD-NEXT: ProgramHeaderCount: # X86FBSD-NEXT: SectionHeaderEntrySize: 40 # X86FBSD-NEXT: SectionHeaderCount: # X86FBSD-NEXT: StringTableSectionIndex: # X86FBSD-NEXT: } # RUN: llvm-mc -filetype=obj -triple=i586-intel-elfiamcu %s -o %tiamcu # RUN: ld.lld -m elf_iamcu %tiamcu -o %t2iamcu # RUN: llvm-readobj -file-headers %t2iamcu | FileCheck --check-prefix=IAMCU %s # RUN: ld.lld %tiamcu -o %t3iamcu # RUN: llvm-readobj -file-headers %t3iamcu | FileCheck --check-prefix=IAMCU %s # RUN: echo 'OUTPUT_FORMAT(elf32-iamcu)' > %t4iamcu.script # RUN: ld.lld %t4iamcu.script %tiamcu -o %t4iamcu # RUN: llvm-readobj -file-headers %t4iamcu | FileCheck --check-prefix=IAMCU %s # IAMCU: ElfHeader { # IAMCU-NEXT: Ident { # IAMCU-NEXT: Magic: (7F 45 4C 46) # IAMCU-NEXT: Class: 32-bit (0x1) # IAMCU-NEXT: DataEncoding: LittleEndian (0x1) # IAMCU-NEXT: FileVersion: 1 # IAMCU-NEXT: OS/ABI: SystemV (0x0) # IAMCU-NEXT: ABIVersion: 0 # IAMCU-NEXT: Unused: (00 00 00 00 00 00 00) # IAMCU-NEXT: } # IAMCU-NEXT: Type: Executable (0x2) # IAMCU-NEXT: Machine: EM_IAMCU (0x6) # IAMCU-NEXT: Version: 1 # IAMCU-NEXT: Entry: # IAMCU-NEXT: ProgramHeaderOffset: 0x34 # IAMCU-NEXT: SectionHeaderOffset: # IAMCU-NEXT: Flags [ (0x0) # IAMCU-NEXT: ] # IAMCU-NEXT: HeaderSize: 52 # IAMCU-NEXT: ProgramHeaderEntrySize: 32 # IAMCU-NEXT: ProgramHeaderCount: # IAMCU-NEXT: SectionHeaderEntrySize: 40 # IAMCU-NEXT: SectionHeaderCount: # IAMCU-NEXT: StringTableSectionIndex: # IAMCU-NEXT: } .globl _start _start: Index: vendor/lld/dist-release_80/test/ELF/no-discard-this_module.s =================================================================== --- vendor/lld/dist-release_80/test/ELF/no-discard-this_module.s (nonexistent) +++ vendor/lld/dist-release_80/test/ELF/no-discard-this_module.s (revision 343802) @@ -0,0 +1,41 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=x86_64-linux-gnu -save-temp-labels %s -o %t +// RUN: ld.lld %t -o %t2 +// RUN: llvm-readobj -s -sd -t %t2 | FileCheck %s + +.global _start +_start: + +// This section and symbol is used by Linux kernel modules. Ensure it's not +// accidentally discarded. +.section .gnu.linkonce.this_module: +__this_module: +.byte 0x00 + +// CHECK: Section { +// CHECK: Index: +// CHECK: Name: .gnu.linkonce.this_module +// CHECK-NEXT: Type: SHT_PROGBITS +// CHECK-NEXT: Flags [ +// CHECK-NEXT: ] +// CHECK-NEXT: Address: +// CHECK-NEXT: Offset: +// CHECK-NEXT: Size: +// CHECK-NEXT: Link: +// CHECK-NEXT: Info: +// CHECK-NEXT: AddressAlignment: +// CHECK-NEXT: EntrySize: +// CHECK-NEXT: SectionData ( +// CHECK-NEXT: 0000: 00 |.| +// CHECK-NEXT: ) +// CHECK-NEXT: } + +// CHECK: Symbol { +// CHECK: Name: __this_module +// CHECK-NEXT: Value: +// CHECK-NEXT: Size: +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: +// CHECK-NEXT: Section: .gnu.linkonce.this_module: +// CHECK-NEXT: } Index: vendor/lld/dist-release_80/test/ELF/sht-group-empty.test =================================================================== --- vendor/lld/dist-release_80/test/ELF/sht-group-empty.test (nonexistent) +++ vendor/lld/dist-release_80/test/ELF/sht-group-empty.test (revision 343802) @@ -0,0 +1,55 @@ +# RUN: yaml2obj %s -o %t.o +# RUN: ld.lld %t.o %t.o -o %t -r +# RUN: llvm-readobj -s %t | FileCheck %s + +# CHECK: Name: .text.foo +# CHECK: Name: .rela.text.foo + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + Info: foo + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .text.foo + - SectionOrType: .text.bar + - SectionOrType: .note + - Name: .note + Type: SHT_NOTE + Flags: [ SHF_GROUP ] + - Name: .text.foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + - Name: .text.bar + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + - Name: .rela.text.foo + Type: SHT_RELA + Flags: [ SHF_INFO_LINK, SHF_GROUP ] + Link: .symtab + Info: .text.foo + Relocations: + - Offset: 0x0000000000000000 + Symbol: foo + Type: R_X86_64_64 + - Name: .rela.text.bar + Type: SHT_RELA + Flags: [ SHF_INFO_LINK, SHF_GROUP ] + Link: .symtab + Info: .text.bar + Relocations: + - Offset: 0x0000000000000000 + Symbol: bar + Type: R_X86_64_64 +Symbols: + Global: + - Name: foo + - Name: bar +