diff --git a/llvm/include/llvm/MC/MCWinEH.h b/llvm/include/llvm/MC/MCWinEH.h --- a/llvm/include/llvm/MC/MCWinEH.h +++ b/llvm/include/llvm/MC/MCWinEH.h @@ -40,6 +40,7 @@ bool HandlesUnwind = false; bool HandlesExceptions = false; + bool EmitAttempted = false; int LastFrameInst = -1; const FrameInfo *ChainedParent = nullptr; @@ -53,6 +54,15 @@ 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 { diff --git a/llvm/lib/MC/MCWin64EH.cpp b/llvm/lib/MC/MCWin64EH.cpp --- a/llvm/lib/MC/MCWin64EH.cpp +++ b/llvm/lib/MC/MCWin64EH.cpp @@ -494,6 +494,27 @@ // 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 @@ 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); } } diff --git a/llvm/test/MC/AArch64/seh.s b/llvm/test/MC/AArch64/seh.s --- a/llvm/test/MC/AArch64/seh.s +++ b/llvm/test/MC/AArch64/seh.s @@ -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 @@ 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 @@ 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