This patch reuses the existing SEH, stack unwinding and C++ exceptions
from ix86 and implements the required unwinding for AArch64.

Co-authored-by: Zac Walker <[email protected]>
Signed-off-by: Evgeny Karpov <[email protected]>

gcc/ChangeLog:

        * common/config/aarch64/aarch64-common.cc (aarch64_except_unwind_info):
        Add unwinding for AArch64.
        * config/aarch64/aarch64-abi-ms-protos.h (aarch64_pe_seh_unwind_emit):
        Likewise.
        * config/aarch64/aarch64-abi-ms.cc (aarch64_seh_cfa_adjust_cfa):
        Likewise.
        (aarch64_seh_emit_save): Likewise.
        (aarch64_seh_emit_save_pair): Likewise.
        (aarch64_seh_pattern_emit): Likewise.
        (aarch64_pe_seh_unwind_emit): Likewise.
        * config/aarch64/aarch64-abi-ms.h (CALLEE_SAVED_REG_NUMBER):
        Define callee saved registers.
        * config/aarch64/aarch64.cc (TARGET_SEH): Add unwinding for AArch64.
        (aarch64_get_separate_components): Likewise.
        (aarch64_frame_pointer_required): Likewise.
        (aarch64_override_options_internal): Likewise.
        (aarch64_declare_function_name): Likewise.
        * config/aarch64/cygming.h (SYMBOL_REF_STUBVAR_P): Likewise.
        (TARGET_SEH): Likewise.
        (SEH_MAX_FRAME_SIZE): Likewise.
        (TARGET_ASM_UNWIND_EMIT): Likewise.
        (TARGET_ASM_UNWIND_EMIT_BEFORE_INSN): Likewise.
        (TARGET_ASM_FUNCTION_END_PROLOGUE): Likewise.
        (TARGET_ASM_EMIT_EXCEPT_PERSONALITY): Likewise.
        (TARGET_ASM_INIT_SECTIONS): Likewise.
        (TARGET_EXCEPT_UNWIND_INFO): Likewise.
        (SUBTARGET_ASM_UNWIND_INIT): Likewise.
        (ASM_DECLARE_FUNCTION_SIZE): Likewise.
        (ASM_DECLARE_COLD_FUNCTION_SIZE): Likewise.
        (ASM_DECLARE_COLD_FUNCTION_NAME): Likewise.

libgcc/ChangeLog:

        * unwind-seh.c (defined): Update SEH handler for AArch64.
        (_Unwind_Backtrace): Add unwinding for AArch64.

libstdc++-v3/ChangeLog:

        * libsupc++/eh_personality.cc (defined): Add unwinding for AArch64.
---
 gcc/common/config/aarch64/aarch64-common.cc |  23 ++
 gcc/config/aarch64/aarch64-abi-ms-protos.h  |   2 +
 gcc/config/aarch64/aarch64-abi-ms.cc        | 310 ++++++++++++++++++++
 gcc/config/aarch64/aarch64-abi-ms.h         |   4 +
 gcc/config/aarch64/aarch64.cc               |  17 +-
 gcc/config/aarch64/cygming.h                |  40 ++-
 libgcc/unwind-seh.c                         |  41 ++-
 libstdc++-v3/libsupc++/eh_personality.cc    |  15 +
 8 files changed, 438 insertions(+), 14 deletions(-)

diff --git a/gcc/common/config/aarch64/aarch64-common.cc 
b/gcc/common/config/aarch64/aarch64-common.cc
index fc7e84ae966..e11f70ea8f9 100644
--- a/gcc/common/config/aarch64/aarch64-common.cc
+++ b/gcc/common/config/aarch64/aarch64-common.cc
@@ -842,6 +842,29 @@ is_host_cpu_not_armv8_base (int argc, const char **argv)
   return "";
 }
 
