forked from OSchip/llvm-project
[WinEH][ARM64] Split Unwind Info for Fucntions Larger than 1MB
Create function segments and emit unwind info of them. A segment must be less than 1MB and no prolog or epilog is splitted between two segments. This patch should generate correct, though not optimal, unwind info for large functions. Currently it only generate pacted info (.pdata) only for functions that are less than 1MB (single-segment functions). This is NFC from before this patch. The next step is to enable (.pdata) only unwind info for the first segment or segments that have neither prolog or epilog in a multi-segment function. Another future work item is to further split segments that require more than 255 code words or have more than 65535 epilogs. Reference: https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#function-fragments Differential Revision: https://reviews.llvm.org/D130049
This commit is contained in:
parent
f1eb945f9a
commit
99e50e5838
|
@ -46,6 +46,7 @@ struct FrameInfo {
|
|||
const MCSymbol *Symbol = nullptr;
|
||||
MCSection *TextSection = nullptr;
|
||||
uint32_t PackedInfo = 0;
|
||||
uint32_t PrologCodeBytes = 0;
|
||||
|
||||
bool HandlesUnwind = false;
|
||||
bool HandlesExceptions = false;
|
||||
|
@ -62,6 +63,21 @@ struct FrameInfo {
|
|||
};
|
||||
MapVector<MCSymbol *, Epilog> EpilogMap;
|
||||
|
||||
// For splitting unwind info of large functions
|
||||
struct Segment {
|
||||
int64_t Offset;
|
||||
int64_t Length;
|
||||
bool HasProlog;
|
||||
MCSymbol *Symbol;
|
||||
// Map an Epilog's symbol to its offset within the function.
|
||||
MapVector<MCSymbol *, int64_t> Epilogs;
|
||||
|
||||
Segment(int64_t Offset, int64_t Length, bool HasProlog = false)
|
||||
: Offset(Offset), Length(Length), HasProlog(HasProlog) {}
|
||||
};
|
||||
|
||||
std::vector<Segment> Segments;
|
||||
|
||||
FrameInfo() = default;
|
||||
FrameInfo(const MCSymbol *Function, const MCSymbol *BeginFuncEHLabel)
|
||||
: Begin(BeginFuncEHLabel), Function(Function) {}
|
||||
|
|
|
@ -128,6 +128,17 @@ static void EmitUnwindCode(MCStreamer &streamer, const MCSymbol *begin,
|
|||
}
|
||||
}
|
||||
|
||||
static void EmitSymbolRefWithOfs(MCStreamer &streamer,
|
||||
const MCSymbol *Base,
|
||||
int64_t Offset) {
|
||||
MCContext &Context = streamer.getContext();
|
||||
const MCConstantExpr *OffExpr = MCConstantExpr::create(Offset, Context);
|
||||
const MCSymbolRefExpr *BaseRefRel = MCSymbolRefExpr::create(Base,
|
||||
MCSymbolRefExpr::VK_COFF_IMGREL32,
|
||||
Context);
|
||||
streamer.emitValue(MCBinaryExpr::createAdd(BaseRefRel, OffExpr, Context), 4);
|
||||
}
|
||||
|
||||
static void EmitSymbolRefWithOfs(MCStreamer &streamer,
|
||||
const MCSymbol *Base,
|
||||
const MCSymbol *Other) {
|
||||
|
@ -642,26 +653,29 @@ getARM64OffsetInProlog(const std::vector<WinEH::Instruction> &Prolog,
|
|||
return -1;
|
||||
}
|
||||
|
||||
// If the epilog was a subset of the prolog, find its offset.
|
||||
if (Epilog.size() == Prolog.size())
|
||||
return 0;
|
||||
|
||||
// If the epilog was a subset of the prolog, find its offset.
|
||||
return ARM64CountOfUnwindCodes(ArrayRef<WinEH::Instruction>(
|
||||
&Prolog[Epilog.size()], Prolog.size() - Epilog.size()));
|
||||
}
|
||||
|
||||
static int checkARM64PackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info,
|
||||
WinEH::FrameInfo::Segment *Seg,
|
||||
int PrologCodeBytes) {
|
||||
// Can only pack if there's one single epilog
|
||||
if (info->EpilogMap.size() != 1)
|
||||
if (Seg->Epilogs.size() != 1)
|
||||
return -1;
|
||||
|
||||
MCSymbol *Sym = Seg->Epilogs.begin()->first;
|
||||
const std::vector<WinEH::Instruction> &Epilog =
|
||||
info->EpilogMap.begin()->second.Instructions;
|
||||
info->EpilogMap[Sym].Instructions;
|
||||
|
||||
// Check that the epilog actually is at the very end of the function,
|
||||
// otherwise it can't be packed.
|
||||
uint32_t DistanceFromEnd = (uint32_t)GetAbsDifference(
|
||||
streamer, info->FuncletOrFuncEnd, info->EpilogMap.begin()->first);
|
||||
uint32_t DistanceFromEnd =
|
||||
(uint32_t)(Seg->Offset + Seg->Length - Seg->Epilogs.begin()->second);
|
||||
if (DistanceFromEnd / 4 != Epilog.size())
|
||||
return -1;
|
||||
|
||||
|
@ -686,7 +700,7 @@ static int checkARM64PackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info,
|
|||
|
||||
// As we choose to express the epilog as part of the prolog, remove the
|
||||
// epilog from the map, so we don't try to emit its opcodes.
|
||||
info->EpilogMap.clear();
|
||||
info->EpilogMap.erase(Sym);
|
||||
return Offset;
|
||||
}
|
||||
|
||||
|
@ -923,6 +937,268 @@ static bool tryARM64PackedUnwind(WinEH::FrameInfo *info, uint32_t FuncLength,
|
|||
return true;
|
||||
}
|
||||
|
||||
static void ARM64ProcessEpilogs(WinEH::FrameInfo *info,
|
||||
WinEH::FrameInfo::Segment *Seg,
|
||||
uint32_t &TotalCodeBytes,
|
||||
MapVector<MCSymbol *, uint32_t> &EpilogInfo) {
|
||||
|
||||
std::vector<MCSymbol *> EpilogStarts;
|
||||
for (auto &I : Seg->Epilogs)
|
||||
EpilogStarts.push_back(I.first);
|
||||
|
||||
// Epilogs processed so far.
|
||||
std::vector<MCSymbol *> AddedEpilogs;
|
||||
for (auto S : EpilogStarts) {
|
||||
MCSymbol *EpilogStart = S;
|
||||
auto &EpilogInstrs = info->EpilogMap[S].Instructions;
|
||||
uint32_t CodeBytes = ARM64CountOfUnwindCodes(EpilogInstrs);
|
||||
|
||||
MCSymbol* MatchingEpilog =
|
||||
FindMatchingEpilog(EpilogInstrs, AddedEpilogs, info);
|
||||
int PrologOffset;
|
||||
if (MatchingEpilog) {
|
||||
assert(EpilogInfo.find(MatchingEpilog) != EpilogInfo.end() &&
|
||||
"Duplicate epilog not found");
|
||||
EpilogInfo[EpilogStart] = EpilogInfo.lookup(MatchingEpilog);
|
||||
// Clear the unwind codes in the EpilogMap, so that they don't get output
|
||||
// in ARM64EmitUnwindInfoForSegment().
|
||||
EpilogInstrs.clear();
|
||||
} else if ((PrologOffset = getARM64OffsetInProlog(info->Instructions,
|
||||
EpilogInstrs)) >= 0) {
|
||||
EpilogInfo[EpilogStart] = PrologOffset;
|
||||
// If the segment doesn't have a prolog, an end_c will be emitted before
|
||||
// prolog opcodes. So epilog start index in opcodes array is moved by 1.
|
||||
if (!Seg->HasProlog)
|
||||
EpilogInfo[EpilogStart] += 1;
|
||||
// Clear the unwind codes in the EpilogMap, so that they don't get output
|
||||
// in ARM64EmitUnwindInfoForSegment().
|
||||
EpilogInstrs.clear();
|
||||
} else {
|
||||
EpilogInfo[EpilogStart] = TotalCodeBytes;
|
||||
TotalCodeBytes += CodeBytes;
|
||||
AddedEpilogs.push_back(EpilogStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ARM64FindSegmentsInFunction(MCStreamer &streamer,
|
||||
WinEH::FrameInfo *info,
|
||||
int64_t RawFuncLength) {
|
||||
struct EpilogStartEnd {
|
||||
MCSymbol *Start;
|
||||
int64_t Offset;
|
||||
int64_t End;
|
||||
};
|
||||
// Record Start and End of each epilog.
|
||||
SmallVector<struct EpilogStartEnd, 4> Epilogs;
|
||||
for (auto &I : info->EpilogMap) {
|
||||
MCSymbol *Start = I.first;
|
||||
auto &Instrs = I.second.Instructions;
|
||||
int64_t Offset = GetAbsDifference(streamer, Start, info->Begin);
|
||||
assert((Epilogs.size() == 0 || Offset >= Epilogs.back().End) &&
|
||||
"Epilogs should be monotonically ordered");
|
||||
Epilogs.push_back({Start, Offset, Offset + (int64_t)Instrs.size() * 4});
|
||||
}
|
||||
|
||||
unsigned E = 0;
|
||||
int64_t SegLimit = 0xFFFFC;
|
||||
int64_t SegOffset = 0;
|
||||
|
||||
if (RawFuncLength > SegLimit) {
|
||||
|
||||
int64_t RemainingLength = RawFuncLength;
|
||||
|
||||
while (RemainingLength > SegLimit) {
|
||||
// Try divide the function into segments, requirements:
|
||||
// 1. Segment length <= 0xFFFFC;
|
||||
// 2. Each Prologue or Epilogue must be fully within a segment.
|
||||
int64_t SegLength = SegLimit;
|
||||
int64_t SegEnd = SegOffset + SegLength;
|
||||
// Keep record on symbols and offsets of epilogs in this segment.
|
||||
MapVector<MCSymbol *, int64_t> EpilogsInSegment;
|
||||
|
||||
while (E < Epilogs.size() && Epilogs[E].End < SegEnd) {
|
||||
// Epilogs within current segment.
|
||||
EpilogsInSegment[Epilogs[E].Start] = Epilogs[E].Offset;
|
||||
++E;
|
||||
}
|
||||
|
||||
// At this point, we have:
|
||||
// 1. Put all epilogs in segments already. No action needed here; or
|
||||
// 2. Found an epilog that will cross segments boundry. We need to
|
||||
// move back current segment's end boundry, so the epilog is entirely
|
||||
// in the next segment; or
|
||||
// 3. Left at least one epilog that is entirely after this segment.
|
||||
// It'll be handled by the next iteration, or the last segment.
|
||||
if (E < Epilogs.size() && Epilogs[E].Offset <= SegEnd)
|
||||
// Move back current Segment's end boundry.
|
||||
SegLength = Epilogs[E].Offset - SegOffset;
|
||||
|
||||
auto Seg = WinEH::FrameInfo::Segment(
|
||||
SegOffset, SegLength, /* HasProlog */!SegOffset);
|
||||
Seg.Epilogs = std::move(EpilogsInSegment);
|
||||
info->Segments.push_back(Seg);
|
||||
|
||||
SegOffset += SegLength;
|
||||
RemainingLength -= SegLength;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the last segment when RawFuncLength > 0xFFFFC,
|
||||
// or the only segment otherwise.
|
||||
auto LastSeg =
|
||||
WinEH::FrameInfo::Segment(SegOffset, RawFuncLength - SegOffset,
|
||||
/* HasProlog */!SegOffset);
|
||||
for (; E < Epilogs.size(); ++E)
|
||||
LastSeg.Epilogs[Epilogs[E].Start] = Epilogs[E].Offset;
|
||||
info->Segments.push_back(LastSeg);
|
||||
}
|
||||
|
||||
static void ARM64EmitUnwindInfoForSegment(MCStreamer &streamer,
|
||||
WinEH::FrameInfo *info,
|
||||
WinEH::FrameInfo::Segment &Seg,
|
||||
bool TryPacked = true) {
|
||||
MCContext &context = streamer.getContext();
|
||||
MCSymbol *Label = context.createTempSymbol();
|
||||
|
||||
streamer.emitValueToAlignment(4);
|
||||
streamer.emitLabel(Label);
|
||||
Seg.Symbol = Label;
|
||||
// Use the 1st segemnt's label as function's.
|
||||
if (Seg.Offset == 0)
|
||||
info->Symbol = Label;
|
||||
|
||||
bool HasProlog = Seg.HasProlog;
|
||||
bool HasEpilogs = (Seg.Epilogs.size() != 0);
|
||||
|
||||
uint32_t SegLength = (uint32_t)Seg.Length / 4;
|
||||
uint32_t PrologCodeBytes = info->PrologCodeBytes;
|
||||
|
||||
int PackedEpilogOffset = HasEpilogs ?
|
||||
checkARM64PackedEpilog(streamer, info, &Seg, PrologCodeBytes) : -1;
|
||||
|
||||
// TODO:
|
||||
// 1. Enable packed unwind info (.pdata only) for multi-segment functions.
|
||||
// 2. Emit packed unwind info (.pdata only) for segments that have neithor
|
||||
// prolog nor epilog.
|
||||
if (info->Segments.size() == 1 && PackedEpilogOffset >= 0 &&
|
||||
uint32_t(PackedEpilogOffset) < PrologCodeBytes &&
|
||||
!info->HandlesExceptions && SegLength <= 0x7ff && TryPacked) {
|
||||
// Matching prolog/epilog and no exception handlers; check if the
|
||||
// prolog matches the patterns that can be described by the packed
|
||||
// format.
|
||||
|
||||
// info->Symbol was already set even if we didn't actually write any
|
||||
// unwind info there. Keep using that as indicator that this unwind
|
||||
// info has been generated already.
|
||||
if (tryARM64PackedUnwind(info, SegLength, PackedEpilogOffset))
|
||||
return;
|
||||
}
|
||||
|
||||
// If the prolog is not in this segment, we need to emit an end_c, which takes
|
||||
// 1 byte, before prolog unwind ops.
|
||||
if (!HasProlog) {
|
||||
PrologCodeBytes += 1;
|
||||
if (PackedEpilogOffset >= 0)
|
||||
PackedEpilogOffset += 1;
|
||||
// If a segment has neither prolog nor epilog, "With full .xdata record,
|
||||
// Epilog Count = 1. Epilog Start Index points to end_c."
|
||||
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#function-fragments
|
||||
// TODO: We can remove this if testing shows zero epilog scope is ok with
|
||||
// MS unwinder.
|
||||
if (!HasEpilogs)
|
||||
// Pack the fake epilog into phantom prolog.
|
||||
PackedEpilogOffset = 0;
|
||||
}
|
||||
|
||||
uint32_t TotalCodeBytes = PrologCodeBytes;
|
||||
|
||||
// Process epilogs.
|
||||
MapVector<MCSymbol *, uint32_t> EpilogInfo;
|
||||
ARM64ProcessEpilogs(info, &Seg, TotalCodeBytes, EpilogInfo);
|
||||
|
||||
// Code Words, Epilog count, E, X, Vers, Function Length
|
||||
uint32_t row1 = 0x0;
|
||||
uint32_t CodeWords = TotalCodeBytes / 4;
|
||||
uint32_t CodeWordsMod = TotalCodeBytes % 4;
|
||||
if (CodeWordsMod)
|
||||
CodeWords++;
|
||||
uint32_t EpilogCount =
|
||||
PackedEpilogOffset >= 0 ? PackedEpilogOffset : Seg.Epilogs.size();
|
||||
bool ExtensionWord = EpilogCount > 31 || TotalCodeBytes > 124;
|
||||
if (!ExtensionWord) {
|
||||
row1 |= (EpilogCount & 0x1F) << 22;
|
||||
row1 |= (CodeWords & 0x1F) << 27;
|
||||
}
|
||||
if (info->HandlesExceptions) // X
|
||||
row1 |= 1 << 20;
|
||||
if (PackedEpilogOffset >= 0) // E
|
||||
row1 |= 1 << 21;
|
||||
row1 |= SegLength & 0x3FFFF;
|
||||
streamer.emitInt32(row1);
|
||||
|
||||
// Extended Code Words, Extended Epilog Count
|
||||
if (ExtensionWord) {
|
||||
// FIXME: We should be able to split unwind info into multiple sections.
|
||||
if (CodeWords > 0xFF || EpilogCount > 0xFFFF)
|
||||
report_fatal_error(
|
||||
"SEH unwind data splitting is only implemnted for large functions, "
|
||||
"cases of too many code words or too many epilogs will be done later"
|
||||
);
|
||||
uint32_t row2 = 0x0;
|
||||
row2 |= (CodeWords & 0xFF) << 16;
|
||||
row2 |= (EpilogCount & 0xFFFF);
|
||||
streamer.emitInt32(row2);
|
||||
}
|
||||
|
||||
if (PackedEpilogOffset < 0) {
|
||||
// Epilog Start Index, Epilog Start Offset
|
||||
for (auto &I : EpilogInfo) {
|
||||
MCSymbol *EpilogStart = I.first;
|
||||
uint32_t EpilogIndex = I.second;
|
||||
// Epilog offset within the Segment.
|
||||
uint32_t EpilogOffset = (uint32_t)(Seg.Epilogs[EpilogStart] - Seg.Offset);
|
||||
if (EpilogOffset)
|
||||
EpilogOffset /= 4;
|
||||
uint32_t row3 = EpilogOffset;
|
||||
row3 |= (EpilogIndex & 0x3FF) << 22;
|
||||
streamer.emitInt32(row3);
|
||||
}
|
||||
}
|
||||
|
||||
// Note that even for segments that have no prolog, we still need to emit
|
||||
// prolog unwinding opcodes so that the unwinder knows how to unwind from
|
||||
// such a segment.
|
||||
// The end_c opcode at the start indicates to the unwinder that the actual
|
||||
// prolog is outside of the current segment, and the unwinder shouldn't try
|
||||
// to check for unwinding from a partial prolog.
|
||||
if (!HasProlog)
|
||||
// Emit an end_c.
|
||||
streamer.emitInt8((uint8_t)0xE5);
|
||||
|
||||
// Emit prolog unwind instructions (in reverse order).
|
||||
for (auto Inst : llvm::reverse(info->Instructions))
|
||||
ARM64EmitUnwindCode(streamer, Inst);
|
||||
|
||||
// Emit epilog unwind instructions
|
||||
for (auto &I : Seg.Epilogs) {
|
||||
auto &EpilogInstrs = info->EpilogMap[I.first].Instructions;
|
||||
for (const WinEH::Instruction &inst : EpilogInstrs)
|
||||
ARM64EmitUnwindCode(streamer, inst);
|
||||
}
|
||||
|
||||
int32_t BytesMod = CodeWords * 4 - TotalCodeBytes;
|
||||
assert(BytesMod >= 0);
|
||||
for (int i = 0; i < BytesMod; i++)
|
||||
streamer.emitInt8(0xE3);
|
||||
|
||||
if (info->HandlesExceptions)
|
||||
streamer.emitValue(
|
||||
MCSymbolRefExpr::create(info->ExceptionHandler,
|
||||
MCSymbolRefExpr::VK_COFF_IMGREL32, context),
|
||||
4);
|
||||
}
|
||||
|
||||
// Populate the .xdata section. The format of .xdata on ARM64 is documented at
|
||||
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
|
||||
static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
|
||||
|
@ -956,13 +1232,6 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
|
|||
for (auto &I : info->EpilogMap)
|
||||
simplifyARM64Opcodes(I.second.Instructions, true);
|
||||
|
||||
MCContext &context = streamer.getContext();
|
||||
MCSymbol *Label = context.createTempSymbol();
|
||||
|
||||
streamer.emitValueToAlignment(4);
|
||||
streamer.emitLabel(Label);
|
||||
info->Symbol = Label;
|
||||
|
||||
int64_t RawFuncLength;
|
||||
if (!info->FuncletOrFuncEnd) {
|
||||
report_fatal_error("FuncletOrFuncEnd not set");
|
||||
|
@ -996,134 +1265,15 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
|
|||
RawFuncLength = GetAbsDifference(streamer, info->FuncletOrFuncEnd,
|
||||
info->Begin);
|
||||
}
|
||||
if (RawFuncLength > 0xFFFFF)
|
||||
report_fatal_error("SEH unwind data splitting not yet implemented");
|
||||
uint32_t FuncLength = (uint32_t)RawFuncLength / 4;
|
||||
uint32_t PrologCodeBytes = ARM64CountOfUnwindCodes(info->Instructions);
|
||||
uint32_t TotalCodeBytes = PrologCodeBytes;
|
||||
|
||||
int PackedEpilogOffset =
|
||||
checkARM64PackedEpilog(streamer, info, PrologCodeBytes);
|
||||
ARM64FindSegmentsInFunction(streamer, info, RawFuncLength);
|
||||
|
||||
if (PackedEpilogOffset >= 0 &&
|
||||
uint32_t(PackedEpilogOffset) < PrologCodeBytes &&
|
||||
!info->HandlesExceptions && FuncLength <= 0x7ff && TryPacked) {
|
||||
// Matching prolog/epilog and no exception handlers; check if the
|
||||
// prolog matches the patterns that can be described by the packed
|
||||
// format.
|
||||
info->PrologCodeBytes = ARM64CountOfUnwindCodes(info->Instructions);
|
||||
for (auto &S : info->Segments)
|
||||
ARM64EmitUnwindInfoForSegment(streamer, info, S, TryPacked);
|
||||
|
||||
// info->Symbol was already set even if we didn't actually write any
|
||||
// unwind info there. Keep using that as indicator that this unwind
|
||||
// info has been generated already.
|
||||
|
||||
if (tryARM64PackedUnwind(info, FuncLength, PackedEpilogOffset))
|
||||
return;
|
||||
}
|
||||
|
||||
// Process epilogs.
|
||||
MapVector<MCSymbol *, uint32_t> EpilogInfo;
|
||||
// Epilogs processed so far.
|
||||
std::vector<MCSymbol *> AddedEpilogs;
|
||||
|
||||
for (auto &I : info->EpilogMap) {
|
||||
MCSymbol *EpilogStart = I.first;
|
||||
auto &EpilogInstrs = I.second.Instructions;
|
||||
uint32_t CodeBytes = ARM64CountOfUnwindCodes(EpilogInstrs);
|
||||
|
||||
MCSymbol* MatchingEpilog =
|
||||
FindMatchingEpilog(EpilogInstrs, AddedEpilogs, info);
|
||||
int PrologOffset;
|
||||
if (MatchingEpilog) {
|
||||
assert(EpilogInfo.find(MatchingEpilog) != EpilogInfo.end() &&
|
||||
"Duplicate epilog not found");
|
||||
EpilogInfo[EpilogStart] = EpilogInfo.lookup(MatchingEpilog);
|
||||
// Clear the unwind codes in the EpilogMap, so that they don't get output
|
||||
// in the logic below.
|
||||
EpilogInstrs.clear();
|
||||
} else if ((PrologOffset = getARM64OffsetInProlog(info->Instructions,
|
||||
EpilogInstrs)) >= 0) {
|
||||
EpilogInfo[EpilogStart] = PrologOffset;
|
||||
// Clear the unwind codes in the EpilogMap, so that they don't get output
|
||||
// in the logic below.
|
||||
EpilogInstrs.clear();
|
||||
} else {
|
||||
EpilogInfo[EpilogStart] = TotalCodeBytes;
|
||||
TotalCodeBytes += CodeBytes;
|
||||
AddedEpilogs.push_back(EpilogStart);
|
||||
}
|
||||
}
|
||||
|
||||
// Code Words, Epilog count, E, X, Vers, Function Length
|
||||
uint32_t row1 = 0x0;
|
||||
uint32_t CodeWords = TotalCodeBytes / 4;
|
||||
uint32_t CodeWordsMod = TotalCodeBytes % 4;
|
||||
if (CodeWordsMod)
|
||||
CodeWords++;
|
||||
uint32_t EpilogCount =
|
||||
PackedEpilogOffset >= 0 ? PackedEpilogOffset : info->EpilogMap.size();
|
||||
bool ExtensionWord = EpilogCount > 31 || TotalCodeBytes > 124;
|
||||
if (!ExtensionWord) {
|
||||
row1 |= (EpilogCount & 0x1F) << 22;
|
||||
row1 |= (CodeWords & 0x1F) << 27;
|
||||
}
|
||||
if (info->HandlesExceptions) // X
|
||||
row1 |= 1 << 20;
|
||||
if (PackedEpilogOffset >= 0) // E
|
||||
row1 |= 1 << 21;
|
||||
row1 |= FuncLength & 0x3FFFF;
|
||||
streamer.emitInt32(row1);
|
||||
|
||||
// Extended Code Words, Extended Epilog Count
|
||||
if (ExtensionWord) {
|
||||
// FIXME: We should be able to split unwind info into multiple sections.
|
||||
if (CodeWords > 0xFF || EpilogCount > 0xFFFF)
|
||||
report_fatal_error("SEH unwind data splitting not yet implemented");
|
||||
uint32_t row2 = 0x0;
|
||||
row2 |= (CodeWords & 0xFF) << 16;
|
||||
row2 |= (EpilogCount & 0xFFFF);
|
||||
streamer.emitInt32(row2);
|
||||
}
|
||||
|
||||
if (PackedEpilogOffset < 0) {
|
||||
// Epilog Start Index, Epilog Start Offset
|
||||
for (auto &I : EpilogInfo) {
|
||||
MCSymbol *EpilogStart = I.first;
|
||||
uint32_t EpilogIndex = I.second;
|
||||
uint32_t EpilogOffset =
|
||||
(uint32_t)GetAbsDifference(streamer, EpilogStart, info->Begin);
|
||||
if (EpilogOffset)
|
||||
EpilogOffset /= 4;
|
||||
uint32_t row3 = EpilogOffset;
|
||||
row3 |= (EpilogIndex & 0x3FF) << 22;
|
||||
streamer.emitInt32(row3);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit prolog unwind instructions (in reverse order).
|
||||
uint8_t numInst = info->Instructions.size();
|
||||
for (uint8_t c = 0; c < numInst; ++c) {
|
||||
WinEH::Instruction inst = info->Instructions.back();
|
||||
info->Instructions.pop_back();
|
||||
ARM64EmitUnwindCode(streamer, inst);
|
||||
}
|
||||
|
||||
// Emit epilog unwind instructions
|
||||
for (auto &I : info->EpilogMap) {
|
||||
auto &EpilogInstrs = I.second.Instructions;
|
||||
for (const WinEH::Instruction &inst : EpilogInstrs)
|
||||
ARM64EmitUnwindCode(streamer, inst);
|
||||
}
|
||||
|
||||
int32_t BytesMod = CodeWords * 4 - TotalCodeBytes;
|
||||
assert(BytesMod >= 0);
|
||||
for (int i = 0; i < BytesMod; i++)
|
||||
streamer.emitInt8(0xE3);
|
||||
|
||||
if (info->HandlesExceptions)
|
||||
streamer.emitValue(
|
||||
MCSymbolRefExpr::create(info->ExceptionHandler,
|
||||
MCSymbolRefExpr::VK_COFF_IMGREL32, context),
|
||||
4);
|
||||
// Clear prolog instructions after unwind info is emitted for all segments.
|
||||
info->Instructions.clear();
|
||||
}
|
||||
|
||||
static uint32_t ARMCountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns) {
|
||||
|
@ -2205,6 +2355,24 @@ static void ARMEmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
|
|||
4);
|
||||
}
|
||||
|
||||
static void ARM64EmitRuntimeFunction(MCStreamer &streamer,
|
||||
const WinEH::FrameInfo *info) {
|
||||
MCContext &context = streamer.getContext();
|
||||
|
||||
streamer.emitValueToAlignment(4);
|
||||
for (auto &S : info->Segments) {
|
||||
EmitSymbolRefWithOfs(streamer, info->Begin, S.Offset);
|
||||
if (info->PackedInfo)
|
||||
streamer.emitInt32(info->PackedInfo);
|
||||
else
|
||||
streamer.emitValue(
|
||||
MCSymbolRefExpr::create(S.Symbol, MCSymbolRefExpr::VK_COFF_IMGREL32,
|
||||
context),
|
||||
4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ARMEmitRuntimeFunction(MCStreamer &streamer,
|
||||
const WinEH::FrameInfo *info) {
|
||||
MCContext &context = streamer.getContext();
|
||||
|
@ -2241,7 +2409,7 @@ void llvm::Win64EH::ARM64UnwindEmitter::Emit(MCStreamer &Streamer) const {
|
|||
continue;
|
||||
MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection);
|
||||
Streamer.switchSection(PData);
|
||||
ARMEmitRuntimeFunction(Streamer, Info);
|
||||
ARM64EmitRuntimeFunction(Streamer, Info);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,308 @@
|
|||
// This test checks that we emit unwind info correctly for epilogs that:
|
||||
// 1. mirror the prolog; or
|
||||
// 2. are subsequence at the end of the prolog; or
|
||||
// 3. neither of above two.
|
||||
// in the same segment.
|
||||
// RUN: llvm-mc -triple aarch64-pc-win32 -filetype=obj %s -o %t.o
|
||||
// RUN: llvm-readobj -S -r -u %t.o | FileCheck %s
|
||||
|
||||
// CHECK: Section {
|
||||
// CHECK: Number: 4
|
||||
// CHECK-NEXT: Name: .xdata (2E 78 64 61 74 61 00 00)
|
||||
// CHECK-NEXT: VirtualSize: 0x0
|
||||
// CHECK-NEXT: VirtualAddress: 0x0
|
||||
// CHECK-NEXT: RawDataSize: 80
|
||||
// CHECK-NEXT: PointerToRawData: 0x1251AC
|
||||
// CHECK-NEXT: PointerToRelocations: 0x0
|
||||
// CHECK-NEXT: PointerToLineNumbers: 0x0
|
||||
// CHECK-NEXT: RelocationCount: 0
|
||||
// CHECK-NEXT: LineNumberCount: 0
|
||||
// CHECK-NEXT: Characteristics [ (0x40300040)
|
||||
// CHECK-NEXT: IMAGE_SCN_ALIGN_4BYTES (0x300000)
|
||||
// CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
|
||||
// CHECK-NEXT: IMAGE_SCN_MEM_READ (0x40000000)
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: Section {
|
||||
// CHECK-NEXT: Number: 5
|
||||
// CHECK-NEXT: Name: .pdata (2E 70 64 61 74 61 00 00)
|
||||
// CHECK-NEXT: VirtualSize: 0x0
|
||||
// CHECK-NEXT: VirtualAddress: 0x0
|
||||
// CHECK-NEXT: RawDataSize: 16
|
||||
// CHECK-NEXT: PointerToRawData: 0x1251FC
|
||||
// CHECK-NEXT: PointerToRelocations: 0x12520C
|
||||
// CHECK-NEXT: PointerToLineNumbers: 0x0
|
||||
// CHECK-NEXT: RelocationCount: 4
|
||||
// CHECK-NEXT: LineNumberCount: 0
|
||||
// CHECK-NEXT: Characteristics [ (0x40300040)
|
||||
// CHECK-NEXT: IMAGE_SCN_ALIGN_4BYTES (0x300000)
|
||||
// CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
|
||||
// CHECK-NEXT: IMAGE_SCN_MEM_READ (0x40000000)
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT:]
|
||||
// CHECK-LABEL:Relocations [
|
||||
// CHECK-NEXT: Section (1) .text {
|
||||
// CHECK-NEXT: 0x94 IMAGE_REL_ARM64_BRANCH26 foo (12)
|
||||
// CHECK-NEXT: 0x125068 IMAGE_REL_ARM64_BRANCH26 foo (12)
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: Section (5) .pdata {
|
||||
// CHECK-NEXT: 0x0 IMAGE_REL_ARM64_ADDR32NB .text (0)
|
||||
// CHECK-NEXT: 0x4 IMAGE_REL_ARM64_ADDR32NB .xdata (7)
|
||||
// CHECK-NEXT: 0x8 IMAGE_REL_ARM64_ADDR32NB .text (0)
|
||||
// CHECK-NEXT: 0xC IMAGE_REL_ARM64_ADDR32NB .xdata (7)
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT:]
|
||||
// CHECK-LABEL:UnwindInformation [
|
||||
// CHECK-NEXT: RuntimeFunction {
|
||||
// CHECK-NEXT: Function: multi_epilog (0x0)
|
||||
// CHECK-NEXT: ExceptionRecord: .xdata (0x0)
|
||||
// CHECK-NEXT: ExceptionData {
|
||||
// CHECK-NEXT: FunctionLength: 1048572
|
||||
// CHECK-NEXT: Version: 0
|
||||
// CHECK-NEXT: ExceptionData: No
|
||||
// CHECK-NEXT: EpiloguePacked: No
|
||||
// CHECK-NEXT: EpilogueScopes: 3
|
||||
// CHECK-NEXT: ByteCodeLength: 24
|
||||
// CHECK-NEXT: Prologue [
|
||||
// CHECK-NEXT: 0xe1 ; mov fp, sp
|
||||
// CHECK-NEXT: 0xca16 ; stp x27, x28, [sp, #176]
|
||||
// CHECK-NEXT: 0xc998 ; stp x25, x26, [sp, #192]
|
||||
// CHECK-NEXT: 0xc91a ; stp x23, x24, [sp, #208]
|
||||
// CHECK-NEXT: 0xc89c ; stp x21, x22, [sp, #224]
|
||||
// CHECK-NEXT: 0xc81e ; stp x19, x20, [sp, #240]
|
||||
// CHECK-NEXT: 0x9f ; stp x29, x30, [sp, #-256]!
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: EpilogueScopes [
|
||||
// CHECK-NEXT: EpilogueScope {
|
||||
// CHECK-NEXT: StartOffset: 38
|
||||
// CHECK-NEXT: EpilogueStartIndex: 0
|
||||
// CHECK-NEXT: Opcodes [
|
||||
// CHECK-NEXT: 0xe1 ; mov sp, fp
|
||||
// CHECK-NEXT: 0xca16 ; ldp x27, x28, [sp, #176]
|
||||
// CHECK-NEXT: 0xc998 ; ldp x25, x26, [sp, #192]
|
||||
// CHECK-NEXT: 0xc91a ; ldp x23, x24, [sp, #208]
|
||||
// CHECK-NEXT: 0xc89c ; ldp x21, x22, [sp, #224]
|
||||
// CHECK-NEXT: 0xc81e ; ldp x19, x20, [sp, #240]
|
||||
// CHECK-NEXT: 0x9f ; ldp x29, x30, [sp], #256
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: EpilogueScope {
|
||||
// CHECK-NEXT: StartOffset: 46
|
||||
// CHECK-NEXT: EpilogueStartIndex: 3
|
||||
// CHECK-NEXT: Opcodes [
|
||||
// CHECK-NEXT: 0xc998 ; ldp x25, x26, [sp, #192]
|
||||
// CHECK-NEXT: 0xc91a ; ldp x23, x24, [sp, #208]
|
||||
// CHECK-NEXT: 0xc89c ; ldp x21, x22, [sp, #224]
|
||||
// CHECK-NEXT: 0xc81e ; ldp x19, x20, [sp, #240]
|
||||
// CHECK-NEXT: 0x9f ; ldp x29, x30, [sp], #256
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: EpilogueScope {
|
||||
// CHECK-NEXT: StartOffset: 52
|
||||
// CHECK-NEXT: EpilogueStartIndex: 13
|
||||
// CHECK-NEXT: Opcodes [
|
||||
// CHECK-NEXT: 0xe1 ; mov sp, fp
|
||||
// CHECK-NEXT: 0xc91a ; ldp x23, x24, [sp, #208]
|
||||
// CHECK-NEXT: 0xc89c ; ldp x21, x22, [sp, #224]
|
||||
// CHECK-NEXT: 0xc81e ; ldp x19, x20, [sp, #240]
|
||||
// CHECK-NEXT: 0x9f ; ldp x29, x30, [sp], #256
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: RuntimeFunction {
|
||||
// CHECK-NEXT: Function: multi_epilog +0xFFFFC (0xFFFFC)
|
||||
// CHECK-NEXT: ExceptionRecord: .xdata +0x28 (0x28)
|
||||
// CHECK-NEXT: ExceptionData {
|
||||
// CHECK-NEXT: FunctionLength: 151744
|
||||
// CHECK-NEXT: Version: 0
|
||||
// CHECK-NEXT: ExceptionData: No
|
||||
// CHECK-NEXT: EpiloguePacked: No
|
||||
// CHECK-NEXT: EpilogueScopes: 3
|
||||
// CHECK-NEXT: ByteCodeLength: 24
|
||||
// CHECK-NEXT: Prologue [
|
||||
// CHECK-NEXT: 0xe5 ; end_c
|
||||
// CHECK-NEXT: 0xe1 ; mov fp, sp
|
||||
// CHECK-NEXT: 0xca16 ; stp x27, x28, [sp, #176]
|
||||
// CHECK-NEXT: 0xc998 ; stp x25, x26, [sp, #192]
|
||||
// CHECK-NEXT: 0xc91a ; stp x23, x24, [sp, #208]
|
||||
// CHECK-NEXT: 0xc89c ; stp x21, x22, [sp, #224]
|
||||
// CHECK-NEXT: 0xc81e ; stp x19, x20, [sp, #240]
|
||||
// CHECK-NEXT: 0x9f ; stp x29, x30, [sp, #-256]!
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: EpilogueScopes [
|
||||
// CHECK-NEXT: EpilogueScope {
|
||||
// CHECK-NEXT: StartOffset: 37916
|
||||
// CHECK-NEXT: EpilogueStartIndex: 1
|
||||
// CHECK-NEXT: Opcodes [
|
||||
// CHECK-NEXT: 0xe1 ; mov sp, fp
|
||||
// CHECK-NEXT: 0xca16 ; ldp x27, x28, [sp, #176]
|
||||
// CHECK-NEXT: 0xc998 ; ldp x25, x26, [sp, #192]
|
||||
// CHECK-NEXT: 0xc91a ; ldp x23, x24, [sp, #208]
|
||||
// CHECK-NEXT: 0xc89c ; ldp x21, x22, [sp, #224]
|
||||
// CHECK-NEXT: 0xc81e ; ldp x19, x20, [sp, #240]
|
||||
// CHECK-NEXT: 0x9f ; ldp x29, x30, [sp], #256
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: EpilogueScope {
|
||||
// CHECK-NEXT: StartOffset: 37924
|
||||
// CHECK-NEXT: EpilogueStartIndex: 4
|
||||
// CHECK-NEXT: Opcodes [
|
||||
// CHECK-NEXT: 0xc998 ; ldp x25, x26, [sp, #192]
|
||||
// CHECK-NEXT: 0xc91a ; ldp x23, x24, [sp, #208]
|
||||
// CHECK-NEXT: 0xc89c ; ldp x21, x22, [sp, #224]
|
||||
// CHECK-NEXT: 0xc81e ; ldp x19, x20, [sp, #240]
|
||||
// CHECK-NEXT: 0x9f ; ldp x29, x30, [sp], #256
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: EpilogueScope {
|
||||
// CHECK-NEXT: StartOffset: 37930
|
||||
// CHECK-NEXT: EpilogueStartIndex: 14
|
||||
// CHECK-NEXT: Opcodes [
|
||||
// CHECK-NEXT: 0xe1 ; mov sp, fp
|
||||
// CHECK-NEXT: 0xc91a ; ldp x23, x24, [sp, #208]
|
||||
// CHECK-NEXT: 0xc89c ; ldp x21, x22, [sp, #224]
|
||||
// CHECK-NEXT: 0xc81e ; ldp x19, x20, [sp, #240]
|
||||
// CHECK-NEXT: 0x9f ; ldp x29, x30, [sp], #256
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT:]
|
||||
|
||||
.text
|
||||
.global multi_epilog
|
||||
.p2align 2
|
||||
.seh_proc multi_epilog
|
||||
multi_epilog:
|
||||
stp x29, lr, [sp, #-256]!
|
||||
.seh_save_fplr_x 256
|
||||
stp x19, x20, [sp, #240]
|
||||
.seh_save_regp x19, 240
|
||||
stp x21, x22, [sp, #224]
|
||||
.seh_save_regp x21, 224
|
||||
stp x23, x24, [sp, #208]
|
||||
.seh_save_regp x23, 208
|
||||
stp x25, x26, [sp, #192]
|
||||
.seh_save_regp x25, 192
|
||||
stp x27, x28, [sp, #176]
|
||||
.seh_save_regp x27, 176
|
||||
mov x29, fp
|
||||
.seh_set_fp
|
||||
.seh_endprologue
|
||||
.rept 30
|
||||
nop
|
||||
.endr
|
||||
bl foo
|
||||
// Epilogs 1, 2 and 3 are in the same segment as prolog.
|
||||
// epilog1 - mirroring prolog
|
||||
.seh_startepilogue
|
||||
mov sp, x29
|
||||
.seh_set_fp
|
||||
stp x27, x28, [sp, #176]
|
||||
.seh_save_regp x27, 176
|
||||
stp x25, x26, [sp, #192]
|
||||
.seh_save_regp x25, 192
|
||||
stp x23, x24, [sp, #208]
|
||||
.seh_save_regp x23, 208
|
||||
stp x21, x22, [sp, #224]
|
||||
.seh_save_regp x21, 224
|
||||
ldp x19, x20, [sp, #240]
|
||||
.seh_save_regp x19, 240
|
||||
ldp x29, lr, [sp], #256
|
||||
.seh_save_fplr_x 256
|
||||
.seh_endepilogue
|
||||
ret
|
||||
// epilog2 - a subsequence at the end of prolog, can use prolog's opcodes.
|
||||
.seh_startepilogue
|
||||
stp x25, x26, [sp, #192]
|
||||
.seh_save_regp x25, 192
|
||||
stp x23, x24, [sp, #208]
|
||||
.seh_save_regp x23, 208
|
||||
stp x21, x22, [sp, #224]
|
||||
.seh_save_regp x21, 224
|
||||
ldp x19, x20, [sp, #240]
|
||||
.seh_save_regp x19, 240
|
||||
ldp x29, lr, [sp], #256
|
||||
.seh_save_fplr_x 256
|
||||
.seh_endepilogue
|
||||
ret
|
||||
// epilog3 - cannot use prolog's opcode.
|
||||
.seh_startepilogue
|
||||
mov sp, x29
|
||||
.seh_set_fp
|
||||
stp x23, x24, [sp, #208]
|
||||
.seh_save_regp x23, 208
|
||||
stp x21, x22, [sp, #224]
|
||||
.seh_save_regp x21, 224
|
||||
ldp x19, x20, [sp, #240]
|
||||
.seh_save_regp x19, 240
|
||||
ldp x29, lr, [sp], #256
|
||||
.seh_save_fplr_x 256
|
||||
.seh_endepilogue
|
||||
ret
|
||||
.rept 300000
|
||||
nop
|
||||
.endr
|
||||
bl foo
|
||||
// Epilogs below are in a segment without prolog
|
||||
// epilog4 - mirroring prolog, its start index should be 1, counting the end_c.
|
||||
.seh_startepilogue
|
||||
mov sp, x29
|
||||
.seh_set_fp
|
||||
stp x27, x28, [sp, #176]
|
||||
.seh_save_regp x27, 176
|
||||
stp x25, x26, [sp, #192]
|
||||
.seh_save_regp x25, 192
|
||||
stp x23, x24, [sp, #208]
|
||||
.seh_save_regp x23, 208
|
||||
stp x21, x22, [sp, #224]
|
||||
.seh_save_regp x21, 224
|
||||
ldp x19, x20, [sp, #240]
|
||||
.seh_save_regp x19, 240
|
||||
ldp x29, lr, [sp], #256
|
||||
.seh_save_fplr_x 256
|
||||
.seh_endepilogue
|
||||
ret
|
||||
// epilog5 - same as epilog2, its start index should be: 1 + epilog2's index.
|
||||
.seh_startepilogue
|
||||
stp x25, x26, [sp, #192]
|
||||
.seh_save_regp x25, 192
|
||||
stp x23, x24, [sp, #208]
|
||||
.seh_save_regp x23, 208
|
||||
stp x21, x22, [sp, #224]
|
||||
.seh_save_regp x21, 224
|
||||
ldp x19, x20, [sp, #240]
|
||||
.seh_save_regp x19, 240
|
||||
ldp x29, lr, [sp], #256
|
||||
.seh_save_fplr_x 256
|
||||
.seh_endepilogue
|
||||
ret
|
||||
// epilog6 - same as epilog3, cannot use prolog's opcode. Again its start index
|
||||
// should be: 1 + epilog3's index.
|
||||
.seh_startepilogue
|
||||
mov sp, x29
|
||||
.seh_set_fp
|
||||
stp x23, x24, [sp, #208]
|
||||
.seh_save_regp x23, 208
|
||||
stp x21, x22, [sp, #224]
|
||||
.seh_save_regp x21, 224
|
||||
ldp x19, x20, [sp, #240]
|
||||
.seh_save_regp x19, 240
|
||||
ldp x29, lr, [sp], #256
|
||||
.seh_save_fplr_x 256
|
||||
.seh_endepilogue
|
||||
ret
|
||||
.seh_endfunclet
|
||||
.seh_endproc
|
|
@ -0,0 +1,212 @@
|
|||
// This test checks that we emit unwind info correctly for functions
|
||||
// larger than 1MB.
|
||||
// RUN: llvm-mc -triple aarch64-pc-win32 -filetype=obj %s -o %t.o
|
||||
// RUN: llvm-readobj -S -r -u %t.o | FileCheck %s
|
||||
|
||||
// CHECK: Section {
|
||||
// CHECK: Number: 4
|
||||
// CHECK-NEXT: Name: .xdata (2E 78 64 61 74 61 00 00)
|
||||
// CHECK-NEXT: VirtualSize: 0x0
|
||||
// CHECK-NEXT: VirtualAddress: 0x0
|
||||
// CHECK-NEXT: RawDataSize: 52
|
||||
// CHECK-NEXT: PointerToRawData: 0x3D0A20
|
||||
// CHECK-NEXT: PointerToRelocations: 0x0
|
||||
// CHECK-NEXT: PointerToLineNumbers: 0x0
|
||||
// CHECK-NEXT: RelocationCount: 0
|
||||
// CHECK-NEXT: LineNumberCount: 0
|
||||
// CHECK-NEXT: Characteristics [ (0x40300040)
|
||||
// CHECK-NEXT: IMAGE_SCN_ALIGN_4BYTES (0x300000)
|
||||
// CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
|
||||
// CHECK-NEXT: IMAGE_SCN_MEM_READ (0x40000000)
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: Section {
|
||||
// CHECK-NEXT: Number: 5
|
||||
// CHECK-NEXT: Name: .pdata (2E 70 64 61 74 61 00 00)
|
||||
// CHECK-NEXT: VirtualSize: 0x0
|
||||
// CHECK-NEXT: VirtualAddress: 0x0
|
||||
// CHECK-NEXT: RawDataSize: 40
|
||||
// CHECK-NEXT: PointerToRawData: 0x3D0A54
|
||||
// CHECK-NEXT: PointerToRelocations: 0x3D0A7C
|
||||
// CHECK-NEXT: PointerToLineNumbers: 0x0
|
||||
// CHECK-NEXT: RelocationCount: 10
|
||||
// CHECK-NEXT: LineNumberCount: 0
|
||||
// CHECK-NEXT: Characteristics [ (0x40300040)
|
||||
// CHECK-NEXT: IMAGE_SCN_ALIGN_4BYTES (0x300000)
|
||||
// CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
|
||||
// CHECK-NEXT: IMAGE_SCN_MEM_READ (0x40000000)
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-LABEL: Relocations [
|
||||
// CHECK-NEXT: Section (1) .text {
|
||||
// CHECK-NEXT: 0x186A04 IMAGE_REL_ARM64_BRANCH26 foo (14)
|
||||
// CHECK-NEXT: 0x3D091C IMAGE_REL_ARM64_BRANCH26 foo (14)
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: Section (5) .pdata {
|
||||
// CHECK-NEXT: 0x0 IMAGE_REL_ARM64_ADDR32NB .text (0)
|
||||
// CHECK-NEXT: 0x4 IMAGE_REL_ARM64_ADDR32NB .xdata (9)
|
||||
// CHECK-NEXT: 0x8 IMAGE_REL_ARM64_ADDR32NB .text (0)
|
||||
// CHECK-NEXT: 0xC IMAGE_REL_ARM64_ADDR32NB .xdata (9)
|
||||
// CHECK-NEXT: 0x10 IMAGE_REL_ARM64_ADDR32NB $L.text_1 (2)
|
||||
// CHECK-NEXT: 0x14 IMAGE_REL_ARM64_ADDR32NB .xdata (9)
|
||||
// CHECK-NEXT: 0x18 IMAGE_REL_ARM64_ADDR32NB $L.text_2 (3)
|
||||
// CHECK-NEXT: 0x1C IMAGE_REL_ARM64_ADDR32NB .xdata (9)
|
||||
// CHECK-NEXT: 0x20 IMAGE_REL_ARM64_ADDR32NB $L.text_3 (4)
|
||||
// CHECK-NEXT: 0x24 IMAGE_REL_ARM64_ADDR32NB .xdata (9)
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-LABEL: UnwindInformation [
|
||||
// CHECK-NEXT: RuntimeFunction {
|
||||
// CHECK-NEXT: Function: a (0x0)
|
||||
// CHECK-NEXT: ExceptionRecord: .xdata (0x0)
|
||||
// CHECK-NEXT: ExceptionData {
|
||||
// CHECK-NEXT: FunctionLength: 1048572
|
||||
// CHECK-NEXT: Version: 0
|
||||
// CHECK-NEXT: ExceptionData: No
|
||||
// CHECK-NEXT: EpiloguePacked: No
|
||||
// CHECK-NEXT: EpilogueScopes: 0
|
||||
// CHECK-NEXT: ByteCodeLength: 4
|
||||
// CHECK-NEXT: Prologue [
|
||||
// CHECK-NEXT: 0xd561 ; str x30, [sp, #-16]!
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: EpilogueScopes [
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: RuntimeFunction {
|
||||
// CHECK-NEXT: Function: a +0xFFFFC (0xFFFFC)
|
||||
// CHECK-NEXT: ExceptionRecord: .xdata +0x8 (0x8)
|
||||
// CHECK-NEXT: ExceptionData {
|
||||
// CHECK-NEXT: FunctionLength: 551444
|
||||
// CHECK-NEXT: Version: 0
|
||||
// CHECK-NEXT: ExceptionData: No
|
||||
// CHECK-NEXT: EpiloguePacked: Yes
|
||||
// CHECK-NEXT: EpilogueOffset: 1
|
||||
// CHECK-NEXT: ByteCodeLength: 4
|
||||
// CHECK-NEXT: Prologue [
|
||||
// CHECK-NEXT: 0xe5 ; end_c
|
||||
// CHECK-NEXT: 0xd561 ; str x30, [sp, #-16]!
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: Epilogue [
|
||||
// CHECK-NEXT: 0xd561 ; ldr x30, [sp], #16
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: RuntimeFunction {
|
||||
// CHECK-NEXT: Function: b (0x186A10)
|
||||
// CHECK-NEXT: ExceptionRecord: .xdata +0x10 (0x10)
|
||||
// CHECK-NEXT: ExceptionData {
|
||||
// CHECK-NEXT: FunctionLength: 1048572
|
||||
// CHECK-NEXT: Version: 0
|
||||
// CHECK-NEXT: ExceptionData: No
|
||||
// CHECK-NEXT: EpiloguePacked: No
|
||||
// CHECK-NEXT: EpilogueScopes: 0
|
||||
// CHECK-NEXT: ByteCodeLength: 8
|
||||
// CHECK-NEXT: Prologue [
|
||||
// CHECK-NEXT: 0xe1 ; mov fp, sp
|
||||
// CHECK-NEXT: 0xc81e ; stp x19, x20, [sp, #240]
|
||||
// CHECK-NEXT: 0x9f ; stp x29, x30, [sp, #-256]!
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: EpilogueScopes [
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: RuntimeFunction {
|
||||
// CHECK-NEXT: Function: $L.text_2 +0x86A0C (0x286A0C)
|
||||
// CHECK-NEXT: ExceptionRecord: .xdata +0x1C (0x1C)
|
||||
// CHECK-NEXT: ExceptionData {
|
||||
// CHECK-NEXT: FunctionLength: 1048572
|
||||
// CHECK-NEXT: Version: 0
|
||||
// CHECK-NEXT: ExceptionData: No
|
||||
// CHECK-NEXT: EpiloguePacked: Yes
|
||||
// CHECK-NEXT: EpilogueOffset: 0
|
||||
// CHECK-NEXT: ByteCodeLength: 8
|
||||
// CHECK-NEXT: Prologue [
|
||||
// CHECK-NEXT: 0xe5 ; end_c
|
||||
// CHECK-NEXT: 0xe1 ; mov fp, sp
|
||||
// CHECK-NEXT: 0xc81e ; stp x19, x20, [sp, #240]
|
||||
// CHECK-NEXT: 0x9f ; stp x29, x30, [sp, #-256]!
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: RuntimeFunction {
|
||||
// CHECK-NEXT: Function: $L.text_3 +0x86A08 (0x386A08)
|
||||
// CHECK-NEXT: ExceptionRecord: .xdata +0x28 (0x28)
|
||||
// CHECK-NEXT: ExceptionData {
|
||||
// CHECK-NEXT: FunctionLength: 302888
|
||||
// CHECK-NEXT: Version: 0
|
||||
// CHECK-NEXT: ExceptionData: No
|
||||
// CHECK-NEXT: EpiloguePacked: Yes
|
||||
// CHECK-NEXT: EpilogueOffset: 1
|
||||
// CHECK-NEXT: ByteCodeLength: 8
|
||||
// CHECK-NEXT: Prologue [
|
||||
// CHECK-NEXT: 0xe5 ; end_c
|
||||
// CHECK-NEXT: 0xe1 ; mov fp, sp
|
||||
// CHECK-NEXT: 0xc81e ; stp x19, x20, [sp, #240]
|
||||
// CHECK-NEXT: 0x9f ; stp x29, x30, [sp, #-256]!
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: Epilogue [
|
||||
// CHECK-NEXT: 0xe1 ; mov sp, fp
|
||||
// CHECK-NEXT: 0xc81e ; ldp x19, x20, [sp, #240]
|
||||
// CHECK-NEXT: 0x9f ; ldp x29, x30, [sp], #256
|
||||
// CHECK-NEXT: 0xe4 ; end
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
|
||||
.text
|
||||
// A simple function with an single epilog mirroring the prolog.
|
||||
.global a
|
||||
.p2align 2
|
||||
.seh_proc a
|
||||
a:
|
||||
str x30, [sp, #-16]!
|
||||
.seh_save_reg_x x30, 16
|
||||
.seh_endprologue
|
||||
.rept 400000
|
||||
nop
|
||||
.endr
|
||||
bl foo
|
||||
.seh_startepilogue
|
||||
ldr x30, [sp], #16
|
||||
.seh_save_reg_x x30, 16
|
||||
.seh_endepilogue
|
||||
ret
|
||||
.seh_endfunclet
|
||||
.seh_endproc
|
||||
|
||||
// Example 1 from https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#function-fragments
|
||||
.global b
|
||||
.p2align 2
|
||||
.seh_proc b
|
||||
b:
|
||||
stp x29, lr, [sp, #-256]!
|
||||
.seh_save_fplr_x 256
|
||||
stp x19, x20, [sp, #240]
|
||||
.seh_save_regp x19, 240
|
||||
mov x29, fp
|
||||
.seh_set_fp
|
||||
.seh_endprologue
|
||||
.rept 600000
|
||||
nop
|
||||
.endr
|
||||
bl foo
|
||||
.seh_startepilogue
|
||||
mov sp, x29
|
||||
.seh_set_fp
|
||||
ldp x19, x20, [sp, #240]
|
||||
.seh_save_regp x19, 240
|
||||
ldp x29, lr, [sp], #256
|
||||
.seh_save_fplr_x 256
|
||||
.seh_endepilogue
|
||||
ret
|
||||
.seh_endfunclet
|
||||
.seh_endproc
|
|
@ -855,7 +855,7 @@ bool Decoder::opcode_end_c(const uint8_t *OC, unsigned &Offset, unsigned Length,
|
|||
bool Prologue) {
|
||||
SW.startLine() << format("0x%02x ; end_c\n", OC[Offset]);
|
||||
++Offset;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_save_next(const uint8_t *OC, unsigned &Offset,
|
||||
|
|
Loading…
Reference in New Issue