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 @@ -59,6 +59,7 @@ struct Epilog { std::vector Instructions; unsigned Condition; + MCSymbol *End; }; MapVector EpilogMap; 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 @@ -1197,7 +1197,8 @@ return Count; } -static uint32_t ARMCountOfInstructionBytes(ArrayRef Insns) { +static uint32_t ARMCountOfInstructionBytes(ArrayRef Insns, + bool *HasCustom = nullptr) { uint32_t Count = 0; for (const auto &I : Insns) { switch (static_cast(I.Operation)) { @@ -1247,12 +1248,50 @@ // We can't reason about what instructions this maps to; return a // phony number to make sure we don't accidentally do epilog packing. Count += 1000; + if (HasCustom) + *HasCustom = true; break; } } return Count; } +static void checkARMInstructions(MCStreamer &Streamer, + ArrayRef Insns, + const MCSymbol *Begin, const MCSymbol *End, + StringRef Name, StringRef Type) { + if (!End) + return; + Optional MaybeDistance = + GetOptionalAbsDifference(Streamer, End, Begin); + if (!MaybeDistance) + return; + uint32_t Distance = (uint32_t)*MaybeDistance; + bool HasCustom = false; + uint32_t InstructionBytes = ARMCountOfInstructionBytes(Insns, &HasCustom); + if (HasCustom) + return; + if (Distance != InstructionBytes) { + Streamer.getContext().reportError( + SMLoc(), "Incorrect size for " + Name + " " + Type + ": " + + Twine(Distance) + + " bytes of instructions in range, but .seh directives " + "corresponding to " + + Twine(InstructionBytes) + " bytes\n"); + } +} + +static bool isARMTerminator(const WinEH::Instruction &inst) { + switch (static_cast(inst.Operation)) { + case Win64EH::UOP_End: + case Win64EH::UOP_EndNop: + case Win64EH::UOP_WideEndNop: + return true; + default: + return false; + } +} + // Unwind opcode encodings and restrictions are documented at // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling static void ARMEmitUnwindCode(MCStreamer &streamer, @@ -1953,6 +1992,27 @@ streamer.emitLabel(Label); info->Symbol = Label; + if (!info->PrologEnd) + streamer.getContext().reportError(SMLoc(), "Prologue in " + + info->Function->getName() + + " not correctly terminated"); + + if (info->PrologEnd && !info->Fragment) + checkARMInstructions(streamer, info->Instructions, info->Begin, + info->PrologEnd, info->Function->getName(), + "prologue"); + for (auto &I : info->EpilogMap) { + MCSymbol *EpilogStart = I.first; + auto &Epilog = I.second; + checkARMInstructions(streamer, Epilog.Instructions, EpilogStart, Epilog.End, + info->Function->getName(), "epilogue"); + if (Epilog.Instructions.empty() || + !isARMTerminator(Epilog.Instructions.back())) + streamer.getContext().reportError( + SMLoc(), "Epilogue in " + info->Function->getName() + + " not correctly terminated"); + } + Optional RawFuncLength; const MCExpr *FuncLengthExpr = nullptr; if (!info->FuncletOrFuncEnd) { diff --git a/llvm/lib/MC/WinCOFFObjectWriter.cpp b/llvm/lib/MC/WinCOFFObjectWriter.cpp --- a/llvm/lib/MC/WinCOFFObjectWriter.cpp +++ b/llvm/lib/MC/WinCOFFObjectWriter.cpp @@ -958,7 +958,7 @@ for (const auto &Section : Asm) { COFFSection *Sec = SectionMap[&Section]; - if (Sec->Number == -1) + if (!Sec || Sec->Number == -1) continue; Sec->Header.SizeOfRawData = Layout.getSectionAddressSize(&Section); diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp @@ -11,6 +11,7 @@ #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCWin64EH.h" #include "llvm/MC/MCWinCOFFStreamer.h" @@ -241,6 +242,12 @@ if (!CurFrame) return; + if (!CurrentEpilog) { + S.getContext().reportError(SMLoc(), "Stray .seh_endepilogue in " + + CurFrame->Function->getName()); + return; + } + std::vector &Epilog = CurFrame->EpilogMap[CurrentEpilog].Instructions; @@ -259,6 +266,8 @@ InEpilogCFI = false; WinEH::Instruction Inst = WinEH::Instruction(UnwindCode, nullptr, -1, 0); CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst); + MCSymbol *Label = S.emitCFILabel(); + CurFrame->EpilogMap[CurrentEpilog].End = Label; CurrentEpilog = nullptr; } diff --git a/llvm/test/MC/ARM/seh-checks.s b/llvm/test/MC/ARM/seh-checks.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ARM/seh-checks.s @@ -0,0 +1,51 @@ +// This test checks error reporting for mismatched prolog/epilog lengths + +// RUN: not llvm-mc -triple thumbv7-pc-win32 -filetype=obj -o /dev/null %s 2>&1 | FileCheck %s + +// CHECK-NOT: func1 +// CHECK: error: Incorrect size for func2 epilogue: 6 bytes of instructions in range, but .seh directives corresponding to 4 bytes +// CHECK: error: Incorrect size for func3 prologue: 4 bytes of instructions in range, but .seh directives corresponding to 2 bytes + + .text + .syntax unified + + .seh_proc func1 +func1: + // Instruction with indeterminate length + b other + .seh_endprologue + nop + .seh_startepilogue + // The p2align causes the length of the epilogue to be unknown, so + // we can't report errors about the mismatch here. + .p2align 1 + pop {r4-r7-lr} + .seh_save_regs {r4-r7,lr} + bx lr + .seh_nop + .seh_endepilogue + .seh_endproc + + .seh_proc func2 +func2: + .seh_endprologue + nop + .seh_startepilogue + // As we're popping into lr instead of directly into pc, this pop + // becomes a wide instruction. + pop {r4-r7,lr} + // The directive things we're making a narrow instruction, which + // is wrong. + .seh_save_regs {r4-r7,lr} + bx lr + .seh_nop + .seh_endepilogue + .seh_endproc + + .seh_proc func3 +func3: + nop.w + .seh_nop + .seh_endprologue + nop + .seh_endproc diff --git a/llvm/test/MC/ARM/seh-checks2.s b/llvm/test/MC/ARM/seh-checks2.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ARM/seh-checks2.s @@ -0,0 +1,74 @@ +// This test checks error reporting for missing ending/starting of prologues/epilogues + +// RUN: not llvm-mc -triple thumbv7-pc-win32 -filetype=obj -o /dev/null %s 2>&1 | FileCheck %s + +// CHECK: error: Stray .seh_endepilogue in func1 +// CHECK: error: Prologue in func2 not correctly terminated +// CHECK: error: Epilogue in func3 not correctly terminated +// CHECK: error: Epilogue in func4 not correctly terminated + + .text + .syntax unified + + .seh_proc func1 +func1: + sub sp, #16 + .seh_stackalloc 16 + .seh_endprologue + nop + // Missing .seh_startepilogue + add sp, #16 + .seh_stackalloc 16 + bx lr + .seh_nop + .seh_endepilogue + .seh_endproc + + .seh_proc func2 +func2: + sub sp, #16 + .seh_stackalloc 16 + // Missing .seh_endprologue + nop + .seh_startepilogue + add sp, #16 + .seh_stackalloc 16 + bx lr + .seh_nop + .seh_endepilogue + .seh_endproc + + .seh_proc func3 +func3: + sub sp, #16 + .seh_stackalloc 16 + .seh_endprologue + nop + .seh_startepilogue + add sp, #16 + .seh_stackalloc 16 + bx lr + .seh_nop + // Missing .seh_endepilogue + .seh_endproc + + .seh_proc func4 +func4: + sub sp, #16 + .seh_stackalloc 16 + .seh_endprologue + nop + .seh_startepilogue + add sp, #16 + .seh_stackalloc 16 + bx lr + .seh_nop + // Missing .seh_endepilogue + nop + .seh_startepilogue + add sp, #16 + .seh_stackalloc 16 + bx lr + .seh_nop + .seh_endepilogue + .seh_endproc