+#if TARGET_AARCH64_MS_ABI
+/* Implement TARGET_EXCEPT_UNWIND_INFO.  */
+
+static enum unwind_info_type
+aarch64_except_unwind_info (struct gcc_options *opts)
+{
+  /* Honor the --enable-sjlj-exceptions configure switch.  */
+#ifdef CONFIG_SJLJ_EXCEPTIONS
+  if (CONFIG_SJLJ_EXCEPTIONS)
+    return UI_SJLJ;
+#endif
+
+  /* Prefer SEH exceptions over anything else.  */
+  if (opts->x_flag_unwind_tables)
+    return UI_SEH;
+
+  if (DWARF2_UNWIND_INFO)
+    return UI_DWARF2;
+
+  return UI_SJLJ;
+}
+#endif
+
 struct gcc_targetm_common targetm_common = TARGETM_COMMON_INITIALIZER;
 
 #undef AARCH64_CPU_NAME_LENGTH
diff --git a/gcc/config/aarch64/aarch64-abi-ms-protos.h 
b/gcc/config/aarch64/aarch64-abi-ms-protos.h
index f08ea29d0c2..29e82ae392e 100644
--- a/gcc/config/aarch64/aarch64-abi-ms-protos.h
+++ b/gcc/config/aarch64/aarch64-abi-ms-protos.h
@@ -28,6 +28,8 @@ extern tree aarch64_ms_variadic_abi_fn_abi_va_list (tree 
fndecl);
 
 extern tree aarch64_ms_variadic_abi_canonical_va_list_type (tree type);
 
+extern void aarch64_pe_seh_unwind_emit (FILE *, rtx_insn *);
+
 extern int aarch64_arg_partial_bytes (cumulative_args_t,
                                      const function_arg_info &);
 
diff --git a/gcc/config/aarch64/aarch64-abi-ms.cc 
b/gcc/config/aarch64/aarch64-abi-ms.cc
index 1eaab4246d3..d4db95868cb 100644
--- a/gcc/config/aarch64/aarch64-abi-ms.cc
+++ b/gcc/config/aarch64/aarch64-abi-ms.cc
@@ -32,6 +32,7 @@
 #include "regs.h"
 #include "function-abi.h"
 #include "builtins.h"
