Skip to content

Commit 37ef743

Browse files
committed
[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
1 parent a5d0fd1 commit 37ef743

File tree

3 files changed

+66
-7
lines changed

3 files changed

+66
-7
lines changed

llvm/include/llvm/MC/MCWinEH.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ struct FrameInfo {
4040

4141
bool HandlesUnwind = false;
4242
bool HandlesExceptions = false;
43+
bool EmitAttempted = false;
4344

4445
int LastFrameInst = -1;
4546
const FrameInfo *ChainedParent = nullptr;
@@ -53,6 +54,15 @@ struct FrameInfo {
5354
const FrameInfo *ChainedParent)
5455
: Begin(BeginFuncEHLabel), Function(Function),
5556
ChainedParent(ChainedParent) {}
57+
58+
bool empty() const {
59+
if (!Instructions.empty())
60+
return false;
61+
for (const auto &E : EpilogMap)
62+
if (!E.second.empty())
63+
return false;
64+
return true;
65+
}
5666
};
5767

5868
class UnwindEmitter {

llvm/lib/MC/MCWin64EH.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,27 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) {
494494
// If this UNWIND_INFO already has a symbol, it's already been emitted.
495495
if (info->Symbol)
496496
return;
497+
// If there's no unwind info here (not even a terminating UOP_End), the
498+
// unwind info is considered bogus and skipped. If this was done in
499+
// response to an explicit .seh_handlerdata, the associated trailing
500+
// handler data is left orphaned in the xdata section.
501+
if (info->empty()) {
502+
info->EmitAttempted = true;
503+
return;
504+
}
505+
if (info->EmitAttempted) {
506+
// If we tried to emit unwind info before (due to an explicit
507+
// .seh_handlerdata directive), but skipped it (because there was no
508+
// valid information to emit at the time), and it later got valid unwind
509+
// opcodes, we can't emit it here, because the trailing handler data
510+
// was already emitted elsewhere in the xdata section.
511+
streamer.getContext().reportError(
512+
SMLoc(), "Earlier .seh_handlerdata for " + info->Function->getName() +
513+
" skipped due to no unwind info at the time "
514+
"(.seh_handlerdata too early?), but the function later "
515+
"did get unwind info that can't be emitted");
516+
return;
517+
}
497518

498519
MCContext &context = streamer.getContext();
499520
MCSymbol *Label = context.createTempSymbol();
@@ -657,16 +678,25 @@ static void ARM64EmitRuntimeFunction(MCStreamer &streamer,
657678
void llvm::Win64EH::ARM64UnwindEmitter::Emit(MCStreamer &Streamer) const {
658679
// Emit the unwind info structs first.
659680
for (const auto &CFI : Streamer.getWinFrameInfos()) {
681+
WinEH::FrameInfo *Info = CFI.get();
682+
if (Info->empty())
683+
continue;
660684
MCSection *XData = Streamer.getAssociatedXDataSection(CFI->TextSection);
661685
Streamer.SwitchSection(XData);
662-
ARM64EmitUnwindInfo(Streamer, CFI.get());
686+
ARM64EmitUnwindInfo(Streamer, Info);
663687
}
664688

665689
// Now emit RUNTIME_FUNCTION entries.
666690
for (const auto &CFI : Streamer.getWinFrameInfos()) {
691+
WinEH::FrameInfo *Info = CFI.get();
692+
// ARM64EmitUnwindInfo above clears the info struct, so we can't check
693+
// empty here. But if a Symbol is set, we should create the corresponding
694+
// pdata entry.
695+
if (!Info->Symbol)
696+
continue;
667697
MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection);
668698
Streamer.SwitchSection(PData);
669-
ARM64EmitRuntimeFunction(Streamer, CFI.get());
699+
ARM64EmitRuntimeFunction(Streamer, Info);
670700
}
671701
}
672702

llvm/test/MC/AArch64/seh.s

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// This test checks that the SEH directives don't cause the assembler to fail.
2+
// Checking that llvm-readobj doesn't bail out on the unwind data, but not
3+
// really checking the contents yet.
24

3-
// RUN: llvm-mc -triple aarch64-pc-win32 -filetype=obj %s | llvm-readobj -S -r - | FileCheck %s
5+
// RUN: llvm-mc -triple aarch64-pc-win32 -filetype=obj %s | llvm-readobj -S -r -u - | FileCheck %s
46

57
// CHECK: Sections [
68
// CHECK: Section {
@@ -25,7 +27,7 @@
2527
// CHECK-NEXT: }
2628
// CHECK: Section {
2729
// CHECK: Name: .pdata
28-
// CHECK: RelocationCount: 4
30+
// CHECK: RelocationCount: 2
2931
// CHECK: Characteristics [
3032
// CHECK-NEXT: ALIGN_4BYTES
3133
// CHECK-NEXT: CNT_INITIALIZED_DATA
@@ -41,8 +43,6 @@
4143
// CHECK-NEXT: Section (5) .pdata {
4244
// CHECK-NEXT: 0x0 IMAGE_REL_ARM64_ADDR32NB func
4345
// CHECK-NEXT: 0x4 IMAGE_REL_ARM64_ADDR32NB .xdata
44-
// CHECK-NEXT: 0x8 IMAGE_REL_ARM64_ADDR32NB smallFunc
45-
// CHECK-NEXT: 0xC IMAGE_REL_ARM64_ADDR32NB .xdata
4646
// CHECK-NEXT: }
4747
// CHECK-NEXT: ]
4848

@@ -67,7 +67,8 @@ func:
6767
ret
6868
.seh_endproc
6969

70-
// Test emission of small functions.
70+
// Function with no .seh directives; no pdata/xdata entries are
71+
// generated.
7172
.globl smallFunc
7273
.def smallFunc
7374
.scl 2
@@ -77,3 +78,21 @@ func:
7778
smallFunc:
7879
ret
7980
.seh_endproc
81+
82+
// Function with no .seh directives, but with .seh_handlerdata.
83+
// No xdata/pdata entries are generated, but the custom handler data
84+
// (the .long after .seh_handlerdata) is left orphaned in the xdata
85+
// section.
86+
.globl handlerFunc
87+
.def handlerFunc
88+
.scl 2
89+
.type 32
90+
.endef
91+
.seh_proc handlerFunc
92+
handlerFunc:
93+
ret
94+
.seh_handler __C_specific_handler, @except
95+
.seh_handlerdata
96+
.long 0
97+
.text
98+
.seh_endproc

0 commit comments

Comments
 (0)