From: Rafael Espindola Date: Thu, 21 Jul 2016 20:18:30 +0000 (+0000) Subject: Fix PR28575. X-Git-Tag: llvmorg-4.0.0-rc1~14585 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2deeb6093d65a9cf2adb81e5a60dd4aa105cabf7;p=platform%2Fupstream%2Fllvm.git Fix PR28575. Not all relocations from a .eh_frame that point to an executable section should be ignored. In particular, the relocation finding the personality function should not. This is a reduction from trying to bootstrap a static lld on linux. llvm-svn: 276329 --- diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 57550ee..7ac25bc 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -443,14 +443,53 @@ bool EhInputSection::classof(const InputSectionBase *S) { return S->SectionKind == InputSectionBase::EHFrame; } +// Returns the index of the first relocation that points to a region between +// Begin and Begin+Size. +template +static unsigned getReloc(IntTy Begin, IntTy Size, const ArrayRef &Rels, + unsigned &RelocI) { + // Start search from RelocI for fast access. That works because the + // relocations are sorted in .eh_frame. + for (unsigned N = Rels.size(); RelocI < N; ++RelocI) { + const RelTy &Rel = Rels[RelocI]; + if (Rel.r_offset < Begin) + continue; + + if (Rel.r_offset < Begin + Size) + return RelocI; + return -1; + } + return -1; +} + // .eh_frame is a sequence of CIE or FDE records. // This function splits an input section into records and returns them. template void EhInputSection::split() { + // Early exit if already split. + if (!this->Pieces.empty()) + return; + + if (RelocSection) { + ELFFile &Obj = this->File->getObj(); + if (RelocSection->sh_type == SHT_RELA) + split(Obj.relas(RelocSection)); + else + split(Obj.rels(RelocSection)); + return; + } + split(makeArrayRef(nullptr, nullptr)); +} + +template +template +void EhInputSection::split(ArrayRef Rels) { ArrayRef Data = this->getSectionData(); + unsigned RelI = 0; for (size_t Off = 0, End = Data.size(); Off != End;) { size_t Size = readEhRecordSize(Data.slice(Off)); - this->Pieces.emplace_back(Off, Data.slice(Off, Size)); + this->Pieces.emplace_back(Off, Data.slice(Off, Size), + getReloc(Off, Size, Rels, RelI)); // The empty record is the end marker. if (Size == 4) break; diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h index 69a80e0..d4d2d25 100644 --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -149,6 +149,12 @@ private: llvm::DenseSet LiveOffsets; }; +struct EhSectionPiece : public SectionPiece { + EhSectionPiece(size_t Off, ArrayRef Data, unsigned FirstRelocation) + : SectionPiece(Off, Data), FirstRelocation(FirstRelocation) {} + unsigned FirstRelocation; +}; + // This corresponds to a .eh_frame section of an input file. template class EhInputSection : public InputSectionBase { public: @@ -157,10 +163,11 @@ public: EhInputSection(ObjectFile *F, const Elf_Shdr *Header); static bool classof(const InputSectionBase *S); void split(); + template void split(ArrayRef Rels); // Splittable sections are handled as a sequence of data // rather than a single large blob of data. - std::vector Pieces; + std::vector Pieces; // Relocation section that refer to this one. const Elf_Shdr *RelocSection = nullptr; diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp index 41e30ce..23e4fbf 100644 --- a/lld/ELF/MarkLive.cpp +++ b/lld/ELF/MarkLive.cpp @@ -36,6 +36,7 @@ using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; +using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; @@ -95,19 +96,70 @@ static void forEachSuccessor(InputSection &Sec, run(Obj, Sec, RelSec, Fn); } +// The .eh_frame section is an unfortunate special case. +// The section is divided in CIEs and FDEs and the relocations it can have are +// * CIEs can refer to a personality function. +// * FDEs can refer to a LSDA +// * FDEs refer to the function they contain information about +// The last kind of relocation cannot keep the referred section alive, or they +// would keep everything alive in a common object file. In fact, each FDE is +// alive if the section it refers to is alive. +// To keep things simple, in here we just ignore the last relocation kind. The +// other two keep the referred section alive. +// +// A possible improvement would be to fully process .eh_frame in the middle of +// the gc pass. With that we would be able to also gc some sections holding +// LSDAs and personality functions if we found that they were unused. +template +static void +scanEhFrameSection(EhInputSection &EH, ArrayRef Rels, + std::function)> Enqueue) { + const endianness E = ELFT::TargetEndianness; + for (unsigned I = 0, N = EH.Pieces.size(); I < N; ++I) { + EhSectionPiece &Piece = EH.Pieces[I]; + unsigned FirstRelI = Piece.FirstRelocation; + if (FirstRelI == (unsigned)-1) + continue; + if (read32(Piece.data().data() + 4) == 0) { + // This is a CIE, we only need to worry about the first relocation. It is + // known to point to the personality function. + Enqueue(resolveReloc(EH, Rels[FirstRelI])); + continue; + } + // This is a FDE. The relocations point to the described function or to + // a LSDA. We only need to keep the LSDA alive, so ignore anything that + // points to executable sections. + typename ELFT::uint PieceEnd = Piece.InputOff + Piece.size(); + for (unsigned I2 = FirstRelI, N2 = Rels.size(); I2 < N2; ++I2) { + const RelTy &Rel = Rels[I2]; + if (Rel.r_offset >= PieceEnd) + break; + ResolvedReloc R = resolveReloc(EH, Rels[I2]); + if (!R.Sec || R.Sec == &InputSection::Discarded) + continue; + if (R.Sec->getSectionHdr()->sh_flags & SHF_EXECINSTR) + continue; + Enqueue({R.Sec, 0}); + } + } +} + template -static void scanEhFrameSection(EhInputSection &EH, - std::function)> Fn) { +static void +scanEhFrameSection(EhInputSection &EH, + std::function)> Enqueue) { if (!EH.RelocSection) return; + + // Unfortunately we need to split .eh_frame early since some relocations in + // .eh_frame keep other section alive and some don't. + EH.split(); + ELFFile &EObj = EH.getFile()->getObj(); - run(EObj, EH, EH.RelocSection, [&](ResolvedReloc R) { - if (!R.Sec || R.Sec == &InputSection::Discarded) - return; - if (R.Sec->getSectionHdr()->sh_flags & SHF_EXECINSTR) - return; - Fn({R.Sec, 0}); - }); + if (EH.RelocSection->sh_type == SHT_RELA) + scanEhFrameSection(EH, EObj.relas(EH.RelocSection), Enqueue); + else + scanEhFrameSection(EH, EObj.rels(EH.RelocSection), Enqueue); } // Sections listed below are special because they are used by the loader diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index 7ff5ba9..34e5685 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -963,41 +963,22 @@ template EhOutputSection::EhOutputSection() : OutputSectionBase(".eh_frame", SHT_PROGBITS, SHF_ALLOC) {} -// Returns the first relocation that points to a region -// between Begin and Begin+Size. -template -static const RelTy *getReloc(IntTy Begin, IntTy Size, ArrayRef &Rels) { - for (auto I = Rels.begin(), E = Rels.end(); I != E; ++I) { - if (I->r_offset < Begin) - continue; - - // Truncate Rels for fast access. That means we expect that the - // relocations are sorted and we are looking up symbols in - // sequential order. It is naturally satisfied for .eh_frame. - Rels = Rels.slice(I - Rels.begin()); - if (I->r_offset < Begin + Size) - return I; - return nullptr; - } - Rels = ArrayRef(); - return nullptr; -} - // 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 template -CieRecord *EhOutputSection::addCie(SectionPiece &Piece, +CieRecord *EhOutputSection::addCie(EhSectionPiece &Piece, EhInputSection *Sec, - ArrayRef &Rels) { + ArrayRef Rels) { const endianness E = ELFT::TargetEndianness; if (read32(Piece.data().data() + 4) != 0) fatal("CIE expected at beginning of .eh_frame: " + Sec->getSectionName()); SymbolBody *Personality = nullptr; - if (const RelTy *Rel = getReloc(Piece.InputOff, Piece.size(), Rels)) - Personality = &Sec->getFile()->getRelocTargetSym(*Rel); + unsigned FirstRelI = Piece.FirstRelocation; + if (FirstRelI != (unsigned)-1) + Personality = &Sec->getFile()->getRelocTargetSym(Rels[FirstRelI]); // Search for an existing CIE by CIE contents/relocation target pair. CieRecord *Cie = &CieMap[{Piece.data(), Personality}]; @@ -1014,13 +995,14 @@ CieRecord *EhOutputSection::addCie(SectionPiece &Piece, // points to a live function. template template -bool EhOutputSection::isFdeLive(SectionPiece &Piece, +bool EhOutputSection::isFdeLive(EhSectionPiece &Piece, EhInputSection *Sec, - ArrayRef &Rels) { - const RelTy *Rel = getReloc(Piece.InputOff, Piece.size(), Rels); - if (!Rel) + ArrayRef Rels) { + unsigned FirstRelI = Piece.FirstRelocation; + if (FirstRelI == (unsigned)-1) fatal("FDE doesn't reference another section"); - SymbolBody &B = Sec->getFile()->getRelocTargetSym(*Rel); + const RelTy &Rel = Rels[FirstRelI]; + SymbolBody &B = Sec->getFile()->getRelocTargetSym(Rel); auto *D = dyn_cast>(&B); if (!D || !D->Section) return false; @@ -1039,7 +1021,7 @@ void EhOutputSection::addSectionAux(EhInputSection *Sec, const endianness E = ELFT::TargetEndianness; DenseMap OffsetToCie; - for (SectionPiece &Piece : Sec->Pieces) { + for (EhSectionPiece &Piece : Sec->Pieces) { // The empty record is the end marker. if (Piece.size() == 4) return; diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h index f0a7196..bf6f208 100644 --- a/lld/ELF/OutputSections.h +++ b/lld/ELF/OutputSections.h @@ -24,7 +24,7 @@ namespace lld { namespace elf { class SymbolBody; -struct SectionPiece; +struct EhSectionPiece; template class SymbolTable; template class SymbolTableSection; template class StringTableSection; @@ -372,8 +372,8 @@ private: }; struct CieRecord { - SectionPiece *Piece = nullptr; - std::vector FdePieces; + EhSectionPiece *Piece = nullptr; + std::vector FdePieces; }; // Output section for .eh_frame. @@ -399,12 +399,12 @@ private: void addSectionAux(EhInputSection *S, llvm::ArrayRef Rels); template - CieRecord *addCie(SectionPiece &Piece, EhInputSection *Sec, - ArrayRef &Rels); + CieRecord *addCie(EhSectionPiece &Piece, EhInputSection *Sec, + ArrayRef Rels); template - bool isFdeLive(SectionPiece &Piece, EhInputSection *Sec, - ArrayRef &Rels); + bool isFdeLive(EhSectionPiece &Piece, EhInputSection *Sec, + ArrayRef Rels); uintX_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc); diff --git a/lld/test/ELF/eh-frame-gc2.s b/lld/test/ELF/eh-frame-gc2.s new file mode 100644 index 0000000..9cf0d08 --- /dev/null +++ b/lld/test/ELF/eh-frame-gc2.s @@ -0,0 +1,15 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o +// RUN: ld.lld --gc-sections %t.o -o %t +// RUN: llvm-readobj -s %t | FileCheck %s + +// Test that the we don't gc the personality function. +// CHECK: Name: .foobar + + .globl _start +_start: + .cfi_startproc + .cfi_personality 3, foobar + .cfi_endproc + .section .foobar,"ax" +foobar: