Skip to content

Commit

Permalink
[MC] [Win64EH] Avoid producing malformed xdata records
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mstorsjo committed Aug 28, 2020
1 parent a5d0fd1 commit 37ef743
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 7 deletions.
10 changes: 10 additions & 0 deletions llvm/include/llvm/MC/MCWinEH.h
Expand Up @@ -40,6 +40,7 @@ struct FrameInfo {

bool HandlesUnwind = false;
bool HandlesExceptions = false;
bool EmitAttempted = false;

int LastFrameInst = -1;
const FrameInfo *ChainedParent = nullptr;
Expand All @@ -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 {
Expand Down
34 changes: 32 additions & 2 deletions llvm/lib/MC/MCWin64EH.cpp
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}
}

Expand Down
29 changes: 24 additions & 5 deletions 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 {
Expand All @@ -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
Expand All @@ -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: ]

Expand All @@ -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
Expand All @@ -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

0 comments on commit 37ef743

Please sign in to comment.