Skip to content

Commit

Permalink
[ELF][ARM] Redesign of .ARM.exidx handling to use a SyntheticSection
Browse files Browse the repository at this point in the history
Instead of creating extra Synthetic .ARM.exidx sections to account for
gaps in the table, create a single .ARM.exidx SyntheticSection that can
derive the contents of the gaps from a sorted list of the executable
InputSections. This has the benefit of moving the ARM specific code for
SyntheticSections in SHF_LINK_ORDER processing and the table merging code
into the ARM specific SyntheticSection. This also makes it easier to create
EXIDX_CANTUNWIND table entries for executable InputSections that don't
have an associated .ARM.exidx section.

Fixes pr40277

Differential Revision: https://reviews.llvm.org/D59216

llvm-svn: 356666
  • Loading branch information
smithp35 committed Mar 21, 2019
1 parent d47eac5 commit d3511a2
Show file tree
Hide file tree
Showing 11 changed files with 391 additions and 175 deletions.
4 changes: 3 additions & 1 deletion lld/ELF/OutputSections.cpp
Expand Up @@ -289,7 +289,9 @@ void OutputSection::finalize() {
// SHF_LINK_ORDER flag. The dependency is indicated by the sh_link field. We
// need to translate the InputSection sh_link to the OutputSection sh_link,
// all InputSections in the OutputSection have the same dependency.
if (auto *D = First->getLinkOrderDep())
if (auto *EX = dyn_cast<ARMExidxSyntheticSection>(First))
Link = EX->getLinkOrderDep()->getParent()->SectionIndex;
else if (auto *D = First->getLinkOrderDep())
Link = D->getParent()->SectionIndex;
}

Expand Down
194 changes: 172 additions & 22 deletions lld/ELF/SyntheticSections.cpp
Expand Up @@ -3052,33 +3052,183 @@ MipsRldMapSection::MipsRldMapSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, Config->Wordsize,
".rld_map") {}

ARMExidxSentinelSection::ARMExidxSentinelSection()
ARMExidxSyntheticSection::ARMExidxSyntheticSection()
: SyntheticSection(SHF_ALLOC | SHF_LINK_ORDER, SHT_ARM_EXIDX,
Config->Wordsize, ".ARM.exidx") {}

// Write a terminating sentinel entry to the end of the .ARM.exidx table.
// This section will have been sorted last in the .ARM.exidx table.
// This table entry will have the form:
// | PREL31 upper bound of code that has exception tables | EXIDX_CANTUNWIND |
// The sentinel must have the PREL31 value of an address higher than any
// address described by any other table entry.
void ARMExidxSentinelSection::writeTo(uint8_t *Buf) {
assert(Highest);
uint64_t S = Highest->getVA(Highest->getSize());
uint64_t P = getVA();
Target->relocateOne(Buf, R_ARM_PREL31, S - P);
write32le(Buf + 4, 1);
}

// The sentinel has to be removed if there are no other .ARM.exidx entries.
bool ARMExidxSentinelSection::empty() const {
for (InputSection *IS : getInputSections(getParent()))
if (!isa<ARMExidxSentinelSection>(IS))
Config->Wordsize, ".ARM.exidx") {
for (InputSectionBase *&IS : InputSections) {
if (isa<InputSection>(IS) && IS->Type == SHT_ARM_EXIDX) {
ExidxSections.push_back(cast<InputSection>(IS));
IS = nullptr;
} else if (IS->Live && isa<InputSection>(IS) &&
IS->kind() != SectionBase::Synthetic &&
(IS->Flags & SHF_ALLOC) && (IS->Flags & SHF_EXECINSTR) &&
IS->getSize() > 0) {
ExecutableSections.push_back(cast<InputSection>(IS));
}
}
setSizeAndOffsets();

std::vector<InputSectionBase *> &V = InputSections;
V.erase(std::remove(V.begin(), V.end(), nullptr), V.end());
}

static InputSection *findExidxSection(InputSection *IS) {
for (InputSection *D : IS->DependentSections)
if (D->Type == SHT_ARM_EXIDX)
return D;
return nullptr;
}

void ARMExidxSyntheticSection::setSizeAndOffsets() {
size_t Offset = 0;
Size = 0;
for (InputSection *IS : ExecutableSections) {
if (InputSection *D = findExidxSection(IS)) {
D->OutSecOff = Offset;
D->Parent = getParent();
Offset += D->getSize();
Empty = false;
} else {
Offset += 8;
}
}
// Size includes Sentinel.
Size = Offset + 8;
}

// References to .ARM.Extab Sections have bit 31 clear and are not the
// special EXIDX_CANTUNWIND bit-pattern.
static bool isExtabRef(uint32_t Unwind) {
return (Unwind & 0x80000000) == 0 && Unwind != 0x1;
}

