forked from OSchip/llvm-project
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
This commit is contained in:
parent
9eec550a2b
commit
2deeb6093d
|
|
@ -443,14 +443,53 @@ bool EhInputSection<ELFT>::classof(const InputSectionBase<ELFT> *S) {
|
|||
return S->SectionKind == InputSectionBase<ELFT>::EHFrame;
|
||||
}
|
||||
|
||||
// Returns the index of the first relocation that points to a region between
|
||||
// Begin and Begin+Size.
|
||||
template <class IntTy, class RelTy>
|
||||
static unsigned getReloc(IntTy Begin, IntTy Size, const ArrayRef<RelTy> &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 <class ELFT>
|
||||
void EhInputSection<ELFT>::split() {
|
||||
// Early exit if already split.
|
||||
if (!this->Pieces.empty())
|
||||
return;
|
||||
|
||||
if (RelocSection) {
|
||||
ELFFile<ELFT> &Obj = this->File->getObj();
|
||||
if (RelocSection->sh_type == SHT_RELA)
|
||||
split(Obj.relas(RelocSection));
|
||||
else
|
||||
split(Obj.rels(RelocSection));
|
||||
return;
|
||||
}
|
||||
split(makeArrayRef<typename ELFT::Rela>(nullptr, nullptr));
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
template <class RelTy>
|
||||
void EhInputSection<ELFT>::split(ArrayRef<RelTy> Rels) {
|
||||
ArrayRef<uint8_t> Data = this->getSectionData();
|
||||
unsigned RelI = 0;
|
||||
for (size_t Off = 0, End = Data.size(); Off != End;) {
|
||||
size_t Size = readEhRecordSize<ELFT>(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;
|
||||
|
|
|
|||
|
|
@ -149,6 +149,12 @@ private:
|
|||
llvm::DenseSet<uintX_t> LiveOffsets;
|
||||
};
|
||||
|
||||
struct EhSectionPiece : public SectionPiece {
|
||||
EhSectionPiece(size_t Off, ArrayRef<uint8_t> Data, unsigned FirstRelocation)
|
||||
: SectionPiece(Off, Data), FirstRelocation(FirstRelocation) {}
|
||||
unsigned FirstRelocation;
|
||||
};
|
||||
|
||||
// This corresponds to a .eh_frame section of an input file.
|
||||
template <class ELFT> class EhInputSection : public InputSectionBase<ELFT> {
|
||||
public:
|
||||
|
|
@ -157,10 +163,11 @@ public:
|
|||
EhInputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header);
|
||||
static bool classof(const InputSectionBase<ELFT> *S);
|
||||
void split();
|
||||
template <class RelTy> void split(ArrayRef<RelTy> Rels);
|
||||
|
||||
// Splittable sections are handled as a sequence of data
|
||||
// rather than a single large blob of data.
|
||||
std::vector<SectionPiece> Pieces;
|
||||
std::vector<EhSectionPiece> Pieces;
|
||||
|
||||
// Relocation section that refer to this one.
|
||||
const Elf_Shdr *RelocSection = nullptr;
|
||||
|
|
|
|||
|
|
@ -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<ELFT> &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 <class ELFT, class RelTy>
|
||||
static void
|
||||
scanEhFrameSection(EhInputSection<ELFT> &EH, ArrayRef<RelTy> Rels,
|
||||
std::function<void(ResolvedReloc<ELFT>)> 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<E>(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<ELFT> R = resolveReloc(EH, Rels[I2]);
|
||||
if (!R.Sec || R.Sec == &InputSection<ELFT>::Discarded)
|
||||
continue;
|
||||
if (R.Sec->getSectionHdr()->sh_flags & SHF_EXECINSTR)
|
||||
continue;
|
||||
Enqueue({R.Sec, 0});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static void scanEhFrameSection(EhInputSection<ELFT> &EH,
|
||||
std::function<void(ResolvedReloc<ELFT>)> Fn) {
|
||||
static void
|
||||
scanEhFrameSection(EhInputSection<ELFT> &EH,
|
||||
std::function<void(ResolvedReloc<ELFT>)> 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<ELFT> &EObj = EH.getFile()->getObj();
|
||||
run<ELFT>(EObj, EH, EH.RelocSection, [&](ResolvedReloc<ELFT> R) {
|
||||
if (!R.Sec || R.Sec == &InputSection<ELFT>::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
|
||||
|
|
|
|||
|
|
@ -963,41 +963,22 @@ template <class ELFT>
|
|||
EhOutputSection<ELFT>::EhOutputSection()
|
||||
: OutputSectionBase<ELFT>(".eh_frame", SHT_PROGBITS, SHF_ALLOC) {}
|
||||
|
||||
// Returns the first relocation that points to a region
|
||||
// between Begin and Begin+Size.
|
||||
template <class IntTy, class RelTy>
|
||||
static const RelTy *getReloc(IntTy Begin, IntTy Size, ArrayRef<RelTy> &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<RelTy>();
|
||||
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 <class ELFT>
|
||||
template <class RelTy>
|
||||
CieRecord *EhOutputSection<ELFT>::addCie(SectionPiece &Piece,
|
||||
CieRecord *EhOutputSection<ELFT>::addCie(EhSectionPiece &Piece,
|
||||
EhInputSection<ELFT> *Sec,
|
||||
ArrayRef<RelTy> &Rels) {
|
||||
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());
|
||||
|
||||
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<ELFT>::addCie(SectionPiece &Piece,
|
|||
// points to a live function.
|
||||
template <class ELFT>
|
||||
template <class RelTy>
|
||||
bool EhOutputSection<ELFT>::isFdeLive(SectionPiece &Piece,
|
||||
bool EhOutputSection<ELFT>::isFdeLive(EhSectionPiece &Piece,
|
||||
EhInputSection<ELFT> *Sec,
|
||||
ArrayRef<RelTy> &Rels) {
|
||||
const RelTy *Rel = getReloc(Piece.InputOff, Piece.size(), Rels);
|
||||
if (!Rel)
|
||||
ArrayRef<RelTy> 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<DefinedRegular<ELFT>>(&B);
|
||||
if (!D || !D->Section)
|
||||
return false;
|
||||
|
|
@ -1039,7 +1021,7 @@ void EhOutputSection<ELFT>::addSectionAux(EhInputSection<ELFT> *Sec,
|
|||
const endianness E = ELFT::TargetEndianness;
|
||||
|
||||
DenseMap<size_t, CieRecord *> OffsetToCie;
|
||||
for (SectionPiece &Piece : Sec->Pieces) {
|
||||
for (EhSectionPiece &Piece : Sec->Pieces) {
|
||||
// The empty record is the end marker.
|
||||
if (Piece.size() == 4)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ namespace lld {
|
|||
namespace elf {
|
||||
|
||||
class SymbolBody;
|
||||
struct SectionPiece;
|
||||
struct EhSectionPiece;
|
||||
template <class ELFT> class SymbolTable;
|
||||
template <class ELFT> class SymbolTableSection;
|
||||
template <class ELFT> class StringTableSection;
|
||||
|
|
@ -372,8 +372,8 @@ private:
|
|||
};
|
||||
|
||||
struct CieRecord {
|
||||
SectionPiece *Piece = nullptr;
|
||||
std::vector<SectionPiece *> FdePieces;
|
||||
EhSectionPiece *Piece = nullptr;
|
||||
std::vector<EhSectionPiece *> FdePieces;
|
||||
};
|
||||
|
||||
// Output section for .eh_frame.
|
||||
|
|
@ -399,12 +399,12 @@ private:
|
|||
void addSectionAux(EhInputSection<ELFT> *S, llvm::ArrayRef<RelTy> Rels);
|
||||
|
||||
template <class RelTy>
|
||||
CieRecord *addCie(SectionPiece &Piece, EhInputSection<ELFT> *Sec,
|
||||
ArrayRef<RelTy> &Rels);
|
||||
CieRecord *addCie(EhSectionPiece &Piece, EhInputSection<ELFT> *Sec,
|
||||
ArrayRef<RelTy> Rels);
|
||||
|
||||
template <class RelTy>
|
||||
bool isFdeLive(SectionPiece &Piece, EhInputSection<ELFT> *Sec,
|
||||
ArrayRef<RelTy> &Rels);
|
||||
bool isFdeLive(EhSectionPiece &Piece, EhInputSection<ELFT> *Sec,
|
||||
ArrayRef<RelTy> Rels);
|
||||
|
||||
uintX_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc);
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
Loading…
Reference in New Issue