+#include "diagnostic-core.h"
 #include "aarch64-abi-ms-protos.h"
 
 /* Iterate through the target-specific builtin types for va_list.
@@ -104,3 +105,312 @@ aarch64_arg_partial_bytes (cumulative_args_t pcum_v,
 
   return 0;
 }
+
+/* Emit SEH directive to adjust the stack pointer by offset.  */
+
+static void
+aarch64_seh_cfa_adjust_cfa (FILE *f, struct seh_frame_state *seh, rtx pat)
+{
+  rtx src = SET_SRC (pat);
+  if (GET_CODE (src) != PLUS)
+    gcc_unreachable ();
+
+  const HOST_WIDE_INT reg_offset = INTVAL (XEXP (src, 1));
+  src = XEXP (src, 0);
+
+  gcc_assert (src == stack_pointer_rtx);
+  gcc_assert (seh->cfa_reg == stack_pointer_rtx);
+
+  rtx dest = SET_DEST (pat);
+  if (REGNO (dest) != STACK_POINTER_REGNUM)
+    gcc_unreachable ();
+
+  mingw_pe_seh_emit_stackalloc (f, seh, reg_offset);
+}
+
+/* Emit SEH directive to save a REG.  */
+
+static void
+aarch64_seh_emit_save (FILE *f, rtx reg, HOST_WIDE_INT cfa_offset)
+{
+  const unsigned int regno = REGNO (reg);
+  fputs ((FP_REGNUM_P (regno) ? " \t.seh_save_freg\t"
+        : GP_REGNUM_P (regno) ?  " \t.seh_save_reg\t"
+        : (gcc_unreachable (), "")), f);
+  if (REGNO (reg) >= V0_REGNUM)
+    fprintf (f, "d%d", REGNO (reg) - V0_REGNUM);
+  else
+    fprintf (f, "x%d", REGNO (reg));
+  fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC " \n", abs (cfa_offset));
+  return;
+}
+
+/* Emit SEH directive to save REG or REG pair.  */
+
+static void
+aarch64_seh_emit_save_pair (FILE *f, HOST_WIDE_INT regno[2],
+                           HOST_WIDE_INT offset)
+{
+  const char *unwind_code_prefix = ".seh_save_";
+  const char *unwind_code_infix = "";
+  bool negative_offset = offset < 0;
+  const char *unwind_code_suffix = negative_offset ? "_x": "";
+  offset = abs (offset);
+
+  if (regno[0] == FP_REGNUM && regno[1] == LR_REGNUM)
+  {
+    unwind_code_infix = "fplr";
+    fprintf (f, "\t%s%s%s\t%ld\n",
+            unwind_code_prefix,
+            unwind_code_infix,
+            unwind_code_suffix,
+            offset);
+    return;
+  }
+
+  const unsigned save_r19r20_x_max_offset = 248;
+  if (regno[0] == R19_REGNUM && regno[1] == R20_REGNUM && negative_offset
+      && offset <= save_r19r20_x_max_offset)
+  {
+    unwind_code_infix = "r19r20";
+    fprintf (f, "\t%s%s%s\t%ld\n",
+            unwind_code_prefix,
+            unwind_code_infix,
+            unwind_code_suffix,
+            offset);
+    return;
+  }
+
+  bool callee_save_reg1 = CALLEE_SAVED_REG_NUMBER (regno[0]);
+  bool callee_save_reg2 = CALLEE_SAVED_REG_NUMBER (regno[1]);
+
+  if (!callee_save_reg1 && !callee_save_reg2)
+    return;
+
+  bool emit_single_register = false;
+  unsigned reg_count = 1;
+  if (callee_save_reg1 && callee_save_reg2)
+  {
+    emit_single_register = (regno[1] - regno[0]) != 1;
+    reg_count += emit_single_register ? 1: 0;
+  }
+  else
+  {
+    emit_single_register = true;
+    if (!callee_save_reg1)
+      regno[0] = regno[1];
+  }
+
+  unwind_code_infix = emit_single_register ? "reg": "regp";
+  char reg_prefix = 'x';
+  unsigned regno_offset = 0;
+  HOST_WIDE_INT offset_increment = 8;
+
+  if (FP_REGNUM_P (regno[0]))
+  {
+    unwind_code_prefix = ".seh_save_f";
+    reg_prefix = 'd';
+    regno_offset = V0_REGNUM;
+  }
+
+  for (unsigned i = 0; i < reg_count; ++i)
+    fprintf (f, "\t%s%s%s\t%c%ld, %ld\n",
+            unwind_code_prefix,
+            unwind_code_infix,
+            unwind_code_suffix,
+            reg_prefix,
+            regno[i] - regno_offset,
+            offset + i * offset_increment);
+}
+
+/* Emmit SEH directive if the pattern is recognized.  */
+
+static void
+aarch64_seh_pattern_emit (FILE *f, struct seh_frame_state *seh, const rtx pat)
+{
+  rtx dest, src;
+
+  if (GET_CODE (pat) == PARALLEL)
+    {
+      HOST_WIDE_INT regno[2];
+      HOST_WIDE_INT offsets[2];
+      unsigned reg_count = 0;
+
+      for (unsigned i = 0, n = XVECLEN (pat, 0); i < n; ++i)
+       {
+         rtx ele = XVECEXP (pat, 0, i);
+
+         if (GET_CODE (ele) != SET)
+           continue;
+
+         src = SET_SRC (ele);
+         dest = SET_DEST (ele);
+         if (GET_CODE (src) != REG || GET_CODE (dest) != MEM)
+           continue;
+
+         dest = XEXP (dest, 0);
+         if (dest == stack_pointer_rtx)
+           offsets[reg_count] = 0;
+         else if (GET_CODE (dest) == PLUS
+                 && XEXP (dest, 0) == stack_pointer_rtx)
+           {
+             if (GET_CODE (XEXP (dest, 1)) != CONST_INT)
+               {
+                 sorry ("unexpected offset type");
+                 return;
+               }
+             offsets[reg_count] = INTVAL (XEXP (dest, 1));
+           }
+         else
+           continue;
+
+
+         if (reg_count == 2)
+           gcc_unreachable ();
+         regno[reg_count] = REGNO (src);
+
+         if (CALLEE_SAVED_REG_NUMBER (regno[reg_count]))
+           ++reg_count;
+       }
+
+      if (reg_count == 1)
+       gcc_unreachable ();
+
+      if (reg_count == 2)
+       {
+         if (offsets[0] > offsets[1])
+           {
+             std::swap (offsets[0], offsets[1]);
+             std::swap (regno[0], regno[1]);
+           }
+         if (offsets[1]- offsets[0] != UNITS_PER_WORD)
+           gcc_unreachable ();
+
+         aarch64_seh_emit_save_pair (f, regno, offsets[0]);
+       }
+
+      return;
+    }
+
+  if (GET_CODE (pat) != SET)
+    return;
+
+  src = SET_SRC (pat);
+  dest = SET_DEST (pat);
+  switch (GET_CODE (dest))
+    {
+    case REG:
+      switch (GET_CODE (src))
+       {
+       case REG:
+         if (dest == hard_frame_pointer_rtx && src == stack_pointer_rtx)
+           fprintf (f, "\t.seh_set_fp\n");
+         break;
+
+       case PLUS:
+         if (dest == stack_pointer_rtx)
+           {
+             if (GET_CODE (XEXP (src, 1)) != CONST_INT)
+               {
+                 sorry ("unexpected offset type");
+                 return;
+               }
+             const HOST_WIDE_INT offset = abs (INTVAL (XEXP (src, 1)));
+             mingw_pe_seh_emit_stackalloc (f, seh, -offset);
+           }
+         break;
+
+       case MEM:
+         src = XEXP (src, 0);
+         if (GET_CODE (src) == PLUS
+             && XEXP (src, 0) == stack_pointer_rtx
+             && CALLEE_SAVED_REG_NUMBER (REGNO (dest)))
+           {
+             if (GET_CODE (XEXP (src, 1)) != CONST_INT)
+               {
+                 sorry ("unexpected offset type");
+                 return;
+               }
+             aarch64_seh_emit_save (f, dest, INTVAL (XEXP (src, 1)));
+           }
+         break;
+
+       default:
+         break;
+       }
+      break;
+
+    case MEM:
+      dest = XEXP (dest, 0);
+
+      if (GET_CODE (dest) == PLUS && XEXP (dest, 0) == stack_pointer_rtx
+         && GET_CODE (src) == REG && CALLEE_SAVED_REG_NUMBER (REGNO (src)))
+       {
+         if (GET_CODE (XEXP (dest, 1)) != CONST_INT)
+           {
+             sorry ("unexpected offset type");
+             return;
+           }
+
+         aarch64_seh_emit_save (f, src, INTVAL (XEXP (dest, 1)));
+       }
+      else if (GET_CODE (dest) == PLUS
+              && XEXP (dest, 0) == hard_frame_pointer_rtx
+              && GET_CODE (src) == UNSPEC
+              && XINT (src, 1) == UNSPEC_STP)
+       {
+         if (GET_CODE (XEXP (dest, 1)) != CONST_INT)
+           {
+             sorry ("unexpected offset type");
+             return;
+           }
+
+         HOST_WIDE_INT offset = INTVAL (XEXP (dest, 1));
+         rtvec vec = XVEC (src, 0);
+         HOST_WIDE_INT regno[2] = { REGNO (RTVEC_ELT (vec, 0)),
+                                    REGNO (RTVEC_ELT (vec, 1))};
+         aarch64_seh_emit_save_pair (f, regno, offset);
+       }
+      break;
+
+    default:
+      break;
+    }
+}
+
+/* This function looks at a single insn and emits any SEH directives
+   required to unwind of this insn.  */
+
+void
+aarch64_pe_seh_unwind_emit (FILE *out_file, rtx_insn *insn)
+{
+  if (!TARGET_SEH || NOTE_P (insn) || !RTX_FRAME_RELATED_P (insn))
+    return;
+
+  struct seh_frame_state *seh = cfun->machine->seh;
+  if (!seh || seh->after_prologue)
+    return;
+
+  rtx pat = PATTERN (insn);
+  for (rtx note = REG_NOTES (insn); note ; note = XEXP (note, 1))
+    {
+      switch (REG_NOTE_KIND (note))
+       {
+       case REG_FRAME_RELATED_EXPR:
+         pat = XEXP (note, 0);
+         break;
+
+       case REG_CFA_ADJUST_CFA:
+         if (XEXP (note, 0))
+           pat = XEXP (note, 0);
+         if (GET_CODE (pat) == PARALLEL)
+           pat = XVECEXP (pat, 0, 0);
+         aarch64_seh_cfa_adjust_cfa (out_file, seh, pat);
+         return;
+       default:
+         break;
+       }
+    }
+
+  aarch64_seh_pattern_emit (out_file, seh, pat);
+}
diff --git a/gcc/config/aarch64/aarch64-abi-ms.h 
b/gcc/config/aarch64/aarch64-abi-ms.h
index 1e1fb3fa400..f25e476b3a8 100644
--- a/gcc/config/aarch64/aarch64-abi-ms.h
+++ b/gcc/config/aarch64/aarch64-abi-ms.h
@@ -31,6 +31,10 @@ along with GCC; see the file COPYING3.  If not see
 #undef  STATIC_CHAIN_REGNUM
 #define STATIC_CHAIN_REGNUM R17_REGNUM
 