// Return true if the .ARM.exidx section Cur can be merged into the .ARM.exidx
// section Prev, where Cur follows Prev in the table. This can be done if the
// unwinding instructions in Cur are identical to Prev. Linker generated
// EXIDX_CANTUNWIND entries are represented by nullptr as they do not have an
// InputSection.
static bool isDuplicateArmExidxSec(InputSection *Prev, InputSection *Cur) {

struct ExidxEntry {
ulittle32_t Fn;
ulittle32_t Unwind;
};
// Get the last table Entry from the previous .ARM.exidx section. If Prev is
// nullptr then it will be a synthesized EXIDX_CANTUNWIND entry.
ExidxEntry PrevEntry = {ulittle32_t(0), ulittle32_t(1)};
if (Prev)
PrevEntry = Prev->getDataAs<ExidxEntry>().back();
if (isExtabRef(PrevEntry.Unwind))
return false;

// We consider the unwind instructions of an .ARM.exidx table entry
// a duplicate if the previous unwind instructions if:
// - Both are the special EXIDX_CANTUNWIND.
// - Both are the same inline unwind instructions.
// We do not attempt to follow and check links into .ARM.extab tables as
// consecutive identical entries are rare and the effort to check that they
// are identical is high.

// If Cur is nullptr then this is synthesized EXIDX_CANTUNWIND entry.
if (Cur == nullptr)
return PrevEntry.Unwind == 1;

for (const ExidxEntry Entry : Cur->getDataAs<ExidxEntry>())
if (isExtabRef(Entry.Unwind) || Entry.Unwind != PrevEntry.Unwind)
return false;

// All table entries in this .ARM.exidx Section can be merged into the
// previous Section.
return true;
}

