Skip to content

Commit

Permalink
[ELF] - implemented --eh-frame-hdr command line option.
Browse files Browse the repository at this point in the history
--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

llvm-svn: 257753
  • Loading branch information
George Rimar committed Jan 14, 2016
1 parent 3557b88 commit 28f4fbe
Show file tree
Hide file tree
Showing 10 changed files with 418 additions and 2 deletions.
1 change: 1 addition & 0 deletions lld/ELF/Config.h
Expand Up @@ -57,6 +57,7 @@ struct Configuration {
bool DiscardAll;
bool DiscardLocals;
bool DiscardNone;
bool EhFrameHdr;
bool EnableNewDtags;
bool ExportDynamic;
bool GcSections;
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/Driver.cpp
Expand Up @@ -189,6 +189,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
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);
Expand Down
4 changes: 3 additions & 1 deletion lld/ELF/Options.td
Expand Up @@ -36,6 +36,9 @@ def discard_none : Flag<["-"], "discard-none">,
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">;

Expand Down Expand Up @@ -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">;
Expand Down
209 changes: 208 additions & 1 deletion lld/ELF/OutputSections.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -755,6 +757,99 @@ template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *Buf) {
WriteVal(DT_NULL, 0);
}

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)
Expand Down Expand Up @@ -908,7 +1003,9 @@ template <class ELFT> void OutputSection<ELFT>::writeTo(uint8_t *Buf) {
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)
Expand All @@ -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(
Expand Down Expand Up @@ -964,6 +1162,8 @@ void EHOutputSection<ELFT>::addSectionAux(
if (ID == 0) {
// CIE
Cie<ELFT> C(S, Index);
if (Config->EhFrameHdr)
C.FdeEncoding = getFdeEncoding(D);

StringRef Personality;
if (HasReloc) {
Expand All @@ -989,6 +1189,7 @@ void EHOutputSection<ELFT>::addSectionAux(
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 += align(Length, sizeof(uintX_t));
}
}
Expand Down Expand Up @@ -1062,6 +1263,7 @@ template <class ELFT> void EHOutputSection<ELFT>::writeTo(uint8_t *Buf) {
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;
}
}
Expand Down Expand Up @@ -1455,6 +1657,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>;
Expand Down
40 changes: 40 additions & 0 deletions lld/ELF/OutputSections.h
Expand Up @@ -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>
Expand All @@ -308,6 +309,7 @@ class EHOutputSection final : public OutputSectionBase<ELFT> {
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;
Expand Down Expand Up @@ -429,6 +431,42 @@ class MipsReginfoOutputSection final : public OutputSectionBase<ELFT> {
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);
}
Expand All @@ -440,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;
Expand All @@ -461,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;
Expand Down

0 comments on commit 28f4fbe

Please sign in to comment.