+#define CALLEE_SAVED_REG_NUMBER(r)             \
+  (((r) >= R19_REGNUM && (r) <= R30_REGNUM)    \
+   || ((r) >= V8_REGNUM && (r) <= V15_REGNUM))
+
 #define ASM_COMMENT_START "//"
 
 /* ASM_OUTPUT_TYPE_DIRECTIVE is not yet supported by binutils for the
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 9dd19d81d7b..892a0ea9f5c 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -123,6 +123,11 @@
 #define TARGET_AARCH64_MS_ABI 0
 #endif
 
+/* Not on SEH unless explicitly set.  */
+#ifndef TARGET_SEH
+#define TARGET_SEH 0
+#endif
+
 /* Flags that describe how a function shares certain architectural state
    with its callers.
 
@@ -9799,6 +9804,9 @@ offset_12bit_unsigned_scaled_p (machine_mode mode, 
poly_int64 offset)
 static sbitmap
 aarch64_get_separate_components (void)
 {
+  if (TARGET_SEH)
+     return NULL;
+
   aarch64_frame &frame = cfun->machine->frame;
   sbitmap components = sbitmap_alloc (LAST_SAVED_REGNUM + 1);
   bitmap_clear (components);
@@ -13920,7 +13928,7 @@ aarch64_frame_pointer_required ()
 {
   /* If the function needs to record the incoming value of PSTATE.SM,
      make sure that the slot is accessible from the frame pointer.  */
