[lld] r270382 - Refactor EHOutputSection.
Rui Ueyama via llvm-commits
llvm-commits at lists.llvm.org
Sun May 22 16:16:14 PDT 2016
Author: ruiu
Date: Sun May 22 18:16:14 2016
New Revision: 270382
URL: http://llvm.org/viewvc/llvm-project?rev=270382&view=rev
Log:
Refactor EHOutputSection.
This patch refactors EHOutputSection using SectionPiece struct.
EHRegion class was removed since we can now directly use SectionPiece.
An incomplete support of large CIE/FDE record (> 2^32 bytes) was removed
because it silently created broken executable. There are several places
in the existing code that "size" field is always 4 bytes and at offset 4
in the record, which is not true for 64-bit size records. We will have to
support that in future, but it is better to error out instead of creating
malformed eh_frame sections.
Removed:
lld/trunk/test/ELF/valid-cie-length-dw64.s
Modified:
lld/trunk/ELF/OutputSections.cpp
lld/trunk/ELF/OutputSections.h
lld/trunk/test/ELF/invalid-cie-length4.s
lld/trunk/test/ELF/invalid-cie-length5.s
Modified: lld/trunk/ELF/OutputSections.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/OutputSections.cpp?rev=270382&r1=270381&r2=270382&view=diff
==============================================================================
--- lld/trunk/ELF/OutputSections.cpp (original)
+++ lld/trunk/ELF/OutputSections.cpp Sun May 22 18:16:14 2016
@@ -972,18 +972,6 @@ void EHOutputSection<ELFT>::forEachInput
F(S);
}
-template <class ELFT>
-EHRegion<ELFT>::EHRegion(EHInputSection<ELFT> *Sec, unsigned Index)
- : Sec(Sec), Index(Index) {}
-
-template <class ELFT> ArrayRef<uint8_t> EHRegion<ELFT>::data() const {
- return Sec->Pieces[Index].Data;
-}
-
-template <class ELFT>
-CieRecord<ELFT>::CieRecord(EHInputSection<ELFT> *Sec, unsigned Index)
- : EHRegion<ELFT>(Sec, Index) {}
-
// Read a byte and advance D by one byte.
static uint8_t readByte(ArrayRef<uint8_t> &D) {
if (D.empty())
@@ -1084,29 +1072,21 @@ uint8_t EHOutputSection<ELFT>::getFdeEnc
return DW_EH_PE_absptr;
}
-template <class ELFT>
-static typename ELFT::uint readEntryLength(ArrayRef<uint8_t> D) {
+template <class ELFT> static size_t readRecordSize(ArrayRef<uint8_t> D) {
const endianness E = ELFT::TargetEndianness;
if (D.size() < 4)
fatal("CIE/FDE too small");
// First 4 bytes of CIE/FDE is the size of the record.
- // If it is 0xFFFFFFFF, the next 8 bytes contain the size instead.
+ // If it is 0xFFFFFFFF, the next 8 bytes contain the size instead,
+ // but we do not support that format yet.
uint64_t V = read32<E>(D.data());
- if (V < UINT32_MAX) {
- uint64_t Len = V + 4;
- if (Len > D.size())
- fatal("CIE/FIE ends past the end of the section");
- return Len;
- }
-
- if (D.size() < 12)
- fatal("CIE/FDE too small");
- V = read64<E>(D.data() + 4);
- uint64_t Len = V + 12;
- if (Len < V || D.size() < Len)
+ if (V == UINT32_MAX)
+ fatal("CIE/FDE too large");
+ uint64_t Size = V + 4;
+ if (Size > D.size())
fatal("CIE/FIE ends past the end of the section");
- return Len;
+ return Size;
}
// Returns the first relocation that points to a region
@@ -1122,88 +1102,123 @@ static const RelTy *getReloc(IntTy Begin
return &Rels[I];
}
+// .eh_frame is a sequence of CIE or FDE records.
+// This function splits an input section into records and returns them.
+template <class ELFT>
+std::vector<SectionPiece>
+EHOutputSection<ELFT>::splitInputSection(const EHInputSection<ELFT> *Sec) {
+ ArrayRef<uint8_t> Data = Sec->getSectionData();
+ std::vector<SectionPiece> V;
+ for (size_t Off = 0, End = Data.size(); Off != End;) {
+ size_t Size = readRecordSize<ELFT>(Data.slice(Off));
+ // The empty record is the end marker.
+ if (Size == 4)
+ break;
+ V.emplace_back(Off, Data.slice(Off, Size));
+ Off += Size;
+ }
+ return V;
+}
+
+// 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 <class ELFT>
template <class RelTy>
-void EHOutputSection<ELFT>::addSectionAux(EHInputSection<ELFT> *Sec,
- ArrayRef<RelTy> Rels) {
+CieRecord &EHOutputSection<ELFT>::addCie(SectionPiece &Piece,
+ EHInputSection<ELFT> *Sec,
+ ArrayRef<RelTy> Rels) {
const endianness E = ELFT::TargetEndianness;
+ if (read32<E>(Piece.Data.data() + 4) != 0)
+ fatal("CIE expected at beginning of .eh_frame: " + Sec->getSectionName());
- Sec->OutSec = this;
- this->updateAlign(Sec->Align);
- Sections.push_back(Sec);
-
- ArrayRef<uint8_t> D = Sec->getSectionData();
- uintX_t Offset = 0;
-
- DenseMap<uintX_t, uintX_t> OffsetToIndex;
- while (!D.empty()) {
- uintX_t Length = readEntryLength<ELFT>(D);
- // If CIE/FDE data length is zero then Length is 4, this
- // shall be considered a terminator and processing shall end.
- if (Length == 4)
- break;
- StringRef Entry((const char *)D.data(), Length);
+ SymbolBody *Personality = nullptr;
+ if (const RelTy *Rel = getReloc(Piece.InputOff, Piece.size(), Rels))
+ Personality = &Sec->getFile()->getRelocTargetSym(*Rel);
+
+ // Search for an existing CIE by CIE contents/relocation target pair.
+ CieRecord &Cie = CieMap[{Piece.Data, Personality}];
+
+ // If not found, create a new one.
+ if (Cie.Piece == nullptr) {
+ Cie.Piece = &Piece;
+ if (Config->EhFrameHdr)
+ Cie.FdeEncoding = getFdeEncoding(Piece.Data);
+ Cies.push_back(&Cie);
+ }
+ return Cie;
+}
- unsigned Index = Sec->Pieces.size();
- Sec->Pieces.emplace_back(Offset, D.slice(0, Length));
+template <class ELFT> static void validateFde(SectionPiece &Piece) {
+ // We assume that all FDEs refer the first CIE in the same object file.
+ const endianness E = ELFT::TargetEndianness;
+ uint32_t ID = read32<E>(Piece.Data.data() + 4);
+ if (Piece.InputOff + 4 - ID != 0)
+ fatal("invalid CIE reference");
+}
- uint32_t ID = read32<E>(D.data() + 4);
- if (ID == 0) {
- // CIE
- CieRecord<ELFT> Cie(Sec, Index);
- if (Config->EhFrameHdr)
- Cie.FdeEncoding = getFdeEncoding(D);
-
- SymbolBody *Personality = nullptr;
- if (const RelTy *Rel = getReloc(Offset, Length, Rels))
- Personality = &Sec->getFile()->getRelocTargetSym(*Rel);
-
- std::pair<StringRef, SymbolBody *> CieInfo(Entry, Personality);
- auto P = CieMap.insert(std::make_pair(CieInfo, Cies.size()));
- if (P.second) {
- Cies.push_back(Cie);
- this->Header.sh_size += alignTo(Length, sizeof(uintX_t));
- }
- OffsetToIndex[Offset] = P.first->second;
- } else {
- const RelTy *Rel = getReloc(Offset, Length, Rels);
- if (!Rel)
- fatal("FDE doesn't reference another section");
- SymbolBody &B = Sec->getFile()->getRelocTargetSym(*Rel);
-
- auto *D = dyn_cast<DefinedRegular<ELFT>>(&B);
- if (D && D->Section) {
- InputSectionBase<ELFT> *Target = D->Section->Repl;
- if (Target && Target->Live) {
- uint32_t CieOffset = Offset + 4 - ID;
- auto I = OffsetToIndex.find(CieOffset);
- if (I == OffsetToIndex.end())
- fatal("invalid CIE reference");
- Cies[I->second].Fdes.push_back(EHRegion<ELFT>(Sec, Index));
- Out<ELFT>::EhFrameHdr->reserveFde();
- this->Header.sh_size += alignTo(Length, sizeof(uintX_t));
- }
- }
- }
+// There is one FDE per function. Returns true if a given FDE
+// points to a live function.
+template <class ELFT>
+template <class RelTy>
+bool EHOutputSection<ELFT>::isFdeLive(SectionPiece &Piece,
+ EHInputSection<ELFT> *Sec,
+ ArrayRef<RelTy> Rels) {
+ const RelTy *Rel = getReloc(Piece.InputOff, Piece.size(), Rels);
+ if (!Rel)
+ fatal("FDE doesn't reference another section");
+ SymbolBody &B = Sec->getFile()->getRelocTargetSym(*Rel);
+ auto *D = dyn_cast<DefinedRegular<ELFT>>(&B);
+ if (!D || !D->Section)
+ return false;
+ InputSectionBase<ELFT> *Target = D->Section->Repl;
+ return Target && Target->Live;
+}
+
+// .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 <class ELFT>
+template <class RelTy>
+void EHOutputSection<ELFT>::addSectionAux(EHInputSection<ELFT> *Sec,
+ ArrayRef<RelTy> Rels) {
+ SectionPiece &CiePiece = Sec->Pieces[0];
+ CieRecord &Cie = addCie(CiePiece, Sec, Rels);
- Offset += Length;
- D = D.slice(Length);
+ for (size_t I = 1, End = Sec->Pieces.size(); I != End; ++I) {
+ SectionPiece &FdePiece = Sec->Pieces[I];
+ validateFde<ELFT>(FdePiece);
+ if (!isFdeLive(FdePiece, Sec, Rels))
+ continue;
+ Cie.FdePieces.push_back(&FdePiece);
+ Out<ELFT>::EhFrameHdr->reserveFde();
}
}
template <class ELFT>
void EHOutputSection<ELFT>::addSection(InputSectionBase<ELFT> *C) {
auto *Sec = cast<EHInputSection<ELFT>>(C);
- const Elf_Shdr *RelSec = Sec->RelocSection;
- if (!RelSec) {
- addSectionAux(Sec, makeArrayRef<Elf_Rela>(nullptr, nullptr));
+ Sec->OutSec = this;
+ this->updateAlign(Sec->Align);
+ Sections.push_back(Sec);
+
+ // .eh_frame is a sequence of CIE or FDE records. This function
+ // splits it into pieces so that we can call
+ // SplitInputSection::getSectionPiece on the section.
+ Sec->Pieces = splitInputSection(Sec);
+ if (Sec->Pieces.empty())
+ return;
+
+ if (const Elf_Shdr *RelSec = Sec->RelocSection) {
+ ELFFile<ELFT> &Obj = Sec->getFile()->getObj();
+ if (RelSec->sh_type == SHT_RELA)
+ addSectionAux(Sec, Obj.relas(RelSec));
+ else
+ addSectionAux(Sec, Obj.rels(RelSec));
return;
}
- ELFFile<ELFT> &Obj = Sec->getFile()->getObj();
- if (RelSec->sh_type == SHT_RELA)
- addSectionAux(Sec, Obj.relas(RelSec));
- else
- addSectionAux(Sec, Obj.rels(RelSec));
+ addSectionAux(Sec, makeArrayRef<Elf_Rela>(nullptr, nullptr));
}
template <class ELFT>
@@ -1220,30 +1235,33 @@ template <class ELFT> void EHOutputSecti
return;
Finalized = true;
- size_t Offset = 0;
- for (const CieRecord<ELFT> &Cie : Cies) {
- Cie.Sec->Pieces[Cie.Index].OutputOff = Offset;
- Offset += alignTo(Cie.data().size(), sizeof(uintX_t));
-
- for (const EHRegion<ELFT> &Fde : Cie.Fdes) {
- Fde.Sec->Pieces[Fde.Index].OutputOff = Offset;
- Offset += alignTo(Fde.data().size(), sizeof(uintX_t));
+ size_t Off = 0;
+ for (CieRecord *Cie : Cies) {
+ Cie->Piece->OutputOff = Off;
+ Off += alignTo(Cie->Piece->size(), sizeof(uintX_t));
+
+ for (SectionPiece *Fde : Cie->FdePieces) {
+ Fde->OutputOff = Off;
+ Off += alignTo(Fde->size(), sizeof(uintX_t));
}
}
+ this->Header.sh_size = Off;
}
template <class ELFT> void EHOutputSection<ELFT>::writeTo(uint8_t *Buf) {
const endianness E = ELFT::TargetEndianness;
- for (const CieRecord<ELFT> &Cie : Cies) {
- size_t CieOffset = Cie.Sec->Pieces[Cie.Index].OutputOff;
- writeCieFde<ELFT>(Buf + CieOffset, Cie.data());
-
- for (const EHRegion<ELFT> &Fde : Cie.Fdes) {
- size_t Offset = Fde.Sec->Pieces[Fde.Index].OutputOff;
- writeCieFde<ELFT>(Buf + Offset, Fde.data());
- write32<E>(Buf + Offset + 4, Offset + 4 - CieOffset); // Pointer
-
- Out<ELFT>::EhFrameHdr->addFde(Cie.FdeEncoding, Offset, Buf + Offset + 8);
+ for (CieRecord *Cie : Cies) {
+ size_t CieOffset = Cie->Piece->OutputOff;
+ writeCieFde<ELFT>(Buf + CieOffset, Cie->Piece->Data);
+
+ for (SectionPiece *Fde : Cie->FdePieces) {
+ size_t Off = Fde->OutputOff;
+ writeCieFde<ELFT>(Buf + Off, Fde->Data);
+
+ // FDE's second word should have the offset to an associated CIE.
+ // Write it.
+ write32<E>(Buf + Off + 4, Off + 4 - CieOffset);
+ Out<ELFT>::EhFrameHdr->addFde(Cie->FdeEncoding, Off, Buf + Off + 8);
}
}
Modified: lld/trunk/ELF/OutputSections.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/OutputSections.h?rev=270382&r1=270381&r2=270382&view=diff
==============================================================================
--- lld/trunk/ELF/OutputSections.h (original)
+++ lld/trunk/ELF/OutputSections.h Sun May 22 18:16:14 2016
@@ -23,6 +23,7 @@ namespace lld {
namespace elf {
class SymbolBody;
+struct SectionPiece;
template <class ELFT> class SymbolTable;
template <class ELFT> class SymbolTableSection;
template <class ELFT> class StringTableSection;
@@ -324,21 +325,13 @@ private:
llvm::StringTableBuilder Builder;
};
-// FDE or CIE
-template <class ELFT> struct EHRegion {
- typedef typename ELFT::uint uintX_t;
- EHRegion(EHInputSection<ELFT> *Sec, unsigned Index);
- ArrayRef<uint8_t> data() const;
- EHInputSection<ELFT> *Sec;
- unsigned Index;
-};
-
-template <class ELFT> struct CieRecord : public EHRegion<ELFT> {
- CieRecord(EHInputSection<ELFT> *S, unsigned Index);
- std::vector<EHRegion<ELFT>> Fdes;
- uint8_t FdeEncoding;
+struct CieRecord {
+ SectionPiece *Piece = nullptr;
+ std::vector<SectionPiece *> FdePieces;
+ uint8_t FdeEncoding = 0;
};
+// Output section for .eh_frame.
template <class ELFT>
class EHOutputSection final : public OutputSectionBase<ELFT> {
public:
@@ -352,19 +345,30 @@ public:
void
forEachInputSection(std::function<void(InputSectionBase<ELFT> *)> F) override;
+ void addSection(InputSectionBase<ELFT> *S) override;
+
+private:
template <class RelTy>
void addSectionAux(EHInputSection<ELFT> *S, llvm::ArrayRef<RelTy> Rels);
- void addSection(InputSectionBase<ELFT> *S) override;
+ std::vector<SectionPiece> splitInputSection(const EHInputSection<ELFT> *Sec);
+
+ template <class RelTy>
+ CieRecord &addCie(SectionPiece &Piece, EHInputSection<ELFT> *Sec,
+ ArrayRef<RelTy> Rels);
+
+ template <class RelTy>
+ bool isFdeLive(SectionPiece &Piece, EHInputSection<ELFT> *Sec,
+ ArrayRef<RelTy> Rels);
-private:
uint8_t getFdeEncoding(ArrayRef<uint8_t> D);
std::vector<EHInputSection<ELFT> *> Sections;
- std::vector<CieRecord<ELFT>> Cies;
+ std::vector<CieRecord *> Cies;
+
+ // CIE records are uniquified by their contents and personality functions.
+ llvm::DenseMap<std::pair<ArrayRef<uint8_t>, SymbolBody *>, CieRecord> CieMap;
- // Maps CIE content + personality to a index in Cies.
- llvm::DenseMap<std::pair<StringRef, SymbolBody *>, uintX_t> CieMap;
bool Finalized = false;
};
Modified: lld/trunk/test/ELF/invalid-cie-length4.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/invalid-cie-length4.s?rev=270382&r1=270381&r2=270382&view=diff
==============================================================================
--- lld/trunk/test/ELF/invalid-cie-length4.s (original)
+++ lld/trunk/test/ELF/invalid-cie-length4.s Sun May 22 18:16:14 2016
@@ -7,4 +7,4 @@
.long 0xFFFFFFFF
.byte 0
-// CHECK: CIE/FDE too small
+// CHECK: CIE/FDE too large
Modified: lld/trunk/test/ELF/invalid-cie-length5.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/invalid-cie-length5.s?rev=270382&r1=270381&r2=270382&view=diff
==============================================================================
--- lld/trunk/test/ELF/invalid-cie-length5.s (original)
+++ lld/trunk/test/ELF/invalid-cie-length5.s Sun May 22 18:16:14 2016
@@ -7,4 +7,4 @@
.long 0xFFFFFFFF
.quad 0xFFFFFFFFFFFFFFF4
-// CHECK: CIE/FIE ends past the end of the section
+// CHECK: CIE/FDE too large
Removed: lld/trunk/test/ELF/valid-cie-length-dw64.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/valid-cie-length-dw64.s?rev=270381&view=auto
==============================================================================
--- lld/trunk/test/ELF/valid-cie-length-dw64.s (original)
+++ lld/trunk/test/ELF/valid-cie-length-dw64.s (removed)
@@ -1,13 +0,0 @@
-// REQUIRES: x86
-
-// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
-// RUN: not ld.lld %t -o %t2 2>&1 | FileCheck %s
-
- .section .eh_frame
- .long 0xFFFFFFFF
- .quad 1
- nop
-
-// CHECK-NOT: Truncated CIE/FDE length
-// CHECK-NOT: CIE/FIE size is too large
-// CHECK-NOT: CIE/FIE ends past the end of the section
More information about the llvm-commits
mailing list