[PATCH][X86_64/MC] Towards usable Win64 exception handling

Kai Nacke kai.nacke at redstar.de
Tue Jun 18 07:57:49 PDT 2013


Hi Werner!

On 17.06.2013 19:08, Werner wrote:
>>
> Hello Kai,
>
> great stuff! I'm just searching for that :-)
> See: http://llvm.org/bugs/show_bug.cgi?id=15393

Yes, the use of /LARGEADDRESSAWARE:NO is a bit annoying. I think there 
was recently a discussion about this on the LLVM dev list. My patches 
fix this issue for the EH stuff (but not for that .debug_str problem).

>
> But I'm slightly confused!?
> You worte:
>> After finding yet another issue I realized that the patch was getting
>> far too big. Therefore I splitted the patch into 4 parts.
>
> I found 4 parts:
> [PATCH 1/5] Fix failing test seh.s
> [PATCH 2/5] Generate IMAGE_REL_AMD64_ADDR32NB relocations for SEH
> [PATCH 3/5] Fix alignment of unwind data.
> [PATCH 4/5] Emit personality function and Dwarf EH data for Win64
>
> But the "5th"?

Is intentionally missing (but see attachment :-) ).

>
> Up to now I'm missing in "X86MCAsmInfo.cpp" the added line(s):
>   X86MCAsmInfoMicrosoft::X86MCAsmInfoMicrosoft(const Triple &Triple) {
>     if (Triple.getArch() == Triple::x86_64) {
>       GlobalPrefix = "";
>       PrivateGlobalPrefix = ".L";
>     }
>
>     AssemblerDialect = AsmWriterFlavor;
>
>     TextAlignFillValue = 0x90;
> +
> +  // Exceptions handling
> +  ExceptionsType = ExceptionHandling::Win64;
>   }


It is not as easy. I attach all 5 patches here. The 5th patch enables 
Dwarf exception handling on Windows 64bit on top of the structure 
exception handling facility. You can use the unwind-seh stuff from gcc 
4.8 (untested) or write your own personality function. A working example 
in D is here: 
https://github.com/ldc-developers/druntime/blob/ldc/src/ldc/eh2.d

I excluded the 5th patch because I still need to write test cases for 
it. It also has 2 "challenges":

- Aligned variables on the stack lead to non-ABI compliant code. This is 
mainly an issue for vector types.
- In certain cases the epilogue code is optimized away. But the Windows 
ABI always requires the epilogue code (for the stack unwinding).

Otherwise it is working and passes all tests from the LLVM test suite.

BTW: Most of the 5th patch is work by Charles Davis.

Regards
Kai

>
> up soon
> -Werner
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>

-------------- next part --------------
>From 0bb3e7ec2a2d04ae2fc74f871e57d014f6058289 Mon Sep 17 00:00:00 2001
From: kai <kai at redstar.de>
Date: Mon, 27 May 2013 07:32:17 +0200
Subject: [PATCH 1/5] Fix failing test seh.s

The code offset for unwind code SET_FPREG is wrong because it
is set to constant 0. The fix is to do the same as for the other
unwind codes: emit a label and later the absolute difference
between the label and the begin of the prologue.
---
 lib/MC/MCStreamer.cpp                       | 4 +++-
 lib/MC/MCWin64EH.cpp                        | 5 ++---
 test/MC/COFF/seh.s                          | 2 --
 test/tools/llvm-objdump/win64-unwind-data.s | 2 +-
 4 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/lib/MC/MCStreamer.cpp b/lib/MC/MCStreamer.cpp
index 96dcf5c..190bd24 100644
--- a/lib/MC/MCStreamer.cpp
+++ b/lib/MC/MCStreamer.cpp
@@ -472,7 +472,9 @@ void MCStreamer::EmitWin64EHSetFrame(unsigned Register, unsigned Offset) {
     report_fatal_error("Frame register and offset already specified!");
   if (Offset & 0x0F)
     report_fatal_error("Misaligned frame pointer offset!");
-  MCWin64EHInstruction Inst(Win64EH::UOP_SetFPReg, 0, Register, Offset);
+  MCSymbol *Label = getContext().CreateTempSymbol();
+  MCWin64EHInstruction Inst(Win64EH::UOP_SetFPReg, Label, Register, Offset);
+  EmitLabel(Label);
   CurFrame->LastFrameInst = CurFrame->Instructions.size();
   CurFrame->Instructions.push_back(Inst);
 }
