Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -1099,8 +1099,6 @@ if (Config->GcSections) markLive(); decompressAndMergeSections(); - if (Config->ICF) - doIcf(); // Write the result to the file. writeResult(); 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 "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 @@ -273,6 +273,9 @@ InputSectionBase *Sec; uint32_t Size; unsigned FirstRelocation; + + // Keeps section where function accociated with FDE resides. + InputSection* FdeTargetSec = nullptr; }; // This corresponds to a .eh_frame section of an input file. @@ -324,6 +327,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 @@ -93,9 +93,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; Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -435,12 +435,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; @@ -450,14 +448,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 @@ -488,10 +491,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++; } } @@ -543,8 +547,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; } } @@ -597,6 +604,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()); @@ -616,6 +626,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); 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" @@ -156,6 +157,9 @@ if (!Config->Relocatable) combineEhFrameSections(); + if (Config->ICF) + doIcf(); + // We need to create some reserved symbols such as _end. Create them. if (!Config->Relocatable) addReservedSymbols(); 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