-  return aarch64_need_old_pstate_sm ();
+  return TARGET_SEH || aarch64_need_old_pstate_sm ();
 }
 
 static bool
@@ -19995,6 +20003,10 @@ aarch64_override_options_internal (struct gcc_options 
*opts,
                         aarch64_autovec_preference,
                         opts->x_autovec_preference);
 
+  /* Enable unwind tables for MS ABI.  */
+  if (TARGET_AARCH64_MS_ABI)
+    SET_OPTION_IF_UNSET (opts, &global_options_set, flag_unwind_tables, 1);
+
   aarch64_override_options_after_change_1 (opts);
 }
 
@@ -26634,6 +26646,9 @@ aarch64_declare_function_name (FILE *stream, const 
char* name,
 
   /* Don't forget the type directive for ELF.  */
   ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "function");
+#ifdef SUBTARGET_ASM_UNWIND_INIT
+  SUBTARGET_ASM_UNWIND_INIT (stream);
+#endif
   ASM_OUTPUT_FUNCTION_LABEL (stream, name, fndecl);
 
   cfun->machine->label_is_assembled = true;
diff --git a/gcc/config/aarch64/cygming.h b/gcc/config/aarch64/cygming.h
index bb3c750df69..4ade1fd2085 100644
--- a/gcc/config/aarch64/cygming.h
+++ b/gcc/config/aarch64/cygming.h
@@ -45,14 +45,29 @@ along with GCC; see the file COPYING3.  If not see
 #define SYMBOL_REF_STUBVAR_P(X) \
        ((SYMBOL_REF_FLAGS (X) & SYMBOL_FLAG_STUBVAR) != 0)
 