bool ARMExidxSentinelSection::classof(const SectionBase *D) {
// The .ARM.exidx table must be sorted in ascending order of the address of the
// functions the table describes. Optionally duplicate adjacent table entries
// can be removed. At the end of the function the ExecutableSections must be
// sorted in ascending order of address, Sentinel is set to the InputSection
// with the highest address and any InputSections that have mergeable
// .ARM.exidx table entries are removed from it.
void ARMExidxSyntheticSection::finalizeContents() {
// Sort the executable sections that may or may not have associated
// .ARM.exidx sections by order of ascending address. This requires the
// relative positions of InputSections to be known.
auto CompareByFilePosition = [](const InputSection *A,
const InputSection *B) {
OutputSection *AOut = A->getParent();
OutputSection *BOut = B->getParent();

if (AOut != BOut)
return AOut->SectionIndex < BOut->SectionIndex;
return A->OutSecOff < B->OutSecOff;
};
std::stable_sort(ExecutableSections.begin(), ExecutableSections.end(),
CompareByFilePosition);
Sentinel = ExecutableSections.back();
// Optionally merge adjacent duplicate entries.
if (Config->MergeArmExidx) {
std::vector<InputSection *> SelectedSections;
SelectedSections.reserve(ExecutableSections.size());
SelectedSections.push_back(ExecutableSections[0]);
size_t Prev = 0;
for (size_t I = 1; I < ExecutableSections.size(); ++I) {
InputSection *EX1 = findExidxSection(ExecutableSections[Prev]);
InputSection *EX2 = findExidxSection(ExecutableSections[I]);
if (!isDuplicateArmExidxSec(EX1, EX2)) {
SelectedSections.push_back(ExecutableSections[I]);
Prev = I;
}
}
ExecutableSections = std::move(SelectedSections);
}
setSizeAndOffsets();
}

InputSection *ARMExidxSyntheticSection::getLinkOrderDep() const {
for (InputSection *IS : ExecutableSections)
if (findExidxSection(IS))
return IS;
return nullptr;
}

// To write the .ARM.exidx table from the ExecutableSections we have three cases
// 1.) The InputSection has a .ARM.exidx InputSection in its dependent sections.
// We write the .ARM.exidx section contents and apply its relocations.
// 2.) The InputSection does not have a dependent .ARM.exidx InputSection. We
// must write the contents of an EXIDX_CANTUNWIND directly. We use the
// start of the InputSection as the purpose of the linker generated
// section is to terminate the address range of the previous entry.
// 3.) A trailing EXIDX_CANTUNWIND sentinel section is required at the end of
// the table to terminate the address range of the final entry.
void ARMExidxSyntheticSection::writeTo(uint8_t *Buf) {

const uint8_t CantUnwindData[8] = {0, 0, 0, 0, // PREL31 to target
1, 0, 0, 0}; // EXIDX_CANTUNWIND

uint64_t Offset = 0;
for (InputSection *IS : ExecutableSections) {
assert(IS->getParent() != nullptr);
if (InputSection *D = findExidxSection(IS)) {
memcpy(Buf + Offset, D->data().data(), D->data().size());
D->relocateAlloc(Buf, Buf + D->getSize());
Offset += D->getSize();
} else {
// A Linker generated CANTUNWIND section.
memcpy(Buf + Offset, CantUnwindData, sizeof(CantUnwindData));
uint64_t S = IS->getVA();
uint64_t P = getVA() + Offset;
Target->relocateOne(Buf + Offset, R_ARM_PREL31, S - P);
Offset += 8;
}
}
// Write Sentinel.
memcpy(Buf + Offset, CantUnwindData, sizeof(CantUnwindData));
uint64_t S = Sentinel->getVA(Sentinel->getSize());
uint64_t P = getVA() + Offset;
Target->relocateOne(Buf + Offset, R_ARM_PREL31, S - P);
assert(Size == Offset + 8);
}

bool ARMExidxSyntheticSection::classof(const SectionBase *D) {
return D->kind() == InputSectionBase::Synthetic && D->Type == SHT_ARM_EXIDX;
}

Expand Down
74 changes: 66 additions & 8 deletions lld/ELF/SyntheticSections.h
Expand Up @@ -936,19 +936,76 @@ class MipsRldMapSection : public SyntheticSection {
void writeTo(uint8_t *Buf) override {}
};

class ARMExidxSentinelSection : public SyntheticSection {
// Representation of the combined .ARM.Exidx input sections. We process these
// as a SyntheticSection like .eh_frame as we need to merge duplicate entries
// and add terminating sentinel entries.
//
// The .ARM.exidx input sections after SHF_LINK_ORDER processing is done form
// a table that the unwinder can derive (Addresses are encoded as offsets from
// table):
// | Address of function | Unwind instructions for function |
// where the unwind instructions are either a small number of unwind or the
// special EXIDX_CANTUNWIND entry representing no unwinding information.
// When an exception is thrown from an address A, the unwinder searches the
// table for the closest table entry with Address of function <= A. This means
// that for two consecutive table entries:
// | A1 | U1 |
// | A2 | U2 |
// The range of addresses described by U1 is [A1, A2)
//
// There are two cases where we need a linker generated table entry to fixup
// the address ranges in the table
// Case 1:
// - A sentinel entry added with an address higher than all
// executable sections. This was needed to work around libunwind bug pr31091.
// - After address assignment we need to find the highest addressed executable
// section and use the limit of that section so that the unwinder never
// matches it.
// Case 2:
// - InputSections without a .ARM.exidx section (usually from Assembly)
// need a table entry so that they terminate the range of the previously
// function. This is pr40277.
//
// Instead of storing pointers to the .ARM.exidx InputSections from
// InputObjects, we store pointers to the executable sections that need
// .ARM.exidx sections. We can then use the dependentSections of these to
// either find the .ARM.exidx section or know that we need to generate one.
class ARMExidxSyntheticSection : public SyntheticSection {
public:
ARMExidxSentinelSection();
size_t getSize() const override { return 8; }
ARMExidxSyntheticSection();
size_t getSize() const override { return Size; }
void writeTo(uint8_t *Buf) override;
bool empty() const override;
bool empty() const override { return Empty; }
// Sort and remove duplicate entries.
void finalizeContents() override;
InputSection *getLinkOrderDep() const;

static bool classof(const SectionBase *D);

// The last section referenced by a regular .ARM.exidx section.
// It is found and filled in Writer<ELFT>::resolveShfLinkOrder().
// The sentinel points at the end of that section.
InputSection *Highest = nullptr;
// Links to the ARMExidxSections so we can transfer the relocations once the
// layout is known.
std::vector<InputSection *> ExidxSections;

private:
// Derive Size from contents of ExecutableSections, including any linker
// generated sentinels. Also set the OutSecOff of the ExidxSections.
void setSizeAndOffsets();
size_t Size;

// Empty if ExecutableSections contains no dependent .ARM.exidx sections.
bool Empty = true;

// Instead of storing pointers to the .ARM.exidx InputSections from
// InputObjects, we store pointers to the executable sections that need
// .ARM.exidx sections. We can then use the dependentSections of these to
// either find the .ARM.exidx section or know that we need to generate one.
std::vector<InputSection *> ExecutableSections;

// The executable InputSection with the highest address to use for the
// sentinel. We store separately from ExecutableSections as merging of
// duplicate entries may mean this InputSection is removed from
// ExecutableSections.
InputSection *Sentinel = nullptr;
};

// A container for one or more linker generated thunks. Instances of these
Expand Down Expand Up @@ -1003,6 +1060,7 @@ Defined *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
// Linker generated sections which can be used as inputs.
struct InStruct {
InputSection *ARMAttributes;
ARMExidxSyntheticSection *ARMExidx;
BssSection *Bss;
BssSection *BssRelRo;
BuildIdSection *BuildId;
Expand Down

0 comments on commit d3511a2

Please sign in to comment.