diff --git a/lib/MC/MCWin64EH.cpp b/lib/MC/MCWin64EH.cpp
index c5b637c..8db1fa2 100644
--- a/lib/MC/MCWin64EH.cpp
+++ b/lib/MC/MCWin64EH.cpp
@@ -64,7 +64,7 @@ static void EmitAbsDifference(MCStreamer &streamer, MCSymbol *lhs,
 
 static void EmitUnwindCode(MCStreamer &streamer, MCSymbol *begin,
                            MCWin64EHInstruction &inst) {
-  uint8_t b1, b2;
+  uint8_t b2;
   uint16_t w;
   b2 = (inst.getOperation() & 0x0F);
   switch (inst.getOperation()) {
@@ -93,8 +93,7 @@ static void EmitUnwindCode(MCStreamer &streamer, MCSymbol *begin,
     streamer.EmitIntValue(b2, 1);
     break;
   case Win64EH::UOP_SetFPReg:
-    b1 = inst.getOffset() & 0xF0;
-    streamer.EmitIntValue(b1, 1);
+    EmitAbsDifference(streamer, inst.getLabel(), begin);
     streamer.EmitIntValue(b2, 1);
     break;
   case Win64EH::UOP_SaveNonVol:
diff --git a/test/MC/COFF/seh.s b/test/MC/COFF/seh.s
index bef425e..b1e61a9 100644
--- a/test/MC/COFF/seh.s
+++ b/test/MC/COFF/seh.s
@@ -1,7 +1,5 @@
 // This test checks that the SEH directives emit the correct unwind data.
 
-// TODO: Expected fail because SET_FPREG has a wrong offset.
-// XFAIL: *
 // RUN: llvm-mc -triple x86_64-pc-win32 -filetype=obj %s | llvm-readobj -s -u | FileCheck %s
 
 // CHECK:      Sections [
diff --git a/test/tools/llvm-objdump/win64-unwind-data.s b/test/tools/llvm-objdump/win64-unwind-data.s
index 1e4c742..a172bfc 100644
--- a/test/tools/llvm-objdump/win64-unwind-data.s
+++ b/test/tools/llvm-objdump/win64-unwind-data.s
@@ -13,7 +13,7 @@
 // CHECK-NEXT: Frame register: RBX
 // CHECK-NEXT: Frame offset: 0
 // CHECK-NEXT: Unwind Codes:
-// CHECK-NEXT: 0x00: UOP_SetFPReg
+// CHECK-NEXT: 0x12: UOP_SetFPReg
 // CHECK-NEXT: 0x0f: UOP_PushNonVol RBX
 // CHECK-NEXT: 0x0e: UOP_SaveXMM128 XMM8 [0x0000]
 // CHECK-NEXT: 0x09: UOP_SaveNonVol RSI [0x0010]
-- 
1.8.0.msysgit.0

-------------- next part --------------
>From f748227c46af7c4051876c754c0a0c0a31b74acb Mon Sep 17 00:00:00 2001
From: kai <kai at redstar.de>
Date: Mon, 27 May 2013 09:06:18 +0200
Subject: [PATCH 2/5] Generate IMAGE_REL_AMD64_ADDR32NB relocations for SEH
 data structures.

The Win64 EH data structures must be of type IMAGE_REL_AMD64_ADDR32NB
instead of IMAGE_REL_AMD64_ADDR32. This is easiely achieved by adding
the VK_COFF_IMGREL32 modifier to the symbol reference.
Change also references to start and end of the SEH range of a function
as offsets to start of the function.
---
 lib/MC/MCWin64EH.cpp                        | 26 +++++++++++++++++++++-----
 test/MC/COFF/seh.s                          | 23 ++++++++++++++++++++++-
 test/tools/llvm-objdump/win64-unwind-data.s | 20 ++++++++++----------
 3 files changed, 53 insertions(+), 16 deletions(-)

diff --git a/lib/MC/MCWin64EH.cpp b/lib/MC/MCWin64EH.cpp
index 8db1fa2..c3d568b 100644
--- a/lib/MC/MCWin64EH.cpp
+++ b/lib/MC/MCWin64EH.cpp
@@ -128,14 +128,29 @@ static void EmitUnwindCode(MCStreamer &streamer, MCSymbol *begin,
   }
 }
 
+static void EmitSymbolRefWithOfs(MCStreamer &streamer,
+                                 const MCSymbol *Base,
+                                 const MCSymbol *Other) {
+  MCContext &Context = streamer.getContext();
+  const MCSymbolRefExpr *BaseRef = MCSymbolRefExpr::Create(Base, Context);
+  const MCSymbolRefExpr *OtherRef = MCSymbolRefExpr::Create(Other, Context);
+  const MCExpr *Ofs = MCBinaryExpr::CreateSub(OtherRef, BaseRef, Context);
+  const MCSymbolRefExpr *BaseRefRel = MCSymbolRefExpr::Create(Base,
+                                              MCSymbolRefExpr::VK_COFF_IMGREL32,
+                                              Context);
+  streamer.EmitValue(MCBinaryExpr::CreateAdd(BaseRefRel, Ofs, Context), 4);
+}
+
 static void EmitRuntimeFunction(MCStreamer &streamer,
                                 const MCWin64EHUnwindInfo *info) {
   MCContext &context = streamer.getContext();
 
   streamer.EmitValueToAlignment(4);
-  streamer.EmitValue(MCSymbolRefExpr::Create(info->Begin, context), 4);
-  streamer.EmitValue(MCSymbolRefExpr::Create(info->End, context), 4);
-  streamer.EmitValue(MCSymbolRefExpr::Create(info->Symbol, context), 4);
+  EmitSymbolRefWithOfs(streamer, info->Function, info->Begin);
+  EmitSymbolRefWithOfs(streamer, info->Function, info->End);
+  streamer.EmitValue(MCSymbolRefExpr::Create(info->Symbol,
+                                             MCSymbolRefExpr::VK_COFF_IMGREL32,
+                                             context), 4);
 }
 
 static void EmitUnwindInfo(MCStreamer &streamer, MCWin64EHUnwindInfo *info) {
@@ -188,8 +203,9 @@ static void EmitUnwindInfo(MCStreamer &streamer, MCWin64EHUnwindInfo *info) {
     EmitRuntimeFunction(streamer, info->ChainedParent);
   else if (flags &
            ((Win64EH::UNW_TerminateHandler|Win64EH::UNW_ExceptionHandler) << 3))
-    streamer.EmitValue(MCSymbolRefExpr::Create(info->ExceptionHandler, context),
-                       4);
+    streamer.EmitValue(MCSymbolRefExpr::Create(info->ExceptionHandler,
+                                              MCSymbolRefExpr::VK_COFF_IMGREL32,
+                                              context), 4);
   else if (numCodes < 2) {
     // The minimum size of an UNWIND_INFO struct is 8 bytes. If we're not
     // a chained unwind info, if there is no handler, and if there are fewer
diff --git a/test/MC/COFF/seh.s b/test/MC/COFF/seh.s
index b1e61a9..72d42f4 100644
--- a/test/MC/COFF/seh.s
+++ b/test/MC/COFF/seh.s
@@ -1,6 +1,6 @@
 // This test checks that the SEH directives emit the correct unwind data.
 
-// RUN: llvm-mc -triple x86_64-pc-win32 -filetype=obj %s | llvm-readobj -s -u | FileCheck %s
+// RUN: llvm-mc -triple x86_64-pc-win32 -filetype=obj %s | llvm-readobj -s -u -r | FileCheck %s
 
 // CHECK:      Sections [
 // CHECK:        Section {
@@ -34,6 +34,27 @@
 // CHECK-NEXT:   }
 // CHECK-NEXT: ]
 
+// CHECK-NEXT: Relocations [
+// CHECK-NEXT:   Section (2) .xdata {
+// CHECK-NEXT:     0x14 IMAGE_REL_AMD64_ADDR32NB __C_specific_handler
+// CHECK-NEXT:     0x20 IMAGE_REL_AMD64_ADDR32NB func
+// CHECK-NEXT:     0x24 IMAGE_REL_AMD64_ADDR32NB func
+// CHECK-NEXT:     0x28 IMAGE_REL_AMD64_ADDR32NB .xdata
+// CHECK-NEXT:   }
+// CHECK-NEXT:   Section (3) .pdata {
+// CHECK-NEXT:     0x0 IMAGE_REL_AMD64_ADDR32NB func
+// CHECK-NEXT:     0x4 IMAGE_REL_AMD64_ADDR32NB func
+// CHECK-NEXT:     0x8 IMAGE_REL_AMD64_ADDR32NB .xdata
+// CHECK-NEXT:     0xC IMAGE_REL_AMD64_ADDR32NB func
+// CHECK-NEXT:     0x10 IMAGE_REL_AMD64_ADDR32NB func
+// CHECK-NEXT:     0x14 IMAGE_REL_AMD64_ADDR32NB .xdata
+// CHECK-NEXT:     0x18 IMAGE_REL_AMD64_ADDR32NB smallFunc
+// CHECK-NEXT:     0x1C IMAGE_REL_AMD64_ADDR32NB smallFunc
+// CHECK-NEXT:     0x20 IMAGE_REL_AMD64_ADDR32NB .xdata
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+
+
 // CHECK:      UnwindInformation [
 // CHECK-NEXT:   RuntimeFunction {
 // CHECK-NEXT:     StartAddress: [[CodeSect1:[^ ]+]] [[BeginDisp1:(\+0x[A-F0-9]+)?]]
diff --git a/test/tools/llvm-objdump/win64-unwind-data.s b/test/tools/llvm-objdump/win64-unwind-data.s
index a172bfc..f8463fb 100644
--- a/test/tools/llvm-objdump/win64-unwind-data.s
+++ b/test/tools/llvm-objdump/win64-unwind-data.s
@@ -3,8 +3,8 @@
 
 // CHECK:      Unwind info:
 // CHECK:      Function Table:
-// CHECK-NEXT: Start Address: .text
-// CHECK-NEXT: End Address: .text + 0x001b
+// CHECK-NEXT: Start Address: func
+// CHECK-NEXT: End Address: func + 0x001b
 // CHECK-NEXT: Unwind Info Address: .xdata
 // CHECK-NEXT: Version: 1
 // CHECK-NEXT: Flags: 1 UNW_ExceptionHandler
@@ -20,8 +20,8 @@
 // CHECK-NEXT: 0x04: UOP_AllocSmall 24
 // CHECK-NEXT: 0x00: UOP_PushMachFrame w/o error code
 // CHECK:      Function Table:
-// CHECK-NEXT: Start Address: .text + 0x0012
-// CHECK-NEXT: End Address: .text + 0x0012
+// CHECK-NEXT: Start Address: func + 0x0012
+// CHECK-NEXT: End Address: func + 0x0012
 // CHECK-NEXT: Unwind Info Address: .xdata + 0x001c
 // CHECK-NEXT: Version: 1
 // CHECK-NEXT: Flags: 4 UNW_ChainInfo
@@ -29,8 +29,8 @@
 // CHECK-NEXT: Number of Codes: 0
 // CHECK-NEXT: No frame pointer used
 // CHECK:      Function Table:
-// CHECK-NEXT: Start Address: .text + 0x001b
-// CHECK-NEXT: End Address: .text + 0x001c
+// CHECK-NEXT: Start Address: smallFunc
+// CHECK-NEXT: End Address: smallFunc + 0x0001
 // CHECK-NEXT: Unwind Info Address: .xdata + 0x002c
 // CHECK-NEXT: Version: 1
 // CHECK-NEXT: Flags: 0
@@ -38,8 +38,8 @@
 // CHECK-NEXT: Number of Codes: 0
 // CHECK-NEXT: No frame pointer used
 // CHECK:      Function Table:
-// CHECK-NEXT: Start Address: .text + 0x001c
-// CHECK-NEXT: End Address: .text + 0x0039
+// CHECK-NEXT: Start Address: allocFunc
+// CHECK-NEXT: End Address: allocFunc + 0x001d
 // CHECK-NEXT: Unwind Info Address: .xdata + 0x0034
 // CHECK-NEXT: Version: 1
 // CHECK-NEXT: Flags: 0
@@ -90,9 +90,9 @@ smallFunc:
     .seh_endproc
 
 // Function with big stack allocation.
-    .globl smallFunc
+    .globl allocFunc
     .def allocFunc; .scl 2; .type 32; .endef
-    .seh_proc smallFunc
+    .seh_proc allocFunc
 allocFunc:
     .seh_pushframe @code
     subq $65520, %rsp
-- 
1.8.0.msysgit.0

-------------- next part --------------
>From fac403ad21bcd4bb87bfb29e74da1b883b419f6e Mon Sep 17 00:00:00 2001
From: kai <kai at redstar.de>
Date: Tue, 28 May 2013 06:37:39 +0200
Subject: [PATCH 3/5] Fix alignment of unwind data.

For alignment purposes, the instruction array will always have an even
number of entries, with the final entry potentially unused (in which case

the array will be one longer than indicated by the count of unwind codes

field).
---
 lib/MC/MCWin64EH.cpp      | 19 +++++++----
 test/MC/COFF/seh-align1.s | 65 +++++++++++++++++++++++++++++++++++++
 test/MC/COFF/seh-align2.s | 78 ++++++++++++++++++++++++++++++++++++++++++++
 test/MC/COFF/seh-align3.s | 83 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 238 insertions(+), 7 deletions(-)
 create mode 100644 test/MC/COFF/seh-align1.s
 create mode 100644 test/MC/COFF/seh-align2.s
 create mode 100644 test/MC/COFF/seh-align3.s

diff --git a/lib/MC/MCWin64EH.cpp b/lib/MC/MCWin64EH.cpp
index c3d568b..b8b07d3 100644
--- a/lib/MC/MCWin64EH.cpp
+++ b/lib/MC/MCWin64EH.cpp
@@ -159,11 +159,11 @@ static void EmitUnwindInfo(MCStreamer &streamer, MCWin64EHUnwindInfo *info) {
 
   MCContext &context = streamer.getContext();
   streamer.EmitValueToAlignment(4);
-  // Upper 3 bits are the version number (currently 1).
-  uint8_t flags = 0x01;
   info->Symbol = context.CreateTempSymbol();
   streamer.EmitLabel(info->Symbol);
 
+  // Upper 3 bits are the version number (currently 1).
+  uint8_t flags = 0x01;
   if (info->ChainedParent)
     flags |= Win64EH::UNW_ChainInfo << 3;
   else {
@@ -199,6 +199,14 @@ static void EmitUnwindInfo(MCStreamer &streamer, MCWin64EHUnwindInfo *info) {
     EmitUnwindCode(streamer, info->Begin, inst);
   }
 
+  // For alignment purposes, the instruction array will always have an even
+  // number of entries, with the final entry potentially unused (in which case
+  // the array will be one longer than indicated by the count of unwind codes
+  // field).
+  if (numCodes & 1) {
+    streamer.EmitIntValue(0, 2);
+  }
+
   if (flags & (Win64EH::UNW_ChainInfo << 3))
     EmitRuntimeFunction(streamer, info->ChainedParent);
   else if (flags &
@@ -206,14 +214,11 @@ static void EmitUnwindInfo(MCStreamer &streamer, MCWin64EHUnwindInfo *info) {
     streamer.EmitValue(MCSymbolRefExpr::Create(info->ExceptionHandler,
                                               MCSymbolRefExpr::VK_COFF_IMGREL32,
                                               context), 4);
-  else if (numCodes < 2) {
+  else if (numCodes == 0) {
     // The minimum size of an UNWIND_INFO struct is 8 bytes. If we're not
     // a chained unwind info, if there is no handler, and if there are fewer
     // than 2 slots used in the unwind code array, we have to pad to 8 bytes.
-    if (numCodes == 1)
-      streamer.EmitIntValue(0, 2);
-    else
-      streamer.EmitIntValue(0, 4);
+    streamer.EmitIntValue(0, 4);
   }
 }
 
diff --git a/test/MC/COFF/seh-align1.s b/test/MC/COFF/seh-align1.s
new file mode 100644
index 0000000..aafc6ed
--- /dev/null
+++ b/test/MC/COFF/seh-align1.s
@@ -0,0 +1,65 @@
+// This test checks the alignment and padding of the unwind info.
+
+// RUN: llvm-mc -triple x86_64-pc-win32 -filetype=obj %s | llvm-readobj -s -sd -sr -u | FileCheck %s
+
+// CHECK:      Sections [
+// CHECK:        Section {
+// CHECK:          Name: .xdata
+// CHECK:          RawDataSize: 8
+// CHECK:          RelocationCount: 0
+// CHECK:          Characteristics [
+// CHECK-NEXT:       ALIGN_4BYTES
+// CHECK-NEXT:       CNT_INITIALIZED_DATA
+// CHECK-NEXT:       MEM_READ
+// CHECK-NEXT:     ]
+// CHECK:          Relocations [
+// CHECK-NEXT:     ]
+// CHECK:          SectionData (
+// CHECK-NEXT:       0000: 01000000 00000000
+// CHECK-NEXT:     )
+// CHECK-NEXT:   }
+// CHECK:        Section {
+// CHECK:          Name: .pdata
+// CHECK:          RawDataSize: 12
+// CHECK:          RelocationCount: 3
+// CHECK:          Characteristics [
+// CHECK-NEXT:       IMAGE_SCN_ALIGN_4BYTES
+// CHECK-NEXT:       IMAGE_SCN_CNT_INITIALIZED_DATA
+// CHECK-NEXT:       IMAGE_SCN_MEM_READ
+// CHECK-NEXT:     ]
+// CHECK:          Relocations [
+// CHECK-NEXT:       [[BeginDisp:0x[A-F0-9]+]] IMAGE_REL_AMD64_ADDR32NB smallFunc
+// CHECK-NEXT:       [[EndDisp:0x[A-F0-9]+]] IMAGE_REL_AMD64_ADDR32NB smallFunc
+// CHECK-NEXT:       [[UnwindDisp:0x[A-F0-9]+]] IMAGE_REL_AMD64_ADDR32NB .xdata
+// CHECK-NEXT:     ]
+// CHECK:          SectionData (
+// CHECK-NEXT:       0000: 00000000 01000000 00000000
+// CHECK-NEXT:     )
+// CHECK-NEXT:   }
+// CHECK:        UnwindInformation [
+// CHECK-NEXT:     RuntimeFunction {
+// CHECK-NEXT:     StartAddress: smallFunc {{(\+0x[A-F0-9]+ )?}}([[BeginDisp]])
+// CHECK-NEXT:     EndAddress: smallFunc {{(\+0x[A-F0-9]+ )?}}([[EndDisp]])
+// CHECK-NEXT:     UnwindInfoAddress: .xdata {{(\+0x[A-F0-9]+ )?}}([[UnwindDisp]])
+// CHECK-NEXT:     UnwindInfo {
+// CHECK-NEXT:       Version: 1
+// CHECK-NEXT:       Flags [
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       PrologSize: 0
+// CHECK-NEXT:       FrameRegister: -
+// CHECK-NEXT:       FrameOffset: -
+// CHECK-NEXT:       UnwindCodeCount: 0
+// CHECK-NEXT:       UnwindCodes [
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+
+// Generate the minimal unwind info.
+// It contains only the version set to 1. All other bytes are 0.
+    .globl smallFunc
+    .def smallFunc; .scl 2; .type 32; .endef
+    .seh_proc smallFunc
+smallFunc:
+    ret
+    .seh_endproc
diff --git a/test/MC/COFF/seh-align2.s b/test/MC/COFF/seh-align2.s
new file mode 100644
index 0000000..5e6c49a
--- /dev/null
+++ b/test/MC/COFF/seh-align2.s
@@ -0,0 +1,78 @@
+// This test checks the alignment and padding of the unwind info.
+
+// RUN: llvm-mc -triple x86_64-pc-win32 -filetype=obj %s | llvm-readobj -s -sd -sr -u | FileCheck %s
+
+// CHECK:      Sections [
+// CHECK:        Section {
+// CHECK:          Name: .xdata
+// CHECK:          RawDataSize: 16
+// CHECK:          RelocationCount: 1
+// CHECK:          Characteristics [
+// CHECK-NEXT:       ALIGN_4BYTES
+// CHECK-NEXT:       CNT_INITIALIZED_DATA
+// CHECK-NEXT:       MEM_READ
+// CHECK-NEXT:     ]
+// CHECK:          Relocations [
+// CHECK-NEXT:       [[HandlerDisp:0x[A-F0-9]+]] IMAGE_REL_AMD64_ADDR32NB __C_specific_handler
+// CHECK-NEXT:     ]
+// CHECK:          SectionData (
+// CHECK-NEXT:       0000: 09000100 04220000 00000000 BEBAFECA
+// CHECK-NEXT:     )
+// CHECK-NEXT:   }
+// CHECK-NEXT:   Section {
+// CHECK:          Name: .pdata
+// CHECK:          RawDataSize: 12
+// CHECK:          RelocationCount: 3
+// CHECK:          Characteristics [
+// CHECK-NEXT:       IMAGE_SCN_ALIGN_4BYTES
+// CHECK-NEXT:       IMAGE_SCN_CNT_INITIALIZED_DATA
+// CHECK-NEXT:       IMAGE_SCN_MEM_READ
+// CHECK-NEXT:     ]
+// CHECK:          Relocations [
+// CHECK-NEXT:       [[BeginDisp:0x[A-F0-9]+]] IMAGE_REL_AMD64_ADDR32NB func
+// CHECK-NEXT:       [[EndDisp:0x[A-F0-9]+]] IMAGE_REL_AMD64_ADDR32NB func
+// CHECK-NEXT:       [[UnwindDisp:0x[A-F0-9]+]] IMAGE_REL_AMD64_ADDR32NB .xdata
+// CHECK-NEXT:     ]
+// CHECK:          SectionData (
+// CHECK-NEXT:       0000: FCFFFFFF 05000000 00000000
+// CHECK-NEXT:     )
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+// CHECK:      UnwindInformation [
+// CHECK-NEXT:   RuntimeFunction {
+// CHECK-NEXT:     StartAddress: func {{(\+0x[A-F0-9]+ )?}}([[BeginDisp]])
+// CHECK-NEXT:     EndAddress: func {{(\+0x[A-F0-9]+ )?}}([[EndDisp]])
+// CHECK-NEXT:     UnwindInfoAddress: .xdata {{(\+0x[A-F0-9]+ )?}}([[UnwindDisp]])
+// CHECK-NEXT:     UnwindInfo {
+// CHECK-NEXT:       Version: 1
+// CHECK-NEXT:       Flags [
+// CHECK-NEXT:         ExceptionHandler
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       PrologSize: 0
+// CHECK-NEXT:       FrameRegister: -
+// CHECK-NEXT:       FrameOffset: -
+// CHECK-NEXT:       UnwindCodeCount: 1
+// CHECK-NEXT:       UnwindCodes [
+// CHECK-NEXT:         0x04: ALLOC_SMALL size=24
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       Handler: __C_specific_handler ([[HandlerDisp]])
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+
+// Generates only one unwind code.
+// Requires padding of the unwind code array.
+    .globl func
+    .def func; .scl 2; .type 32; .endef
+    .seh_proc func
+    subq $24, %rsp
+    .seh_stackalloc 24
+    .seh_handler __C_specific_handler, @except
+    .seh_handlerdata
+    .long 0xcafebabe
+    .text
+    .seh_endprologue
+func:
+    addq $24, %rsp
+    ret
+    .seh_endproc
diff --git a/test/MC/COFF/seh-align3.s b/test/MC/COFF/seh-align3.s
new file mode 100644
index 0000000..238b5de
--- /dev/null
+++ b/test/MC/COFF/seh-align3.s
@@ -0,0 +1,83 @@
+// This test checks the alignment and padding of the unwind info.
+
+// RUN: llvm-mc -triple x86_64-pc-win32 -filetype=obj %s | llvm-readobj -s -sd -sr -u | FileCheck %s
+
+// CHECK:      Sections [
+// CHECK:        Section {
+// CHECK:          Name: .xdata
+// CHECK:          RawDataSize: 16
+// CHECK:          RelocationCount: 1
+// CHECK:          Characteristics [
+// CHECK-NEXT:       ALIGN_4BYTES
+// CHECK-NEXT:       CNT_INITIALIZED_DATA
+// CHECK-NEXT:       MEM_READ
+// CHECK-NEXT:     ]
+// CHECK:          Relocations [
+// CHECK-NEXT:       [[HandlerDisp:0x[A-F0-9]+]] IMAGE_REL_AMD64_ADDR32NB __C_specific_handler
+// CHECK-NEXT:     ]
+// CHECK:          SectionData (
+// CHECK-NEXT:       0000: 19000200 04D002C0 00000000 BEBAFECA
+// CHECK-NEXT:     )
+// CHECK-NEXT:   }
+// CHECK-NEXT:   Section {
+// CHECK:          Name: .pdata
+// CHECK:          RawDataSize: 12
+// CHECK:          RelocationCount: 3
+// CHECK:          Characteristics [
+// CHECK-NEXT:       IMAGE_SCN_ALIGN_4BYTES
+// CHECK-NEXT:       IMAGE_SCN_CNT_INITIALIZED_DATA
+// CHECK-NEXT:       IMAGE_SCN_MEM_READ
+// CHECK-NEXT:     ]
+// CHECK:          Relocations [
+// CHECK-NEXT:       [[BeginDisp:0x[A-F0-9]+]] IMAGE_REL_AMD64_ADDR32NB func
+// CHECK-NEXT:       [[EndDisp:0x[A-F0-9]+]] IMAGE_REL_AMD64_ADDR32NB func
+// CHECK-NEXT:       [[UnwindDisp:0x[A-F0-9]+]] IMAGE_REL_AMD64_ADDR32NB .xdata
+// CHECK-NEXT:     ]
+// CHECK:          SectionData (
+// CHECK-NEXT:       0000: FCFFFFFF 05000000 00000000
+// CHECK-NEXT:     )
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+// CHECK:      UnwindInformation [
+// CHECK-NEXT:   RuntimeFunction {
+// CHECK-NEXT:     StartAddress: func {{(\+0x[A-F0-9]+ )?}}([[BeginDisp]])
+// CHECK-NEXT:     EndAddress: func {{(\+0x[A-F0-9]+ )?}}([[EndDisp]])
+// CHECK-NEXT:     UnwindInfoAddress: .xdata {{(\+0x[A-F0-9]+ )?}}([[UnwindDisp]])
+// CHECK-NEXT:     UnwindInfo {
+// CHECK-NEXT:       Version: 1
+// CHECK-NEXT:       Flags [
+// CHECK-NEXT:         ExceptionHandler
+// CHECK-NEXT:         TerminateHandler
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       PrologSize: 0
+// CHECK-NEXT:       FrameRegister: -
+// CHECK-NEXT:       FrameOffset: -
+// CHECK-NEXT:       UnwindCodeCount: 2
+// CHECK-NEXT:       UnwindCodes [
+// CHECK-NEXT:         0x04: PUSH_NONVOL reg=R13
+// CHECK-NEXT:         0x02: PUSH_NONVOL reg=R12
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       Handler: __C_specific_handler ([[HandlerDisp]])
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+
+// Generates two unwind codes.
+// Requires no padding of the unwind code array.
+    .globl func
+    .def func; .scl 2; .type 32; .endef
+    .seh_proc func
+    push %r12
+    .seh_pushreg 12
+    push %r13
+    .seh_pushreg 13
+    .seh_handler __C_specific_handler, @except, @unwind
+    .seh_handlerdata
+    .long 0xcafebabe
+    .text
+    .seh_endprologue
+func:
+    pop %r13
+    pop %r12
+    ret
+    .seh_endproc
-- 
1.8.0.msysgit.0

-------------- next part --------------
>From 5db7e4263d720df88db1fbd6bf0c124691503a2e Mon Sep 17 00:00:00 2001
From: kai <kai at redstar.de>
Date: Wed, 29 May 2013 06:41:03 +0200
Subject: [PATCH 4/5] Emit personality function and Dwarf EH data for Win64
 SEH.

Obviously the personality function should be emitted as language handler
instead of the hard coded _GCC_specific_handler. The language specific
data must be placed after the unwind information therefore it must not
be emitted into a separate section.
---
 lib/CodeGen/AsmPrinter/Win64Exception.cpp | 12 +++---------
 lib/MC/MCObjectFileInfo.cpp               | 15 ++++++++++-----
 2 files changed, 13 insertions(+), 14 deletions(-)

diff --git a/lib/CodeGen/AsmPrinter/Win64Exception.cpp b/lib/CodeGen/AsmPrinter/Win64Exception.cpp
index 1561012..99d53b9 100644
--- a/lib/CodeGen/AsmPrinter/Win64Exception.cpp
+++ b/lib/CodeGen/AsmPrinter/Win64Exception.cpp
@@ -78,9 +78,9 @@ void Win64Exception::BeginFunction(const MachineFunction *MF) {
   if (!shouldEmitPersonality)
     return;
 
-  MCSymbol *GCCHandlerSym =
-    Asm->GetExternalSymbolSymbol("_GCC_specific_handler");
-  Asm->OutStreamer.EmitWin64EHHandler(GCCHandlerSym, true, true);
+  const MCSymbol *PersHandlerSym = TLOF.getCFIPersonalitySymbol(Per, Asm->Mang,
+                                                                MMI);
+  Asm->OutStreamer.EmitWin64EHHandler(PersHandlerSym, true, true);
 
   Asm->OutStreamer.EmitLabel(Asm->GetTempSymbol("eh_func_begin",
                                                 Asm->getFunctionNumber()));
@@ -99,14 +99,8 @@ void Win64Exception::EndFunction() {
   MMI->TidyLandingPads();
 
   if (shouldEmitPersonality) {
-    const TargetLoweringObjectFile &TLOF = Asm->getObjFileLowering();
-    const Function *Per = MMI->getPersonalities()[MMI->getPersonalityIndex()];
-    const MCSymbol *Sym = TLOF.getCFIPersonalitySymbol(Per, Asm->Mang, MMI);
-
     Asm->OutStreamer.PushSection();
     Asm->OutStreamer.EmitWin64EHHandlerData();
-    Asm->OutStreamer.EmitValue(MCSymbolRefExpr::Create(Sym, Asm->OutContext),
-                               4);
     EmitExceptionTable();
     Asm->OutStreamer.PopSection();
   }
diff --git a/lib/MC/MCObjectFileInfo.cpp b/lib/MC/MCObjectFileInfo.cpp
index 96b62f1..1be20c3 100644
--- a/lib/MC/MCObjectFileInfo.cpp
+++ b/lib/MC/MCObjectFileInfo.cpp
@@ -547,11 +547,16 @@ void MCObjectFileInfo::InitCOFFMCObjectFileInfo(Triple T) {
   // though it contains relocatable pointers.  In PIC mode, this is probably a
   // big runtime hit for C++ apps.  Either the contents of the LSDA need to be
   // adjusted or this should be a data section.
-  LSDASection =
-    Ctx->getCOFFSection(".gcc_except_table",
-                        COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
-                        COFF::IMAGE_SCN_MEM_READ,
-                        SectionKind::getReadOnly());
+  if (T.getOS() == Triple::Win32) {
+    // On Windows with SEH, the LSDA is emitted into the .xdata section
+    LSDASection = 0;
+  } else {
+    LSDASection =
+      Ctx->getCOFFSection(".gcc_except_table",
+                          COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
+                          COFF::IMAGE_SCN_MEM_READ,
+                          SectionKind::getReadOnly());
+  }
 
   // Debug info.
   DwarfAbbrevSection =
-- 
1.8.0.msysgit.0

-------------- next part --------------
>From 1523c3b748a591c67c1d7c36e9be66fc29c993b9 Mon Sep 17 00:00:00 2001
From: kai <kai at redstar.de>
Date: Wed, 29 May 2013 07:10:59 +0200
Subject: [PATCH 5/5] Add Win64 exception handling.

This patch enables exception handling on Windows based on the
structured exception handling mechanism built into the OS.
Most of this patch is based on work by Charles Davis.
---
 lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp |  7 ++-
 lib/Target/X86/X86AsmPrinter.h               |  3 ++
 lib/Target/X86/X86ISelLowering.cpp           |  3 +-
 lib/Target/X86/X86MCInstLower.cpp            | 70 ++++++++++++++++++++++++++++
 4 files changed, 81 insertions(+), 2 deletions(-)

diff --git a/lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp b/lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp
index 7815ae9..440ade9 100644
--- a/lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp
+++ b/lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp
@@ -131,7 +131,8 @@ getNonexecutableStackSection(MCContext &Ctx) const {
 void X86MCAsmInfoMicrosoft::anchor() { }
 
 X86MCAsmInfoMicrosoft::X86MCAsmInfoMicrosoft(const Triple &Triple) {
-  if (Triple.getArch() == Triple::x86_64) {
+  bool is64Bit = Triple.getArch() == Triple::x86_64;
+  if (is64Bit) {
     GlobalPrefix = "";
     PrivateGlobalPrefix = ".L";
   }
@@ -139,6 +140,10 @@ X86MCAsmInfoMicrosoft::X86MCAsmInfoMicrosoft(const Triple &Triple) {
   AssemblerDialect = AsmWriterFlavor;
 
   TextAlignFillValue = 0x90;
+
+  // Exceptions handling
+  if (is64Bit)
+    ExceptionsType = ExceptionHandling::Win64;
 }
 
 void X86MCAsmInfoGNUCOFF::anchor() { }
diff --git a/lib/Target/X86/X86AsmPrinter.h b/lib/Target/X86/X86AsmPrinter.h
index 6eed5ce..aa4227d 100644
--- a/lib/Target/X86/X86AsmPrinter.h
+++ b/lib/Target/X86/X86AsmPrinter.h
@@ -24,6 +24,9 @@ class MCStreamer;
 
 class LLVM_LIBRARY_VISIBILITY X86AsmPrinter : public AsmPrinter {
   const X86Subtarget *Subtarget;
+
+  void EmitUnwindingInstruction(const MachineInstr *MI);
+
  public:
   explicit X86AsmPrinter(TargetMachine &TM, MCStreamer &Streamer)
     : AsmPrinter(TM, Streamer) {
diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp
index 7e2e99c..4a78f48 100644
--- a/lib/Target/X86/X86ISelLowering.cpp
+++ b/lib/Target/X86/X86ISelLowering.cpp
@@ -559,7 +559,8 @@ void X86TargetLowering::resetOperationActions() {
   // FIXME - use subtarget debug flags
   if (!Subtarget->isTargetDarwin() &&
       !Subtarget->isTargetELF() &&
-      !Subtarget->isTargetCygMing()) {
+      !Subtarget->isTargetCygMing() &&
+      !Subtarget->isTargetWin64()) {
     setOperationAction(ISD::EH_LABEL, MVT::Other, Expand);
   }
 
diff --git a/lib/Target/X86/X86MCInstLower.cpp b/lib/Target/X86/X86MCInstLower.cpp
index 3fea4cb..5c8813a 100644
--- a/lib/Target/X86/X86MCInstLower.cpp
+++ b/lib/Target/X86/X86MCInstLower.cpp
@@ -650,6 +650,70 @@ static void LowerTlsAddr(MCStreamer &OutStreamer,
     .addExpr(tlsRef));
 }
 
+static bool isCalleeSavedReg(const MachineFunction *MF,
+                             const TargetRegisterInfo *RI, unsigned Reg) {
+  const uint16_t *NVRegs = RI->getCalleeSavedRegs(MF);
+  while (*NVRegs) {
+    if (*NVRegs == Reg)
+      return true;
+    NVRegs++;
+  }
+  return false;
+}
+
+void X86AsmPrinter::EmitUnwindingInstruction(const MachineInstr *MI) {
+  assert(MI->getFlag(MachineInstr::FrameSetup) &&
+         "Only call frame setup instructions allowed here!");
+  unsigned SReg, DReg;
+  unsigned Offset;
+  const X86RegisterInfo *RI =
+    static_cast<const X86RegisterInfo *>(TM.getRegisterInfo());
+  switch (MI->getOpcode()) {
+  default: llvm_unreachable("Unknown frame setup opcode!");
+  case X86::PUSH64r:
+    SReg = MI->getOperand(0).getReg();
+    if (isCalleeSavedReg(MI->getParent()->getParent(), RI, SReg))
+      OutStreamer.EmitWin64EHPushReg(RI->getSEHRegNum(SReg));
+    else
+      OutStreamer.EmitWin64EHAllocStack(8);
+    break;
+  case X86::SUB64ri8:
+  case X86::SUB64ri32: {
+    DReg = MI->getOperand(0).getReg();
+    Offset = MI->getOperand(2).getImm();
+    if (DReg == RI->getStackRegister()) {
+      OutStreamer.EmitWin64EHAllocStack(Offset);
+    }
+  } break;
+  case X86::MOV64rr:
+    DReg = MI->getOperand(0).getReg();
+    SReg = MI->getOperand(1).getReg();
+    if (DReg == RI->getFrameRegister(*MF) && SReg == RI->getStackRegister())
+      OutStreamer.EmitWin64EHSetFrame(RI->getSEHRegNum(DReg), 0);
+    break;
+  case X86::MOV64mr:
+  case X86::MOVAPSmr:
+    DReg = MI->getOperand(0).getReg();
+    Offset = MI->getOperand(0).getOffset();
+    SReg = MI->getOperand(1).getReg();
+    if (DReg == RI->getFrameRegister(*MF) || DReg == RI->getStackRegister()) {
+      if (MI->getOpcode() == X86::MOVAPSmr)
+        OutStreamer.EmitWin64EHSaveXMM(RI->getSEHRegNum(SReg), Offset);
+      else
+        OutStreamer.EmitWin64EHSaveReg(RI->getSEHRegNum(SReg), Offset);
+    }
+    break;
+  case X86::MOV64ri:
+  case X86::W64ALLOCA:
+    // Occurs if alloca() is used
+    break;
+  }
+  if (MI->getNextNode() &&
+      !MI->getNextNode()->getFlag(MachineInstr::FrameSetup)) {
+    OutStreamer.EmitWin64EHEndProlog();
+  }
+}
+
 void X86AsmPrinter::EmitInstruction(const MachineInstr *MI) {
   X86MCInstLower MCInstLowering(Mang, *MF, *this);
   switch (MI->getOpcode()) {
@@ -744,4 +808,10 @@ void X86AsmPrinter::EmitInstruction(const MachineInstr *MI) {
   MCInst TmpInst;
   MCInstLowering.Lower(MI, TmpInst);
   OutStreamer.EmitInstruction(TmpInst);
+
+  // Emit SEH unwind info for Win64-style EH.
+  if (MAI->getExceptionHandlingType() == ExceptionHandling::Win64 &&
+      MI->getFlag(MachineInstr::FrameSetup) &&
+      MI->getParent()->getParent()->getFunction()->needsUnwindTableEntry())
+    EmitUnwindingInstruction(MI);
 }
-- 
1.8.0.msysgit.0



More information about the llvm-commits mailing list