-/* Disable SEH and declare the required SEH-related macros that are
-still needed for compilation.  */
 #undef TARGET_SEH
-#define TARGET_SEH 0
+#define TARGET_SEH 1
 
 #define SSE_REGNO_P(N) (gcc_unreachable (), 0)
 #define GENERAL_REGNO_P(N) (gcc_unreachable (), 0)
-#define SEH_MAX_FRAME_SIZE (gcc_unreachable (), 0)
+
+/* Support hooks for SEH.  */
+#undef  TARGET_ASM_UNWIND_EMIT
+#define TARGET_ASM_UNWIND_EMIT  aarch64_pe_seh_unwind_emit
+#undef  TARGET_ASM_UNWIND_EMIT_BEFORE_INSN
+#define TARGET_ASM_UNWIND_EMIT_BEFORE_INSN  false
+#undef  TARGET_ASM_FUNCTION_END_PROLOGUE
+#define TARGET_ASM_FUNCTION_END_PROLOGUE  mingw_pe_seh_end_prologue
+#undef  TARGET_ASM_EMIT_EXCEPT_PERSONALITY
+#define TARGET_ASM_EMIT_EXCEPT_PERSONALITY mingw_pe_seh_emit_except_personality
+#undef  TARGET_ASM_INIT_SECTIONS
+#define TARGET_ASM_INIT_SECTIONS  mingw_pe_seh_init_sections
+#undef  TARGET_EXCEPT_UNWIND_INFO
+#define TARGET_EXCEPT_UNWIND_INFO  aarch64_except_unwind_info
+#undef  SUBTARGET_ASM_UNWIND_INIT
+#define SUBTARGET_ASM_UNWIND_INIT  mingw_pe_seh_init
+
+#define SEH_MAX_FRAME_SIZE (1 << 28)
 
 #undef TARGET_PECOFF
 #define TARGET_PECOFF 1
@@ -118,6 +133,7 @@ still needed for compilation.  */
       builtin_define ("__MSVCRT__");                                   \
       builtin_define ("__MINGW32__");                                  \
       builtin_define ("_WIN32");                                       \
+      builtin_define ("__SEH__");                                      \
       builtin_define_std ("WIN32");                                    \
       builtin_define_std ("WINNT");                                    \
       builtin_define_with_int_value ("_INTEGRAL_MAX_BITS",             \
@@ -203,6 +219,14 @@ still needed for compilation.  */
     flag_stack_check = STATIC_BUILTIN_STACK_CHECK;     \
   } while (0)
 
