mirror of
https://github.com/intel/llvm.git
synced 2026-01-13 02:38:07 +08:00
ELF: Move .eh_frame_hdr code closer to .eh_frame . NFC
... as they are closely related. Also improve the comments.
This commit is contained in:
@@ -540,43 +540,6 @@ void EhFrameSection::finalizeContents() {
|
||||
this->size = off;
|
||||
}
|
||||
|
||||
// Returns data for .eh_frame_hdr. .eh_frame_hdr is a binary search table
|
||||
// to get an FDE from an address to which FDE is applied. This function
|
||||
// returns a list of such pairs.
|
||||
SmallVector<EhFrameSection::FdeData, 0> EhFrameSection::getFdeData() const {
|
||||
uint8_t *buf = ctx.bufferStart + getParent()->offset + outSecOff;
|
||||
SmallVector<FdeData, 0> ret;
|
||||
|
||||
uint64_t va = getPartition(ctx).ehFrameHdr->getVA();
|
||||
for (CieRecord *rec : cieRecords) {
|
||||
uint8_t enc = getFdeEncoding(rec->cie);
|
||||
for (EhSectionPiece *fde : rec->fdes) {
|
||||
uint64_t pc = getFdePc(buf, fde->outputOff, enc);
|
||||
uint64_t fdeVA = getParent()->addr + fde->outputOff;
|
||||
if (!isInt<32>(pc - va)) {
|
||||
Err(ctx) << fde->sec << ": PC offset is too large: 0x"
|
||||
<< Twine::utohexstr(pc - va);
|
||||
continue;
|
||||
}
|
||||
ret.push_back({uint32_t(pc - va), uint32_t(fdeVA - va)});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the FDE list by their PC and uniqueify. Usually there is only
|
||||
// one FDE for a PC (i.e. function), but if ICF merges two functions
|
||||
// into one, there can be more than one FDEs pointing to the address.
|
||||
auto less = [](const FdeData &a, const FdeData &b) {
|
||||
return a.pcRel < b.pcRel;
|
||||
};
|
||||
llvm::stable_sort(ret, less);
|
||||
auto eq = [](const FdeData &a, const FdeData &b) {
|
||||
return a.pcRel == b.pcRel;
|
||||
};
|
||||
ret.erase(llvm::unique(ret, eq), ret.end());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint64_t readFdeAddr(Ctx &ctx, uint8_t *buf, int size) {
|
||||
switch (size) {
|
||||
case DW_EH_PE_udata2:
|
||||
@@ -630,14 +593,79 @@ void EhFrameSection::writeTo(uint8_t *buf) {
|
||||
}
|
||||
}
|
||||
|
||||
// Apply relocations. .eh_frame section contents are not contiguous
|
||||
// in the output buffer, but relocateAlloc() still works because
|
||||
// getOffset() takes care of discontiguous section pieces.
|
||||
// Apply relocations to .eh_frame entries. This includes CIE personality
|
||||
// pointers, FDE initial_location fields, and LSDA pointers.
|
||||
for (EhInputSection *s : sections)
|
||||
ctx.target->relocateEh(*s, buf);
|
||||
|
||||
if (getPartition(ctx).ehFrameHdr && getPartition(ctx).ehFrameHdr->getParent())
|
||||
getPartition(ctx).ehFrameHdr->write();
|
||||
EhFrameHeader *hdr = getPartition(ctx).ehFrameHdr.get();
|
||||
if (!hdr || !hdr->getParent())
|
||||
return;
|
||||
|
||||
// Write the .eh_frame_hdr section, which contains a binary search table of
|
||||
// pointers to FDEs. This must be written after .eh_frame relocation since
|
||||
// the content depends on relocated initial_location fields in FDEs.
|
||||
using FdeData = EhFrameSection::FdeData;
|
||||
SmallVector<FdeData, 0> fdes;
|
||||
uint64_t va = hdr->getVA();
|
||||
for (CieRecord *rec : cieRecords) {
|
||||
uint8_t enc = getFdeEncoding(rec->cie);
|
||||
for (EhSectionPiece *fde : rec->fdes) {
|
||||
uint64_t pc = getFdePc(buf, fde->outputOff, enc);
|
||||
uint64_t fdeVA = getParent()->addr + fde->outputOff;
|
||||
if (!isInt<32>(pc - va)) {
|
||||
Err(ctx) << fde->sec << ": PC offset is too large: 0x"
|
||||
<< Twine::utohexstr(pc - va);
|
||||
continue;
|
||||
}
|
||||
fdes.push_back({uint32_t(pc - va), uint32_t(fdeVA - va)});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the FDE list by their PC and uniqueify. Usually there is only
|
||||
// one FDE for a PC (i.e. function), but if ICF merges two functions
|
||||
// into one, there can be more than one FDEs pointing to the address.
|
||||
llvm::stable_sort(fdes, [](const FdeData &a, const FdeData &b) {
|
||||
return a.pcRel < b.pcRel;
|
||||
});
|
||||
fdes.erase(
|
||||
llvm::unique(fdes, [](auto &a, auto &b) { return a.pcRel == b.pcRel; }),
|
||||
fdes.end());
|
||||
|
||||
// Write header.
|
||||
uint8_t *hdrBuf = ctx.bufferStart + hdr->getParent()->offset + hdr->outSecOff;
|
||||
hdrBuf[0] = 1; // version
|
||||
hdrBuf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4; // eh_frame_ptr_enc
|
||||
hdrBuf[2] = DW_EH_PE_udata4; // fde_count_enc
|
||||
hdrBuf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; // table_enc
|
||||
write32(ctx, hdrBuf + 4,
|
||||
getParent()->addr - hdr->getVA() - 4); // eh_frame_ptr
|
||||
write32(ctx, hdrBuf + 8, fdes.size()); // fde_count
|
||||
hdrBuf += 12;
|
||||
|
||||
// Write binary search table. Each entry describes the starting PC and the FDE
|
||||
// address.
|
||||
for (FdeData &fde : fdes) {
|
||||
write32(ctx, hdrBuf, fde.pcRel);
|
||||
write32(ctx, hdrBuf + 4, fde.fdeVARel);
|
||||
hdrBuf += 8;
|
||||
}
|
||||
}
|
||||
|
||||
EhFrameHeader::EhFrameHeader(Ctx &ctx)
|
||||
: SyntheticSection(ctx, ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, 4) {}
|
||||
|
||||
void EhFrameHeader::writeTo(uint8_t *buf) {
|
||||
// The section content is written during EhFrameSection::writeTo.
|
||||
}
|
||||
|
||||
size_t EhFrameHeader::getSize() const {
|
||||
// .eh_frame_hdr has a 12 bytes header followed by an array of FDEs.
|
||||
return 12 + getPartition(ctx).ehFrame->numFdes * 8;
|
||||
}
|
||||
|
||||
bool EhFrameHeader::isNeeded() const {
|
||||
return isLive() && getPartition(ctx).ehFrame->isNeeded();
|
||||
}
|
||||
|
||||
GotSection::GotSection(Ctx &ctx)
|
||||
@@ -3658,51 +3686,6 @@ void GdbIndexSection::writeTo(uint8_t *buf) {
|
||||
|
||||
bool GdbIndexSection::isNeeded() const { return !chunks.empty(); }
|
||||
|
||||
EhFrameHeader::EhFrameHeader(Ctx &ctx)
|
||||
: SyntheticSection(ctx, ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, 4) {}
|
||||
|
||||
void EhFrameHeader::writeTo(uint8_t *buf) {
|
||||
// Unlike most sections, the EhFrameHeader section is written while writing
|
||||
// another section, namely EhFrameSection, which calls the write() function
|
||||
// below from its writeTo() function. This is necessary because the contents
|
||||
// of EhFrameHeader depend on the relocated contents of EhFrameSection and we
|
||||
// don't know which order the sections will be written in.
|
||||
}
|
||||
|
||||
// .eh_frame_hdr contains a binary search table of pointers to FDEs.
|
||||
// Each entry of the search table consists of two values,
|
||||
// the starting PC from where FDEs covers, and the FDE's address.
|
||||
// It is sorted by PC.
|
||||
void EhFrameHeader::write() {
|
||||
uint8_t *buf = ctx.bufferStart + getParent()->offset + outSecOff;
|
||||
using FdeData = EhFrameSection::FdeData;
|
||||
SmallVector<FdeData, 0> fdes = getPartition(ctx).ehFrame->getFdeData();
|
||||
|
||||
buf[0] = 1;
|
||||
buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
|
||||
buf[2] = DW_EH_PE_udata4;
|
||||
buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
|
||||
write32(ctx, buf + 4,
|
||||
getPartition(ctx).ehFrame->getParent()->addr - this->getVA() - 4);
|
||||
write32(ctx, buf + 8, fdes.size());
|
||||
buf += 12;
|
||||
|
||||
for (FdeData &fde : fdes) {
|
||||
write32(ctx, buf, fde.pcRel);
|
||||
write32(ctx, buf + 4, fde.fdeVARel);
|
||||
buf += 8;
|
||||
}
|
||||
}
|
||||
|
||||
size_t EhFrameHeader::getSize() const {
|
||||
// .eh_frame_hdr has a 12 bytes header followed by an array of FDEs.
|
||||
return 12 + getPartition(ctx).ehFrame->numFdes * 8;
|
||||
}
|
||||
|
||||
bool EhFrameHeader::isNeeded() const {
|
||||
return isLive() && getPartition(ctx).ehFrame->isNeeded();
|
||||
}
|
||||
|
||||
VersionDefinitionSection::VersionDefinitionSection(Ctx &ctx)
|
||||
: SyntheticSection(ctx, ".gnu.version_d", SHT_GNU_verdef, SHF_ALLOC,
|
||||
sizeof(uint32_t)) {}
|
||||
|
||||
@@ -68,7 +68,6 @@ public:
|
||||
uint32_t fdeVARel;
|
||||
};
|
||||
|
||||
SmallVector<FdeData, 0> getFdeData() const;
|
||||
ArrayRef<CieRecord *> getCieRecords() const { return cieRecords; }
|
||||
template <class ELFT>
|
||||
void iterateFDEWithLSDA(llvm::function_ref<void(InputSection &)> fn);
|
||||
@@ -95,6 +94,17 @@ private:
|
||||
llvm::DenseMap<std::pair<ArrayRef<uint8_t>, Symbol *>, CieRecord *> cieMap;
|
||||
};
|
||||
|
||||
// .eh_frame_hdr contains a binary search table for .eh_frame FDEs. The section
|
||||
// is covered by a PT_GNU_EH_FRAME segment, which allows the runtime unwinder to
|
||||
// locate it via functions like `dl_iterate_phdr`.
|
||||
class EhFrameHeader final : public SyntheticSection {
|
||||
public:
|
||||
EhFrameHeader(Ctx &);
|
||||
void writeTo(uint8_t *buf) override;
|
||||
size_t getSize() const override;
|
||||
bool isNeeded() const override;
|
||||
};
|
||||
|
||||
class GotSection final : public SyntheticSection {
|
||||
public:
|
||||
GotSection(Ctx &);
|
||||
@@ -967,24 +977,6 @@ private:
|
||||
size_t size;
|
||||
};
|
||||
|
||||
// --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")
|
||||
class EhFrameHeader final : public SyntheticSection {
|
||||
public:
|
||||
EhFrameHeader(Ctx &);
|
||||
void write();
|
||||
void writeTo(uint8_t *buf) override;
|
||||
size_t getSize() const override;
|
||||
bool isNeeded() const override;
|
||||
};
|
||||
|
||||
// For more information about .gnu.version and .gnu.version_r see:
|
||||
// https://www.akkadia.org/drepper/symbol-versioning
|
||||
|
||||
|
||||
Reference in New Issue
Block a user