[MC] [Win64EH] Avoid producing malformed xdata records
If there's no unwinding opcodes, omit writing the xdata/pdata records. Previously, this generated truncated xdata records, and llvm-readobj would error out when trying to print them. If writing of an xdata record is forced via the .seh_handlerdata directive, skip it if there's no info to make a sensible unwind info structure out of, and clearly error out if such info appeared later in the process. Differential Revision: https://reviews.llvm.org/D86527
This commit is contained in:
@@ -40,6 +40,7 @@ struct FrameInfo {
|
||||
|
||||
bool HandlesUnwind = false;
|
||||
bool HandlesExceptions = false;
|
||||
bool EmitAttempted = false;
|
||||
|
||||
int LastFrameInst = -1;
|
||||
const FrameInfo *ChainedParent = nullptr;
|
||||
@@ -53,6 +54,15 @@ struct FrameInfo {
|
||||
const FrameInfo *ChainedParent)
|
||||
: Begin(BeginFuncEHLabel), Function(Function),
|
||||
ChainedParent(ChainedParent) {}
|
||||
|
||||
bool empty() const {
|
||||
if (!Instructions.empty())
|
||||
return false;
|
||||
for (const auto &E : EpilogMap)
|
||||
if (!E.second.empty())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class UnwindEmitter {
|
||||
|
||||
@@ -494,6 +494,27 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) {
|
||||
// If this UNWIND_INFO already has a symbol, it's already been emitted.
|
||||
if (info->Symbol)
|
||||
return;
|
||||
// If there's no unwind info here (not even a terminating UOP_End), the
|
||||
// unwind info is considered bogus and skipped. If this was done in
|
||||
// response to an explicit .seh_handlerdata, the associated trailing
|
||||
// handler data is left orphaned in the xdata section.
|
||||
if (info->empty()) {
|
||||
info->EmitAttempted = true;
|
||||
return;
|
||||
}
|
||||
if (info->EmitAttempted) {
|
||||
// If we tried to emit unwind info before (due to an explicit
|
||||
// .seh_handlerdata directive), but skipped it (because there was no
|
||||
// valid information to emit at the time), and it later got valid unwind
|
||||
// opcodes, we can't emit it here, because the trailing handler data
|
||||
// was already emitted elsewhere in the xdata section.
|
||||
streamer.getContext().reportError(
|
||||
SMLoc(), "Earlier .seh_handlerdata for " + info->Function->getName() +
|
||||
" skipped due to no unwind info at the time "
|
||||
"(.seh_handlerdata too early?), but the function later "
|
||||
"did get unwind info that can't be emitted");
|
||||
return;
|
||||
}
|
||||
|
||||
MCContext &context = streamer.getContext();
|
||||
MCSymbol *Label = context.createTempSymbol();
|
||||
@@ -657,16 +678,25 @@ static void ARM64EmitRuntimeFunction(MCStreamer &streamer,
|
||||
void llvm::Win64EH::ARM64UnwindEmitter::Emit(MCStreamer &Streamer) const {
|
||||
// Emit the unwind info structs first.
|
||||
for (const auto &CFI : Streamer.getWinFrameInfos()) {
|
||||
WinEH::FrameInfo *Info = CFI.get();
|
||||
if (Info->empty())
|
||||
continue;
|
||||
MCSection *XData = Streamer.getAssociatedXDataSection(CFI->TextSection);
|
||||
Streamer.SwitchSection(XData);
|
||||
ARM64EmitUnwindInfo(Streamer, CFI.get());
|
||||
ARM64EmitUnwindInfo(Streamer, Info);
|
||||
}
|
||||
|
||||
// Now emit RUNTIME_FUNCTION entries.
|
||||
for (const auto &CFI : Streamer.getWinFrameInfos()) {
|
||||
WinEH::FrameInfo *Info = CFI.get();
|
||||
// ARM64EmitUnwindInfo above clears the info struct, so we can't check
|
||||
// empty here. But if a Symbol is set, we should create the corresponding
|
||||
// pdata entry.
|
||||
if (!Info->Symbol)
|
||||
continue;
|
||||
MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection);
|
||||
Streamer.SwitchSection(PData);
|
||||
ARM64EmitRuntimeFunction(Streamer, CFI.get());
|
||||
ARM64EmitRuntimeFunction(Streamer, Info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// This test checks that the SEH directives don't cause the assembler to fail.
|
||||
// Checking that llvm-readobj doesn't bail out on the unwind data, but not
|
||||
// really checking the contents yet.
|
||||
|
||||
// RUN: llvm-mc -triple aarch64-pc-win32 -filetype=obj %s | llvm-readobj -S -r - | FileCheck %s
|
||||
// RUN: llvm-mc -triple aarch64-pc-win32 -filetype=obj %s | llvm-readobj -S -r -u - | FileCheck %s
|
||||
|
||||
// CHECK: Sections [
|
||||
// CHECK: Section {
|
||||
@@ -25,7 +27,7 @@
|
||||
// CHECK-NEXT: }
|
||||
// CHECK: Section {
|
||||
// CHECK: Name: .pdata
|
||||
// CHECK: RelocationCount: 4
|
||||
// CHECK: RelocationCount: 2
|
||||
// CHECK: Characteristics [
|
||||
// CHECK-NEXT: ALIGN_4BYTES
|
||||
// CHECK-NEXT: CNT_INITIALIZED_DATA
|
||||
@@ -41,8 +43,6 @@
|
||||
// CHECK-NEXT: Section (5) .pdata {
|
||||
// CHECK-NEXT: 0x0 IMAGE_REL_ARM64_ADDR32NB func
|
||||
// CHECK-NEXT: 0x4 IMAGE_REL_ARM64_ADDR32NB .xdata
|
||||
// CHECK-NEXT: 0x8 IMAGE_REL_ARM64_ADDR32NB smallFunc
|
||||
// CHECK-NEXT: 0xC IMAGE_REL_ARM64_ADDR32NB .xdata
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
|
||||
@@ -67,7 +67,8 @@ func:
|
||||
ret
|
||||
.seh_endproc
|
||||
|
||||
// Test emission of small functions.
|
||||
// Function with no .seh directives; no pdata/xdata entries are
|
||||
// generated.
|
||||
.globl smallFunc
|
||||
.def smallFunc
|
||||
.scl 2
|
||||
@@ -77,3 +78,21 @@ func:
|
||||
smallFunc:
|
||||
ret
|
||||
.seh_endproc
|
||||
|
||||
// Function with no .seh directives, but with .seh_handlerdata.
|
||||
// No xdata/pdata entries are generated, but the custom handler data
|
||||
// (the .long after .seh_handlerdata) is left orphaned in the xdata
|
||||
// section.
|
||||
.globl handlerFunc
|
||||
.def handlerFunc
|
||||
.scl 2
|
||||
.type 32
|
||||
.endef
|
||||
.seh_proc handlerFunc
|
||||
handlerFunc:
|
||||
ret
|
||||
.seh_handler __C_specific_handler, @except
|
||||
.seh_handlerdata
|
||||
.long 0
|
||||
.text
|
||||
.seh_endproc
|
||||
|
||||
Reference in New Issue
Block a user