+#undef ASM_DECLARE_FUNCTION_SIZE
+#define ASM_DECLARE_FUNCTION_SIZE(FILE,NAME,DECL) \
+  mingw_pe_end_function (FILE, NAME, DECL)
+
+#undef ASM_DECLARE_COLD_FUNCTION_SIZE
+#define ASM_DECLARE_COLD_FUNCTION_SIZE(FILE,NAME,DECL) \
+  mingw_pe_end_cold_function (FILE, NAME, DECL)
+
 #define SUBTARGET_ATTRIBUTE_TABLE \
   { "selectany", 0, 0, true, false, false, false, \
     mingw_handle_selectany_attribute, NULL },     \
@@ -231,6 +255,14 @@ still needed for compilation.  */
     aarch64_declare_function_name (STREAM, NAME, DECL);                        
\
   } while (0)
 
+#define ASM_DECLARE_COLD_FUNCTION_NAME(FILE, NAME, DECL)       \
+  do                                                           \
+    {                                                          \
+      mingw_pe_declare_type (FILE, NAME, 0, 1);                \
+      mingw_pe_seh_init (FILE);                        \
+      ASM_OUTPUT_LABEL (FILE, NAME);                           \
+    }                                                          \
+  while (0)
 
 /* Define this to be nonzero if static stack checking is supported.  */
 #define STACK_CHECK_STATIC_BUILTIN 1
diff --git a/libgcc/unwind-seh.c b/libgcc/unwind-seh.c
index 3bd0c8a4e03..36da236a9ea 100644
--- a/libgcc/unwind-seh.c
+++ b/libgcc/unwind-seh.c
@@ -30,9 +30,9 @@
 
 #if defined (__SEH__) && !defined (__USING_SJLJ_EXCEPTIONS__)
 
-/* At the moment everything is written for x64, but in theory this could
-   also be used for i386, arm, mips and other extant embedded Windows.  */
-#ifndef __x86_64__
+/* At the moment everything is written for x64 and aarch64, but in theory this
+   could also be used for i386, arm, mips and other extant embedded Windows.  
*/
+#if !defined (__x86_64__) && !defined (__aarch64__)
 #error "Unsupported architecture."
 #endif
 
@@ -209,7 +209,12 @@ _GCC_specific_handler (PEXCEPTION_RECORD ms_exc, void 
*this_frame,
          "installed" the target_ip and RAX value via the arguments
          to RtlUnwindEx.  All that's left is to set the RDX value
          and "continue" to have the context installed.  */
+#ifdef __x86_64__
       ms_disp->ContextRecord->Rdx = ms_exc->ExceptionInformation[3];
+#elif defined (__aarch64__)
+      ms_disp->ContextRecord->X1 = ms_exc->ExceptionInformation[3];
+#endif
+
       return ExceptionContinueSearch;
     }
 
@@ -229,7 +234,11 @@ _GCC_specific_handler (PEXCEPTION_RECORD ms_exc, void 
*this_frame,
       return ExceptionContinueSearch;
     }
 
+#ifdef __x86_64__
   gcc_context.cfa = ms_disp->ContextRecord->Rsp;
+#elif defined (__aarch64__)
+  gcc_context.cfa = ms_disp->ContextRecord->Sp;
+#endif
   gcc_context.ra = ms_disp->ControlPc;
   gcc_context.reg[0] = 0xdeadbeef;     /* These are write-only.  */
   gcc_context.reg[1] = 0xdeadbeef;
