Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -1101,6 +1101,11 @@ markLive(); decompressSections(); mergeSections(); + // Create linker-synthesized sections such as .got or .plt. + // Such sections are of type input section. + createSyntheticSections(); + if (!Config->Relocatable) + combineEhFrameSections(); if (Config->ICF) doIcf(); Index: ELF/EhFrame.h =================================================================== --- ELF/EhFrame.h +++ ELF/EhFrame.h @@ -19,6 +19,8 @@ template size_t readEhRecordSize(InputSectionBase *S, size_t Off); template uint8_t getFdeEncoding(EhSectionPiece *P); +bool equalsFdes(ArrayRef A, ArrayRef B); + } // namespace elf } // namespace lld Index: ELF/EhFrame.cpp =================================================================== --- ELF/EhFrame.cpp +++ ELF/EhFrame.cpp @@ -64,6 +64,44 @@ return EhReader(S, S->Data.slice(Off)).readEhRecordSize(); } +static bool equalsFde(EhSectionPiece *A, EhSectionPiece *B) { + // We want to compare FDEs contents, for that we want to ignore + // CIE pointer field which used to get offset of the start of the + // associated CIE and therefore is fickle. We also ignore length + // field which can differ because of padding. + ArrayRef DataA = A->data().drop_front(4 + 4); + ArrayRef DataB = B->data().drop_front(4 + 4); + if (DataA.size() == DataB.size()) + return DataA == DataB; + + // FDEs may contain padding at the end which should not affect comparsion. + size_t AlignedSizeA = alignTo(DataA.size(), Config->Wordsize); + size_t AlignedSizeB = alignTo(DataB.size(), Config->Wordsize); + if (AlignedSizeA != AlignedSizeB) + return false; + + if (DataA.size() > DataB.size()) + std::swap(DataA, DataB); + + // Check data before possible padding bytes. + if (memcmp(DataA.data(), DataB.data(), DataA.size())) + return false; + + // If tail is filled with zeroes, it is padding. + return llvm::all_of(DataB.drop_front(DataA.size()), + [](uint8_t C) { return C == 0; }); +} + +bool elf::equalsFdes(ArrayRef A, + ArrayRef B) { + if (A.size() != B.size()) + return false; + for (size_t I = 0, E = A.size(); I != E; ++I) + if (!equalsFde(A[I], B[I])) + return false; + return true; +} + // .eh_frame section is a sequence of records. Each record starts with // a 4 byte length field. This function reads the length. template size_t EhReader::readEhRecordSize() { Index: ELF/ICF.cpp =================================================================== --- ELF/ICF.cpp +++ ELF/ICF.cpp @@ -75,7 +75,9 @@ #include "ICF.h" #include "Config.h" +#include "EhFrame.h" #include "SymbolTable.h" +#include "SyntheticSections.h" #include "lld/Common/Threads.h" #include "llvm/ADT/Hashing.h" #include "llvm/BinaryFormat/ELF.h" @@ -163,8 +165,9 @@ // .init and .fini contains instructions that must be executed to // initialize and finalize the process. They cannot and should not // be merged. - return S->Live && (S->Flags & SHF_ALLOC) && (S->Flags & SHF_EXECINSTR) && - !(S->Flags & SHF_WRITE) && S->Name != ".init" && S->Name != ".fini"; + return S->Live && !isa(S) && (S->Flags & SHF_ALLOC) && + (S->Flags & SHF_EXECINSTR) && !(S->Flags & SHF_WRITE) && + S->Name != ".init" && S->Name != ".fini"; } // Split an equivalence class into smaller classes. @@ -279,6 +282,9 @@ A->getSize() != B->getSize() || A->Data != B->Data) return false; + if (!equalsFdes(A->Fdes, B->Fdes)) + return false; + if (A->AreRelocsRela) return constantEq(A, A->template relas(), B, B->template relas()); Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -284,6 +284,9 @@ InputSectionBase *Sec; uint32_t Size; unsigned FirstRelocation; + + // Keeps section where function associated with FDE resides. + InputSection* FdeTargetSec = nullptr; }; // This corresponds to a .eh_frame section of an input file. @@ -335,6 +338,8 @@ // Used by ICF. uint32_t Class[2] = {0, 0}; + std::vector Fdes; + // Called by ICF to merge two input sections. void replace(InputSection *Other); Index: ELF/SyntheticSections.h =================================================================== --- ELF/SyntheticSections.h +++ ELF/SyntheticSections.h @@ -88,9 +88,6 @@ template CieRecord *addCie(EhSectionPiece &Piece, ArrayRef Rels); - template - bool isFdeLive(EhSectionPiece &Piece, ArrayRef Rels); - uint64_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc); std::vector CieRecords; @@ -807,6 +804,8 @@ template MergeInputSection *createCommentSection(); void decompressSections(); void mergeSections(); +template void combineEhFrameSections(); +template void createSyntheticSections(); SymbolBody *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value, uint64_t Size, InputSectionBase *Section); Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -80,6 +80,166 @@ } } +template void elf::combineEhFrameSections() { + for (InputSectionBase *&S : InputSections) { + EhInputSection *ES = dyn_cast(S); + if (!ES || !ES->Live) + continue; + + In::EhFrame->addSection(ES); + S = nullptr; + } + + std::vector &V = InputSections; + V.erase(std::remove(V.begin(), V.end(), nullptr), V.end()); +} + +static bool needsInterpSection() { + return !SharedFiles.empty() && !Config->DynamicLinker.empty() && + Script->needsInterpSection(); +} + +// Initialize Out members. +template void elf::createSyntheticSections() { + using Elf_Ehdr = typename ELFFile::Elf_Ehdr; + + // Initialize all pointers with NULL. This is needed because + // you can call lld::elf::main more than once as a library. + memset(&Out::First, 0, sizeof(Out)); + + auto Add = [](InputSectionBase *Sec) { InputSections.push_back(Sec); }; + + InX::DynStrTab = make(".dynstr", true); + InX::Dynamic = make>(); + In::RelaDyn = make>( + Config->IsRela ? ".rela.dyn" : ".rel.dyn", Config->ZCombreloc); + InX::ShStrTab = make(".shstrtab", false); + + Out::ElfHeader = make("", 0, SHF_ALLOC); + Out::ElfHeader->Size = sizeof(Elf_Ehdr); + Out::ProgramHeaders = make("", 0, SHF_ALLOC); + Out::ProgramHeaders->Alignment = Config->Wordsize; + + if (needsInterpSection()) { + InX::Interp = createInterpSection(); + Add(InX::Interp); + } else { + InX::Interp = nullptr; + } + + if (Config->Strip != StripPolicy::All) { + InX::StrTab = make(".strtab", false); + InX::SymTab = make>(*InX::StrTab); + } + + if (Config->BuildId != BuildIdKind::None) { + InX::BuildId = make(); + Add(InX::BuildId); + } + + InX::Bss = make(".bss", 0, 1); + Add(InX::Bss); + InX::BssRelRo = make(".bss.rel.ro", 0, 1); + Add(InX::BssRelRo); + + // Add MIPS-specific sections. + if (Config->EMachine == EM_MIPS) { + if (!Config->Shared && Config->HasDynSymTab) { + InX::MipsRldMap = make(); + Add(InX::MipsRldMap); + } + if (auto *Sec = MipsAbiFlagsSection::create()) + Add(Sec); + if (auto *Sec = MipsOptionsSection::create()) + Add(Sec); + if (auto *Sec = MipsReginfoSection::create()) + Add(Sec); + } + + if (Config->HasDynSymTab) { + InX::DynSymTab = make>(*InX::DynStrTab); + Add(InX::DynSymTab); + + In::VerSym = make>(); + Add(In::VerSym); + + if (!Config->VersionDefinitions.empty()) { + In::VerDef = make>(); + Add(In::VerDef); + } + + In::VerNeed = make>(); + Add(In::VerNeed); + + if (Config->GnuHash) { + InX::GnuHashTab = make(); + Add(InX::GnuHashTab); + } + + if (Config->SysvHash) { + InX::HashTab = make(); + Add(InX::HashTab); + } + + Add(InX::Dynamic); + Add(InX::DynStrTab); + Add(In::RelaDyn); + } + + // Add .got. MIPS' .got is so different from the other archs, + // it has its own class. + if (Config->EMachine == EM_MIPS) { + InX::MipsGot = make(); + Add(InX::MipsGot); + } else { + InX::Got = make(); + Add(InX::Got); + } + + InX::GotPlt = make(); + Add(InX::GotPlt); + InX::IgotPlt = make(); + Add(InX::IgotPlt); + + if (Config->GdbIndex) { + InX::GdbIndex = createGdbIndex(); + Add(InX::GdbIndex); + } + + // We always need to add rel[a].plt to output if it has entries. + // Even for static linking it can contain R_[*]_IRELATIVE relocations. + In::RelaPlt = make>( + Config->IsRela ? ".rela.plt" : ".rel.plt", false /*Sort*/); + Add(In::RelaPlt); + + // The RelaIplt immediately follows .rel.plt (.rel.dyn for ARM) to ensure + // that the IRelative relocations are processed last by the dynamic loader + In::RelaIplt = make>( + (Config->EMachine == EM_ARM) ? ".rel.dyn" : In::RelaPlt->Name, + false /*Sort*/); + Add(In::RelaIplt); + + InX::Plt = make(Target->PltHeaderSize); + Add(InX::Plt); + InX::Iplt = make(0); + Add(InX::Iplt); + + if (!Config->Relocatable) { + if (Config->EhFrameHdr) { + In::EhFrameHdr = make>(); + Add(In::EhFrameHdr); + } + In::EhFrame = make>(); + Add(In::EhFrame); + } + + if (InX::SymTab) + Add(InX::SymTab); + Add(InX::ShStrTab); + if (InX::StrTab) + Add(InX::StrTab); +} + // Returns an LLD version string. static ArrayRef getVersion() { // Check LLD_VERSION first for ease of testing. @@ -430,12 +590,10 @@ return Rec; } -// There is one FDE per function. Returns true if a given FDE -// points to a live function. -template -template -bool EhFrameSection::isFdeLive(EhSectionPiece &Fde, - ArrayRef Rels) { +// There is one FDE per function. Returns section where function lives. +template +static InputSection *getFdeTargetSection(EhSectionPiece &Fde, + ArrayRef Rels) { auto *Sec = cast(Fde.Sec); unsigned FirstRelI = Fde.FirstRelocation; @@ -445,14 +603,19 @@ // corresponding FDEs, which results in creating bad .eh_frame sections. // To deal with that, we ignore such FDEs. if (FirstRelI == (unsigned)-1) - return false; + return nullptr; const RelTy &Rel = Rels[FirstRelI]; SymbolBody &B = Sec->template getFile()->getRelocTargetSym(Rel); - if (auto *D = dyn_cast(&B)) - if (D->Section) - return cast(D->Section)->Repl->Live; - return false; + if (DefinedRegular *DR = dyn_cast(&B)) + return cast_or_null(DR->Section); + return nullptr; +} + +// There is one FDE per function. Returns true if a given FDE +// points to a live function. +static bool isFdeLive(EhSectionPiece &Fde) { + return Fde.FdeTargetSec && Fde.FdeTargetSec->Repl->Live; } // .eh_frame is a sequence of CIE or FDE records. In general, there @@ -483,10 +646,11 @@ if (!Rec) fatal(toString(Sec) + ": invalid CIE reference"); - if (!isFdeLive(Piece, Rels)) - continue; + Piece.FdeTargetSec = getFdeTargetSection(Piece, Rels); + if (Piece.FdeTargetSec) + Piece.FdeTargetSec->Fdes.push_back(&Piece); + Rec->Fdes.push_back(&Piece); - NumFdes++; } } @@ -540,8 +704,11 @@ Off += alignTo(Rec->Cie->Size, Config->Wordsize); for (EhSectionPiece *Fde : Rec->Fdes) { + if (!isFdeLive(*Fde)) + continue; Fde->OutputOff = Off; Off += alignTo(Fde->Size, Config->Wordsize); + ++NumFdes; } } @@ -596,6 +763,9 @@ writeCieFde(Buf + CieOffset, Rec->Cie->data()); for (EhSectionPiece *Fde : Rec->Fdes) { + if (!isFdeLive(*Fde)) + continue; + size_t Off = Fde->OutputOff; writeCieFde(Buf + Off, Fde->data()); @@ -618,6 +788,8 @@ for (CieRecord *Rec : CieRecords) { uint8_t Enc = getFdeEncoding(Rec->Cie); for (EhSectionPiece *Fde : Rec->Fdes) { + if (!isFdeLive(*Fde)) + continue; uint64_t Pc = getFdePc(Buf, Fde->OutputOff, Enc); uint64_t FdeVA = getParent()->Addr + Fde->OutputOff; In::EhFrameHdr->addFde(Pc, FdeVA); @@ -2463,6 +2635,16 @@ template void elf::createCommonSections(); template void elf::createCommonSections(); +template void elf::combineEhFrameSections(); +template void elf::combineEhFrameSections(); +template void elf::combineEhFrameSections(); +template void elf::combineEhFrameSections(); + +template void elf::createSyntheticSections(); +template void elf::createSyntheticSections(); +template void elf::createSyntheticSections(); +template void elf::createSyntheticSections(); + template MergeInputSection *elf::createCommentSection(); template MergeInputSection *elf::createCommentSection(); template MergeInputSection *elf::createCommentSection(); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -10,6 +10,7 @@ #include "Writer.h" #include "Config.h" #include "Filesystem.h" +#include "ICF.h" #include "LinkerScript.h" #include "MapFile.h" #include "Memory.h" @@ -45,7 +46,6 @@ void run(); private: - void createSyntheticSections(); void copyLocalSymbols(); void addSectionSymbols(); void addReservedSymbols(); @@ -115,11 +115,6 @@ return Name; } -static bool needsInterpSection() { - return !SharedFiles.empty() && !Config->DynamicLinker.empty() && - Script->needsInterpSection(); -} - template void elf::writeResult() { Writer().run(); } template void Writer::removeEmptyPTLoad() { @@ -133,29 +128,8 @@ }); } -template static void combineEhFrameSections() { - for (InputSectionBase *&S : InputSections) { - EhInputSection *ES = dyn_cast(S); - if (!ES || !ES->Live) - continue; - - In::EhFrame->addSection(ES); - S = nullptr; - } - - std::vector &V = InputSections; - V.erase(std::remove(V.begin(), V.end(), nullptr), V.end()); -} - // The main function of the writer. template void Writer::run() { - // Create linker-synthesized sections such as .got or .plt. - // Such sections are of type input section. - createSyntheticSections(); - - if (!Config->Relocatable) - combineEhFrameSections(); - // We need to create some reserved symbols such as _end. Create them. if (!Config->Relocatable) addReservedSymbols(); @@ -248,145 +222,6 @@ error("failed to write to the output file: " + EC.message()); } -// Initialize Out members. -template void Writer::createSyntheticSections() { - // Initialize all pointers with NULL. This is needed because - // you can call lld::elf::main more than once as a library. - memset(&Out::First, 0, sizeof(Out)); - - auto Add = [](InputSectionBase *Sec) { InputSections.push_back(Sec); }; - - InX::DynStrTab = make(".dynstr", true); - InX::Dynamic = make>(); - In::RelaDyn = make>( - Config->IsRela ? ".rela.dyn" : ".rel.dyn", Config->ZCombreloc); - InX::ShStrTab = make(".shstrtab", false); - - Out::ElfHeader = make("", 0, SHF_ALLOC); - Out::ElfHeader->Size = sizeof(Elf_Ehdr); - Out::ProgramHeaders = make("", 0, SHF_ALLOC); - Out::ProgramHeaders->Alignment = Config->Wordsize; - - if (needsInterpSection()) { - InX::Interp = createInterpSection(); - Add(InX::Interp); - } else { - InX::Interp = nullptr; - } - - if (Config->Strip != StripPolicy::All) { - InX::StrTab = make(".strtab", false); - InX::SymTab = make>(*InX::StrTab); - } - - if (Config->BuildId != BuildIdKind::None) { - InX::BuildId = make(); - Add(InX::BuildId); - } - - InX::Bss = make(".bss", 0, 1); - Add(InX::Bss); - InX::BssRelRo = make(".bss.rel.ro", 0, 1); - Add(InX::BssRelRo); - - // Add MIPS-specific sections. - if (Config->EMachine == EM_MIPS) { - if (!Config->Shared && Config->HasDynSymTab) { - InX::MipsRldMap = make(); - Add(InX::MipsRldMap); - } - if (auto *Sec = MipsAbiFlagsSection::create()) - Add(Sec); - if (auto *Sec = MipsOptionsSection::create()) - Add(Sec); - if (auto *Sec = MipsReginfoSection::create()) - Add(Sec); - } - - if (Config->HasDynSymTab) { - InX::DynSymTab = make>(*InX::DynStrTab); - Add(InX::DynSymTab); - - In::VerSym = make>(); - Add(In::VerSym); - - if (!Config->VersionDefinitions.empty()) { - In::VerDef = make>(); - Add(In::VerDef); - } - - In::VerNeed = make>(); - Add(In::VerNeed); - - if (Config->GnuHash) { - InX::GnuHashTab = make(); - Add(InX::GnuHashTab); - } - - if (Config->SysvHash) { - InX::HashTab = make(); - Add(InX::HashTab); - } - - Add(InX::Dynamic); - Add(InX::DynStrTab); - Add(In::RelaDyn); - } - - // Add .got. MIPS' .got is so different from the other archs, - // it has its own class. - if (Config->EMachine == EM_MIPS) { - InX::MipsGot = make(); - Add(InX::MipsGot); - } else { - InX::Got = make(); - Add(InX::Got); - } - - InX::GotPlt = make(); - Add(InX::GotPlt); - InX::IgotPlt = make(); - Add(InX::IgotPlt); - - if (Config->GdbIndex) { - InX::GdbIndex = createGdbIndex(); - Add(InX::GdbIndex); - } - - // We always need to add rel[a].plt to output if it has entries. - // Even for static linking it can contain R_[*]_IRELATIVE relocations. - In::RelaPlt = make>( - Config->IsRela ? ".rela.plt" : ".rel.plt", false /*Sort*/); - Add(In::RelaPlt); - - // The RelaIplt immediately follows .rel.plt (.rel.dyn for ARM) to ensure - // that the IRelative relocations are processed last by the dynamic loader - In::RelaIplt = make>( - (Config->EMachine == EM_ARM) ? ".rel.dyn" : In::RelaPlt->Name, - false /*Sort*/); - Add(In::RelaIplt); - - InX::Plt = make(Target->PltHeaderSize); - Add(InX::Plt); - InX::Iplt = make(0); - Add(InX::Iplt); - - if (!Config->Relocatable) { - if (Config->EhFrameHdr) { - In::EhFrameHdr = make>(); - Add(In::EhFrameHdr); - } - In::EhFrame = make>(); - Add(In::EhFrame); - } - - if (InX::SymTab) - Add(InX::SymTab); - Add(InX::ShStrTab); - if (InX::StrTab) - Add(InX::StrTab); -} - static bool shouldKeepInSymtab(SectionBase *Sec, StringRef SymName, const SymbolBody &B) { if (B.isFile() || B.isSection()) Index: test/ELF/eh-frame-hdr-icf-fde1.s =================================================================== --- test/ELF/eh-frame-hdr-icf-fde1.s +++ test/ELF/eh-frame-hdr-icf-fde1.s @@ -0,0 +1,34 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t2 --icf=all --eh-frame-hdr --verbose | FileCheck %s + +## Check we have 2 equal FDEs with different size +## because of padding in input object. +# RUN: llvm-objdump %t --dwarf=frames | FileCheck %s --check-prefix=OBJ +# OBJ: .debug_frame contents: +# OBJ: {{.*}} 00000010 {{.*}} FDE +# OBJ: {{.*}} 00000014 {{.*}} FDE + +## Check ICF merges sections with the same FDE data even when +## size is different because of padding bytes. +# CHECK: selected .text.f1 +# CHECK-NEXT: removed .text.f2 + +.global personality +.hidden personality +personality: + nop + +.section .text.f1, "ax" +.cfi_startproc +.cfi_personality 0x1b, personality +nop +.cfi_endproc + +.section .text.f2, "ax" +.cfi_startproc +.cfi_personality 0x1b, personality +nop +.cfi_endproc + Index: test/ELF/eh-frame-hdr-icf-fde2.s =================================================================== --- test/ELF/eh-frame-hdr-icf-fde2.s +++ test/ELF/eh-frame-hdr-icf-fde2.s @@ -0,0 +1,24 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t2 --icf=all --eh-frame-hdr --verbose | FileCheck %s + +## Check ICF does not merge sections with different FDE data. +# CHECK-NOT: selected + +## Check we have 2 different FDEs in output. +# RUN: llvm-objdump %t2 --dwarf=frames | FileCheck %s --check-prefix=FRAMES +# FRAMES: .eh_frame contents: +# FRAMES: 00000018 00000014 0000001c FDE cie=0000001c pc=00000e68...00000e69 +# FRAMES: 00000030 00000014 00000034 FDE cie=00000034 pc=00000e51...00000e52 + +.section .text.f1, "ax" +.cfi_startproc +nop +.cfi_endproc + +.section .text.f2, "ax" +.cfi_startproc +.cfi_adjust_cfa_offset 8 +nop +.cfi_endproc Index: test/ELF/eh-frame-hdr-icf-fde3.s =================================================================== --- test/ELF/eh-frame-hdr-icf-fde3.s +++ test/ELF/eh-frame-hdr-icf-fde3.s @@ -0,0 +1,7 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s + +## Check we do not apply ICF for synthetic sections. (empty .plt and .text in this case) +# CHECK-NOT: selected Index: test/ELF/eh-frame-hdr-icf-fde4.s =================================================================== --- test/ELF/eh-frame-hdr-icf-fde4.s +++ test/ELF/eh-frame-hdr-icf-fde4.s @@ -0,0 +1,50 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s + +## In this test each section has 2 FDEs attached. +## .text.f1 and .text.f3 has equal FDEs and second FDE of.text.f2 section +## is different. Check we merge .text.f1 and .text.f3 together. + +# CHECK-NOT: .text.f2 +# CHECK: selected .text.f1 +# CHECK-NEXT: removed .text.f3 +# CHECK-NOT: .text.f2 + +.section .text.f1, "ax" +.cfi_startproc +nop +.cfi_endproc +nop +nop +.cfi_startproc +nop +nop +nop +.cfi_endproc + +.section .text.f2, "ax" +.cfi_startproc +nop +.cfi_endproc +nop +nop +.cfi_startproc +.cfi_adjust_cfa_offset 8 +nop +nop +nop +.cfi_endproc + +.section .text.f3, "ax" +.cfi_startproc +nop +.cfi_endproc +nop +nop +.cfi_startproc +nop +nop +nop +.cfi_endproc Index: test/ELF/eh-frame-hdr-icf.s =================================================================== --- test/ELF/eh-frame-hdr-icf.s +++ test/ELF/eh-frame-hdr-icf.s @@ -5,7 +5,7 @@ # RUN: llvm-objdump -s %t2 | FileCheck %s # CHECK: Contents of section .eh_frame_hdr: -# CHECK-NEXT: 200158 011b033b 1c000000 01000000 a80e0000 +# CHECK-NEXT: 200158 011b033b 1c000000 01000000 a90e0000 # ^ FDE count # CHECK-NEXT: 200168 38000000 00000000 00000000 # ^ FDE for f2