Skip to content

Commit 0b61d22

Browse files
committedMay 3, 2019
[AArch64][Windows] Compute function length correctly in unwind tables.
The primary fix here is to WinException.cpp: we need to exclude jump tables when computing the length of a function, or else we fail to correctly compute the length. (We can only compute the number of bytes consumed by certain assembler directives after the entire file is parsed. ".p2align" is one of those directives, and is used by jump table generation.) The secondary fix, to MCWin64EH, is to make sure we don't silently miscompile if we hit a similar situation in the future. It's possible we could extend ARM64EmitUnwindInfo so it allows function bodies that contain assembler directives, but that's a lot more complicated; see the FIXME in MCWin64EH.cpp. Fixes https://bugs.llvm.org/show_bug.cgi?id=41581 . Differential Revision: https://reviews.llvm.org/D61095 llvm-svn: 359849
1 parent 973d66e commit 0b61d22

File tree

4 files changed

+66
-9
lines changed

4 files changed

+66
-9
lines changed
 

‎llvm/lib/CodeGen/AsmPrinter/WinException.cpp

+16-3
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ void WinException::beginFunction(const MachineFunction *MF) {
109109
beginFunclet(MF->front(), Asm->CurrentFnSym);
110110
}
111111

112+
void WinException::markFunctionEnd() {
113+
if (isAArch64 && CurrentFuncletEntry &&
114+
(shouldEmitMoves || shouldEmitPersonality))
115+
Asm->OutStreamer->EmitWinCFIFuncletOrFuncEnd();
116+
}
117+
112118
/// endFunction - Gather and emit post-function exception information.
113119
///
114120
void WinException::endFunction(const MachineFunction *MF) {
@@ -128,7 +134,7 @@ void WinException::endFunction(const MachineFunction *MF) {
128134
NonConstMF->tidyLandingPads();
129135
}
130136

131-
endFunclet();
137+
endFuncletImpl();
132138

133139
// endFunclet will emit the necessary .xdata tables for x64 SEH.
134140
if (Per == EHPersonality::MSVC_Win64SEH && MF->hasEHFunclets())
@@ -231,6 +237,15 @@ void WinException::beginFunclet(const MachineBasicBlock &MBB,
231237
}
232238

233239
void WinException::endFunclet() {
240+
if (isAArch64 && CurrentFuncletEntry &&
241+
(shouldEmitMoves || shouldEmitPersonality)) {
242+
Asm->OutStreamer->SwitchSection(CurrentFuncletTextSection);
243+
Asm->OutStreamer->EmitWinCFIFuncletOrFuncEnd();
244+
}
245+
endFuncletImpl();
246+
}
247+
248+
void WinException::endFuncletImpl() {
234249
// No funclet to process? Great, we have nothing to do.
235250
if (!CurrentFuncletEntry)
236251
return;
@@ -246,8 +261,6 @@ void WinException::endFunclet() {
246261
// to EmitWinEHHandlerData below can calculate the size of the funclet or
247262
// function.
248263
if (isAArch64) {
249-
Asm->OutStreamer->SwitchSection(CurrentFuncletTextSection);
250-
Asm->OutStreamer->EmitWinCFIFuncletOrFuncEnd();
251264
MCSection *XData = Asm->OutStreamer->getAssociatedXDataSection(
252265
Asm->OutStreamer->getCurrentSectionOnly());
253266
Asm->OutStreamer->SwitchSection(XData);

‎llvm/lib/CodeGen/AsmPrinter/WinException.h

+3
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class LLVM_LIBRARY_VISIBILITY WinException : public EHStreamer {
8585
/// only), it is relative to the frame pointer.
8686
int getFrameIndexOffset(int FrameIndex, const WinEHFuncInfo &FuncInfo);
8787

88+
void endFuncletImpl();
8889
public:
8990
//===--------------------------------------------------------------------===//
9091
// Main entry points.
@@ -99,6 +100,8 @@ class LLVM_LIBRARY_VISIBILITY WinException : public EHStreamer {
99100
/// immediately after the function entry point.
100101
void beginFunction(const MachineFunction *MF) override;
101102

103+
void markFunctionEnd() override;
104+
102105
/// Gather and emit post-function exception information.
103106
void endFunction(const MachineFunction *) override;
104107

‎llvm/lib/MC/MCWin64EH.cpp

+43-6
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,12 @@ static int64_t GetAbsDifference(MCStreamer &Streamer, const MCSymbol *LHS,
255255
MCBinaryExpr::createSub(MCSymbolRefExpr::create(LHS, Context),
256256
MCSymbolRefExpr::create(RHS, Context), Context);
257257
MCObjectStreamer *OS = (MCObjectStreamer *)(&Streamer);
258+
// It should normally be possible to calculate the length of a function
259+
// at this point, but it might not be possible in the presence of certain
260+
// unusual constructs, like an inline asm with an alignment directive.
258261
int64_t value;
259-
Diff->evaluateAsAbsolute(value, OS->getAssembler());
262+
if (!Diff->evaluateAsAbsolute(value, OS->getAssembler()))
263+
report_fatal_error("Failed to evaluate function length in SEH unwind info");
260264
return value;
261265
}
262266

@@ -498,11 +502,44 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) {
498502
streamer.EmitLabel(Label);
499503
info->Symbol = Label;
500504

501-
uint32_t FuncLength = 0x0;
502-
if (info->FuncletOrFuncEnd)
503-
FuncLength = (uint32_t)GetAbsDifference(streamer, info->FuncletOrFuncEnd,
504-
info->Begin);
505-
FuncLength /= 4;
505+
int64_t RawFuncLength;
506+
if (!info->FuncletOrFuncEnd) {
507+
// FIXME: This is very wrong; we emit SEH data which covers zero bytes
508+
// of code. But otherwise test/MC/AArch64/seh.s crashes.
509+
RawFuncLength = 0;
510+
} else {
511+
// FIXME: GetAbsDifference tries to compute the length of the function
512+
// immediately, before the whole file is emitted, but in general
513+
// that's impossible: the size in bytes of certain assembler directives
514+
// like .align and .fill is not known until the whole file is parsed and
515+
// relaxations are applied. Currently, GetAbsDifference fails with a fatal
516+
// error in that case. (We mostly don't hit this because inline assembly
517+
// specifying those directives is rare, and we don't normally try to
518+
// align loops on AArch64.)
519+
//
520+
// There are two potential approaches to delaying the computation. One,
521+
// we could emit something like ".word (endfunc-beginfunc)/4+0x10800000",
522+
// as long as we have some conservative estimate we could use to prove
523+
// that we don't need to split the unwind data. Emitting the constant
524+
// is straightforward, but there's no existing code for estimating the
525+
// size of the function.
526+
//
527+
// The other approach would be to use a dedicated, relaxable fragment,
528+
// which could grow to accommodate splitting the unwind data if
529+
// necessary. This is more straightforward, since it automatically works
530+
// without any new infrastructure, and it's consistent with how we handle
531+
// relaxation in other contexts. But it would require some refactoring
532+
// to move parts of the pdata/xdata emission into the implementation of
533+
// a fragment. We could probably continue to encode the unwind codes
534+
// here, but we'd have to emit the pdata, the xdata header, and the
535+
// epilogue scopes later, since they depend on whether the we need to
536+
// split the unwind data.
537+
RawFuncLength = GetAbsDifference(streamer, info->FuncletOrFuncEnd,
538+
info->Begin);
539+
}
540+
if (RawFuncLength > 0xFFFFF)
541+
report_fatal_error("SEH unwind data splitting not yet implemented");
542+
uint32_t FuncLength = (uint32_t)RawFuncLength / 4;
506543
uint32_t PrologCodeBytes = ARM64CountOfUnwindCodes(info->Instructions);
507544
uint32_t TotalCodeBytes = PrologCodeBytes;
508545

‎llvm/test/CodeGen/AArch64/win64-jumptable.ll

+4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
; RUN: llc -o - %s -mtriple=aarch64-windows -aarch64-enable-compress-jump-tables=0 | FileCheck %s
2+
; RUN: llc -o - %s -mtriple=aarch64-windows -aarch64-enable-compress-jump-tables=0 -filetype=obj | llvm-readobj -unwind | FileCheck %s -check-prefix=UNWIND
23

34
define void @f(i32 %x) {
45
entry:
@@ -46,3 +47,6 @@ declare void @g(i32, i32)
4647
; CHECK: .seh_handlerdata
4748
; CHECK: .text
4849
; CHECK: .seh_endproc
50+
51+
; Check that we can emit an object file with correct unwind info.
52+
; UNWIND: FunctionLength: {{[1-9][0-9]*}}

0 commit comments

Comments
 (0)
Please sign in to comment.