@@ -438,6 +447,8 @@ _Unwind_Backtrace(_Unwind_Trace_Fn trace,
   CONTEXT ms_context;
   struct _Unwind_Context gcc_context;
   DISPATCHER_CONTEXT disp_context;
+  ULONG64 ip;
+  ULONG64 sp;
 
   memset (&ms_history, 0, sizeof(ms_history));
   memset (&gcc_context, 0, sizeof(gcc_context));
@@ -452,31 +463,43 @@ _Unwind_Backtrace(_Unwind_Trace_Fn trace,
 
   while (1)
     {
-      gcc_context.disp->ControlPc = ms_context.Rip;
+#ifdef __x86_64__
+      ip = ms_context.Rip;
+#elif defined (__aarch64__)
+      ip = ms_context.Pc;
+#endif
+      gcc_context.disp->ControlPc = ip;
       gcc_context.disp->FunctionEntry
-       = RtlLookupFunctionEntry (ms_context.Rip, &gcc_context.disp->ImageBase,
+       = RtlLookupFunctionEntry (ip, &gcc_context.disp->ImageBase,
                                  &ms_history);
 
       if (!gcc_context.disp->FunctionEntry)
        return _URC_END_OF_STACK;
 
       gcc_context.disp->LanguageHandler
-       = RtlVirtualUnwind (0, gcc_context.disp->ImageBase, ms_context.Rip,
+       = RtlVirtualUnwind (0, gcc_context.disp->ImageBase, ip,
                            gcc_context.disp->FunctionEntry, &ms_context,
                            &gcc_context.disp->HandlerData,
                            &gcc_context.disp->EstablisherFrame, NULL);
 
       /* Set values that the callback can inspect via _Unwind_GetIP
        * and _Unwind_GetCFA. */
-      gcc_context.ra = ms_context.Rip;
-      gcc_context.cfa = ms_context.Rsp;
+#ifdef __x86_64__
+      ip = ms_context.Rip;
+      sp = ms_context.Rsp;
+#elif defined (__aarch64__)
+      ip = ms_context.Pc;
+      sp = ms_context.Sp;
+#endif
+      gcc_context.ra = ip;
+      gcc_context.cfa = sp;
 
       /* Call trace function.  */
       if (trace (&gcc_context, trace_argument) != _URC_NO_REASON)
        return _URC_FATAL_PHASE1_ERROR;
 
       /* ??? Check for invalid stack pointer.  */
-      if (ms_context.Rip == 0)
+      if (ip == 0)
        return _URC_END_OF_STACK;
     }
 }
diff --git a/libstdc++-v3/libsupc++/eh_personality.cc 
b/libstdc++-v3/libsupc++/eh_personality.cc
index 10458318f88..acf93edf648 100644
--- a/libstdc++-v3/libsupc++/eh_personality.cc
+++ b/libstdc++-v3/libsupc++/eh_personality.cc
@@ -23,6 +23,7 @@
 // <http://www.gnu.org/licenses/>.
 
 #include <bits/c++config.h>
+#include <cstdint>
 #include <cstdlib>
 #include <bits/exception_defines.h>
 #include <cxxabi.h>
@@ -54,6 +55,20 @@ parse_lsda_header (_Unwind_Context *context, const unsigned 
char *p,
 
   info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
 
+#if defined (__SEH__) && defined (__aarch64__)
+  /* Large functions on AArch64 (>= 1MB) are split into multiple fragments.
+     However, it is expected the most of the functions will have only one
+     fragment.  The beginning of each SEH handler data in .xdata record starts
+     with a fragment offset and an address to .seh_handlerdata.
+     .seh_handlerdata is emitted only once for the .xdata record of the last
+     fragment.  */
+  uint32_t fragment_offset = *(uint32_t *) p;
+  p += sizeof (uint32_t);
+  info->Start -= fragment_offset;
+  uint32_t handler_data_rva = *(uint32_t *) p;
+  p = (unsigned char *) _Unwind_GetTextRelBase (context) + handler_data_rva;
+#endif
+
   // Find @LPStart, the base to which landing pad offsets are relative.
   lpstart_encoding = *p++;
   if (lpstart_encoding != DW_EH_PE_omit)

Reply via email to