[lld] r257889 - Reapply r257753 with fix:
George Rimar via llvm-commits
llvm-commits at lists.llvm.org
Fri Jan 15 05:34:52 PST 2016
Author: grimar
Date: Fri Jan 15 07:34:52 2016
New Revision: 257889
URL: http://llvm.org/viewvc/llvm-project?rev=257889&view=rev
Log:
Reapply r257753 with fix:
Added check for terminator CIE/FDE which has zero data size.
void EHOutputSection<ELFT>::addSectionAux(
...
// 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;
...
After this "Bug 25923 - lld/ELF2 linked application crashes if exceptions were used." is fixed for me. Self link of clang also works.
Initial commit message:
[ELF] - implemented --eh-frame-hdr command line option.
--eh-frame-hdr
Request creation of ".eh_frame_hdr" section and ELF "PT_GNU_EH_FRAME" segment header.
Both gold and the GNU linker support an option --eh-frame-hdr which tell them to construct a header for all the .eh_frame sections. This header is placed in a section named .eh_frame_hdr and also in a PT_GNU_EH_FRAME segment. At runtime the unwinder can find all the PT_GNU_EH_FRAME segments by calling dl_iterate_phdr.
This section contains a lookup table for quick binary search of FDEs.
Detailed info can be found here:
http://www.airs.com/blog/archives/462
Differential revision: http://reviews.llvm.org/D15712
Added:
lld/trunk/test/ELF/Inputs/invalid-cie-version2.elf
- copied unchanged from r257796, lld/trunk/test/ELF/Inputs/invalid-cie-version2.elf
lld/trunk/test/ELF/eh-frame-hdr-no-out.s
- copied unchanged from r257796, lld/trunk/test/ELF/eh-frame-hdr-no-out.s
lld/trunk/test/ELF/eh-frame-hdr-no-out2.s
- copied unchanged from r257796, lld/trunk/test/ELF/eh-frame-hdr-no-out2.s
lld/trunk/test/ELF/eh-frame-hdr.s
- copied unchanged from r257796, lld/trunk/test/ELF/eh-frame-hdr.s
Modified:
lld/trunk/ELF/Config.h
lld/trunk/ELF/Driver.cpp
lld/trunk/ELF/Options.td
lld/trunk/ELF/OutputSections.cpp
lld/trunk/ELF/OutputSections.h
lld/trunk/ELF/Writer.cpp
Modified: lld/trunk/ELF/Config.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Config.h?rev=257889&r1=257888&r2=257889&view=diff
==============================================================================
--- lld/trunk/ELF/Config.h (original)
+++ lld/trunk/ELF/Config.h Fri Jan 15 07:34:52 2016
@@ -57,6 +57,7 @@ struct Configuration {
bool DiscardAll;
bool DiscardLocals;
bool DiscardNone;
+ bool EhFrameHdr;
bool EnableNewDtags;
bool ExportDynamic;
bool GcSections;
Modified: lld/trunk/ELF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Driver.cpp?rev=257889&r1=257888&r2=257889&view=diff
==============================================================================
--- lld/trunk/ELF/Driver.cpp (original)
+++ lld/trunk/ELF/Driver.cpp Fri Jan 15 07:34:52 2016
@@ -189,6 +189,7 @@ void LinkerDriver::readConfigs(opt::Inpu
Config->DiscardAll = Args.hasArg(OPT_discard_all);
Config->DiscardLocals = Args.hasArg(OPT_discard_locals);
Config->DiscardNone = Args.hasArg(OPT_discard_none);
+ Config->EhFrameHdr = Args.hasArg(OPT_eh_frame_hdr);
Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags);
Config->ExportDynamic = Args.hasArg(OPT_export_dynamic);
Config->GcSections = Args.hasArg(OPT_gc_sections);
Modified: lld/trunk/ELF/Options.td
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Options.td?rev=257889&r1=257888&r2=257889&view=diff
==============================================================================
--- lld/trunk/ELF/Options.td (original)
+++ lld/trunk/ELF/Options.td Fri Jan 15 07:34:52 2016
@@ -36,6 +36,9 @@ def discard_none : Flag<["-"], "discard-
def dynamic_linker : Separate<["--", "-"], "dynamic-linker">,
HelpText<"Which dynamic linker to use">;
+def eh_frame_hdr : Flag<["--"], "eh-frame-hdr">,
+ HelpText<"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header">;
+
def enable_new_dtags : Flag<["--"], "enable-new-dtags">,
HelpText<"Enable new dynamic tags">;
@@ -154,7 +157,6 @@ def start_group_paren: Flag<["-"], "(">;
// Options listed below are silently ignored for now for compatibility.
def build_id : Flag<["--"], "build-id">;
-def eh_frame_hdr : Flag<["--"], "eh-frame-hdr">;
def fatal_warnings : Flag<["--"], "fatal-warnings">;
def no_add_needed : Flag<["--"], "no-add-needed">;
def no_fatal_warnings : Flag<["--"], "no-fatal-warnings">;
Modified: lld/trunk/ELF/OutputSections.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/OutputSections.cpp?rev=257889&r1=257888&r2=257889&view=diff
==============================================================================
--- lld/trunk/ELF/OutputSections.cpp (original)
+++ lld/trunk/ELF/OutputSections.cpp Fri Jan 15 07:34:52 2016
@@ -11,7 +11,9 @@
#include "Config.h"
#include "SymbolTable.h"
#include "Target.h"
+#include "llvm/Support/Dwarf.h"
#include "llvm/Support/MathExtras.h"
+#include <map>
using namespace llvm;
using namespace llvm::object;
@@ -756,6 +758,99 @@ template <class ELFT> void DynamicSectio
}
template <class ELFT>
+EhFrameHeader<ELFT>::EhFrameHeader()
+ : OutputSectionBase<ELFT>(".eh_frame_hdr", llvm::ELF::SHT_PROGBITS,
+ SHF_ALLOC) {
+ // It's a 4 bytes of header + pointer to the contents of the .eh_frame section
+ // + the number of FDE pointers in the table.
+ this->Header.sh_size = 12;
+}
+
+// We have to get PC values of FDEs. They depend on relocations
+// which are target specific, so we run this code after performing
+// all relocations. We read the values from ouput buffer according to the
+// encoding given for FDEs. Return value is an offset to the initial PC value
+// for the FDE.
+template <class ELFT>
+typename EhFrameHeader<ELFT>::uintX_t
+EhFrameHeader<ELFT>::getFdePc(uintX_t EhVA, const FdeData &F) {
+ const endianness E = ELFT::TargetEndianness;
+ assert((F.Enc & 0xF0) != dwarf::DW_EH_PE_datarel);
+
+ uintX_t FdeOff = EhVA + F.Off + 8;
+ switch (F.Enc & 0xF) {
+ case dwarf::DW_EH_PE_udata2:
+ case dwarf::DW_EH_PE_sdata2:
+ return FdeOff + read16<E>(F.PCRel);
+ case dwarf::DW_EH_PE_udata4:
+ case dwarf::DW_EH_PE_sdata4:
+ return FdeOff + read32<E>(F.PCRel);
+ case dwarf::DW_EH_PE_udata8:
+ case dwarf::DW_EH_PE_sdata8:
+ return FdeOff + read64<E>(F.PCRel);
+ case dwarf::DW_EH_PE_absptr:
+ if (sizeof(uintX_t) == 8)
+ return FdeOff + read64<E>(F.PCRel);
+ return FdeOff + read32<E>(F.PCRel);
+ }
+ error("unknown FDE size encoding");
+}
+
+template <class ELFT> void EhFrameHeader<ELFT>::writeTo(uint8_t *Buf) {
+ const endianness E = ELFT::TargetEndianness;
+
+ const uint8_t Header[] = {1, dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4,
+ dwarf::DW_EH_PE_udata4,
+ dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata4};
+ memcpy(Buf, Header, sizeof(Header));
+
+ uintX_t EhVA = Sec->getVA();
+ uintX_t VA = this->getVA();
+ uintX_t EhOff = EhVA - VA - 4;
+ write32<E>(Buf + 4, EhOff);
+ write32<E>(Buf + 8, this->FdeList.size());
+ Buf += 12;
+
+ // InitialPC -> Offset in .eh_frame, sorted by InitialPC.
+ std::map<uintX_t, size_t> PcToOffset;
+ for (const FdeData &F : FdeList)
+ PcToOffset[getFdePc(EhVA, F)] = F.Off;
+
+ for (auto &I : PcToOffset) {
+ // The first four bytes are an offset to the initial PC value for the FDE.
+ write32<E>(Buf, I.first - VA);
+ // The last four bytes are an offset to the FDE data itself.
+ write32<E>(Buf + 4, EhVA + I.second - VA);
+ Buf += 8;
+ }
+}
+
+template <class ELFT>
+void EhFrameHeader<ELFT>::assignEhFrame(EHOutputSection<ELFT> *Sec) {
+ if (this->Sec && this->Sec != Sec) {
+ warning("multiple .eh_frame sections not supported for .eh_frame_hdr");
+ Live = false;
+ return;
+ }
+ Live = Config->EhFrameHdr;
+ this->Sec = Sec;
+}
+
+template <class ELFT>
+void EhFrameHeader<ELFT>::addFde(uint8_t Enc, size_t Off, uint8_t *PCRel) {
+ if (Live && (Enc & 0xF0) == dwarf::DW_EH_PE_datarel)
+ error("DW_EH_PE_datarel encoding unsupported for FDEs by .eh_frame_hdr");
+ FdeList.push_back(FdeData{Enc, Off, PCRel});
+}
+
+template <class ELFT> void EhFrameHeader<ELFT>::reserveFde() {
+ // Each FDE entry is 8 bytes long:
+ // The first four bytes are an offset to the initial PC value for the FDE. The
+ // last four byte are an offset to the FDE data itself.
+ this->Header.sh_size += 8;
+}
+
+template <class ELFT>
OutputSection<ELFT>::OutputSection(StringRef Name, uint32_t Type,
uintX_t Flags)
: OutputSectionBase<ELFT>(Name, Type, Flags) {}
@@ -908,7 +1003,9 @@ template <class ELFT> void OutputSection
template <class ELFT>
EHOutputSection<ELFT>::EHOutputSection(StringRef Name, uint32_t Type,
uintX_t Flags)
- : OutputSectionBase<ELFT>(Name, Type, Flags) {}
+ : OutputSectionBase<ELFT>(Name, Type, Flags) {
+ Out<ELFT>::EhFrameHdr->assignEhFrame(this);
+}
template <class ELFT>
EHRegion<ELFT>::EHRegion(EHInputSection<ELFT> *S, unsigned Index)
@@ -927,6 +1024,107 @@ template <class ELFT>
Cie<ELFT>::Cie(EHInputSection<ELFT> *S, unsigned Index)
: EHRegion<ELFT>(S, Index) {}
+// Read a byte and advance D by one byte.
+static uint8_t readByte(ArrayRef<uint8_t> &D) {
+ if (D.empty())
+ error("corrupted or unsupported CIE information");
+ uint8_t B = D.front();
+ D = D.slice(1);
+ return B;
+}
+
+static void skipLeb128(ArrayRef<uint8_t> &D) {
+ while (!D.empty()) {
+ uint8_t Val = D.front();
+ D = D.slice(1);
+ if ((Val & 0x80) == 0)
+ return;
+ }
+ error("corrupted or unsupported CIE information");
+}
+
+template <class ELFT> static unsigned getSizeForEncoding(unsigned Enc) {
+ typedef typename ELFFile<ELFT>::uintX_t uintX_t;
+ switch (Enc & 0x0f) {
+ default:
+ error("unknown FDE encoding");
+ case dwarf::DW_EH_PE_absptr:
+ case dwarf::DW_EH_PE_signed:
+ return sizeof(uintX_t);
+ case dwarf::DW_EH_PE_udata2:
+ case dwarf::DW_EH_PE_sdata2:
+ return 2;
+ case dwarf::DW_EH_PE_udata4:
+ case dwarf::DW_EH_PE_sdata4:
+ return 4;
+ case dwarf::DW_EH_PE_udata8:
+ case dwarf::DW_EH_PE_sdata8:
+ return 8;
+ }
+}
+
+template <class ELFT>
+uint8_t EHOutputSection<ELFT>::getFdeEncoding(ArrayRef<uint8_t> D) {
+ auto Check = [](bool C) {
+ if (!C)
+ error("corrupted or unsupported CIE information");
+ };
+
+ Check(D.size() >= 8);
+ D = D.slice(8);
+
+ uint8_t Version = readByte(D);
+ if (Version != 1 && Version != 3)
+ error("FDE version 1 or 3 expected, but got " + Twine((unsigned)Version));
+
+ auto AugEnd = std::find(D.begin() + 1, D.end(), '\0');
+ Check(AugEnd != D.end());
+ ArrayRef<uint8_t> AugString(D.begin(), AugEnd - D.begin());
+ D = D.slice(AugString.size() + 1);
+
+ // Code alignment factor should always be 1 for .eh_frame.
+ if (readByte(D) != 1)
+ error("CIE code alignment must be 1");
+ // Skip data alignment factor
+ skipLeb128(D);
+
+ // Skip the return address register. In CIE version 1 this is a single
+ // byte. In CIE version 3 this is an unsigned LEB128.
+ if (Version == 1)
+ readByte(D);
+ else
+ skipLeb128(D);
+
+ while (!AugString.empty()) {
+ switch (readByte(AugString)) {
+ case 'z':
+ skipLeb128(D);
+ break;
+ case 'R':
+ return readByte(D);
+ case 'P': {
+ uint8_t Enc = readByte(D);
+ if ((Enc & 0xf0) == dwarf::DW_EH_PE_aligned)
+ error("DW_EH_PE_aligned encoding for address of a personality routine "
+ "handler not supported");
+ unsigned EncSize = getSizeForEncoding<ELFT>(Enc);
+ Check(D.size() >= EncSize);
+ D = D.slice(EncSize);
+ break;
+ }
+ case 'S':
+ case 'L':
+ // L: Language Specific Data Area (LSDA) encoding
+ // S: This CIE represents a stack frame for the invocation of a signal
+ // handler
+ break;
+ default:
+ error("unknown .eh_frame augmentation string value");
+ }
+ }
+ return dwarf::DW_EH_PE_absptr;
+}
+
template <class ELFT>
template <bool IsRela>
void EHOutputSection<ELFT>::addSectionAux(
@@ -953,6 +1151,10 @@ void EHOutputSection<ELFT>::addSectionAu
S->Offsets.push_back(std::make_pair(Offset, -1));
uintX_t Length = readEntryLength(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);
while (RelI != RelE && RelI->r_offset < Offset)
@@ -964,6 +1166,8 @@ void EHOutputSection<ELFT>::addSectionAu
if (ID == 0) {
// CIE
Cie<ELFT> C(S, Index);
+ if (Config->EhFrameHdr)
+ C.FdeEncoding = getFdeEncoding(D);
StringRef Personality;
if (HasReloc) {
@@ -989,6 +1193,7 @@ void EHOutputSection<ELFT>::addSectionAu
if (I == OffsetToIndex.end())
error("Invalid CIE reference");
Cies[I->second].Fdes.push_back(EHRegion<ELFT>(S, Index));
+ Out<ELFT>::EhFrameHdr->reserveFde();
this->Header.sh_size += alignTo(Length, sizeof(uintX_t));
}
}
@@ -1062,6 +1267,7 @@ template <class ELFT> void EHOutputSecti
uintX_t Len = writeAlignedCieOrFde<ELFT>(F.data(), Buf + Offset);
write32<E>(Buf + Offset + 4, Offset + 4 - CieOffset); // Pointer
F.S->Offsets[F.Index].second = Offset;
+ Out<ELFT>::EhFrameHdr->addFde(C.FdeEncoding, Offset, Buf + Offset + 8);
Offset += Len;
}
}
@@ -1455,6 +1661,11 @@ template class OutputSectionBase<ELF32BE
template class OutputSectionBase<ELF64LE>;
template class OutputSectionBase<ELF64BE>;
+template class EhFrameHeader<ELF32LE>;
+template class EhFrameHeader<ELF32BE>;
+template class EhFrameHeader<ELF64LE>;
+template class EhFrameHeader<ELF64BE>;
+
template class GotPltSection<ELF32LE>;
template class GotPltSection<ELF32BE>;
template class GotPltSection<ELF64LE>;
Modified: lld/trunk/ELF/OutputSections.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/OutputSections.h?rev=257889&r1=257888&r2=257889&view=diff
==============================================================================
--- lld/trunk/ELF/OutputSections.h (original)
+++ lld/trunk/ELF/OutputSections.h Fri Jan 15 07:34:52 2016
@@ -287,6 +287,7 @@ template <class ELFT> struct EHRegion {
template <class ELFT> struct Cie : public EHRegion<ELFT> {
Cie(EHInputSection<ELFT> *S, unsigned Index);
std::vector<EHRegion<ELFT>> Fdes;
+ uint8_t FdeEncoding;
};
template <class ELFT>
@@ -308,6 +309,7 @@ public:
void addSection(InputSectionBase<ELFT> *S) override;
private:
+ uint8_t getFdeEncoding(ArrayRef<uint8_t> D);
uintX_t readEntryLength(ArrayRef<uint8_t> D);
std::vector<EHInputSection<ELFT> *> Sections;
@@ -429,6 +431,46 @@ private:
uint32_t GprMask = 0;
};
+// --eh-frame-hdr option tells linker to construct a header for all the
+// .eh_frame sections. This header is placed to a section named .eh_frame_hdr
+// and also to a PT_GNU_EH_FRAME segment.
+// At runtime the unwinder then can find all the PT_GNU_EH_FRAME segments by
+// calling dl_iterate_phdr.
+// This section contains a lookup table for quick binary search of FDEs.
+// Detailed info about internals can be found in Ian Lance Taylor's blog:
+// http://www.airs.com/blog/archives/460 (".eh_frame")
+// http://www.airs.com/blog/archives/462 (".eh_frame_hdr")
+template <class ELFT>
+class EhFrameHeader final : public OutputSectionBase<ELFT> {
+ typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
+
+public:
+ EhFrameHeader();
+ void writeTo(uint8_t *Buf) override;
+
+ void addFde(uint8_t Enc, size_t Off, uint8_t *PCRel);
+ void assignEhFrame(EHOutputSection<ELFT> *Sec);
+ void reserveFde();
+
+ bool Live = false;
+
+private:
+ struct FdeData {
+ uint8_t Enc;
+ size_t Off;
+ uint8_t *PCRel;
+ };
+
+ uintX_t getFdePc(uintX_t EhVA, const FdeData &F);
+
+ EHOutputSection<ELFT> *Sec = nullptr;
+ std::vector<FdeData> FdeList;
+};
+
+inline uint64_t align(uint64_t Value, uint64_t Align) {
+ return llvm::RoundUpToAlignment(Value, Align);
+}
+
// All output sections that are hadnled by the linker specially are
// globally accessible. Writer initializes them, so don't use them
// until Writer is initialized.
@@ -436,6 +478,7 @@ template <class ELFT> struct Out {
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Phdr Elf_Phdr;
static DynamicSection<ELFT> *Dynamic;
+ static EhFrameHeader<ELFT> *EhFrameHdr;
static GnuHashTableSection<ELFT> *GnuHashTab;
static GotPltSection<ELFT> *GotPlt;
static GotSection<ELFT> *Got;
@@ -457,6 +500,7 @@ template <class ELFT> struct Out {
};
template <class ELFT> DynamicSection<ELFT> *Out<ELFT>::Dynamic;
+template <class ELFT> EhFrameHeader<ELFT> *Out<ELFT>::EhFrameHdr;
template <class ELFT> GnuHashTableSection<ELFT> *Out<ELFT>::GnuHashTab;
template <class ELFT> GotPltSection<ELFT> *Out<ELFT>::GotPlt;
template <class ELFT> GotSection<ELFT> *Out<ELFT>::Got;
Modified: lld/trunk/ELF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Writer.cpp?rev=257889&r1=257888&r2=257889&view=diff
==============================================================================
--- lld/trunk/ELF/Writer.cpp (original)
+++ lld/trunk/ELF/Writer.cpp Fri Jan 15 07:34:52 2016
@@ -140,6 +140,8 @@ template <class ELFT> void elf2::writeRe
Out<ELFT>::RelaPlt = &RelaPlt;
DynamicSection<ELFT> Dynamic(*Symtab);
Out<ELFT>::Dynamic = &Dynamic;
+ EhFrameHeader<ELFT> EhFrameHdr;
+ Out<ELFT>::EhFrameHdr = &EhFrameHdr;
Writer<ELFT>(*Symtab).run();
}
@@ -914,6 +916,9 @@ template <class ELFT> void Writer<ELFT>:
Add(Out<ELFT>::GotPlt);
if (!Out<ELFT>::Plt->empty())
Add(Out<ELFT>::Plt);
+
+ if (Out<ELFT>::EhFrameHdr->Live)
+ Add(Out<ELFT>::EhFrameHdr);
}
// The linker is expected to define SECNAME_start and SECNAME_end
@@ -1113,6 +1118,12 @@ template <class ELFT> void Writer<ELFT>:
*PH = GnuRelroPhdr;
}
+ if (Out<ELFT>::EhFrameHdr->Live) {
+ Elf_Phdr *PH = &Phdrs[++PhdrIdx];
+ PH->p_type = PT_GNU_EH_FRAME;
+ copyPhdr(PH, Out<ELFT>::EhFrameHdr);
+ }
+
// PT_GNU_STACK is a special section to tell the loader to make the
// pages for the stack non-executable.
if (!Config->ZExecStack) {
@@ -1162,6 +1173,8 @@ template <class ELFT> int Writer<ELFT>::
++I;
if (HasRelro)
++I;
+ if (Out<ELFT>::EhFrameHdr->Live)
+ ++I;
return I;
}
More information about the llvm-commits
mailing list