[PATCH] gas: Add AArch64 Windows SEH (pdata/xdata) support

trcrsired <[email protected]> Wed, 5 Aug 2026 02:02:13 +0800
Newsgroups gmane.comp.gnu.binutils
Message-ID <[email protected]>
Implement Windows exception-handling (SEH) unwind info generation for
the aarch64-w64-mingw32 target in GAS.

The existing obj-coff-seh.c only handled x64.  This adds full AArch64
support:

* .seh_proc/.seh_endprologue and all AArch64 unwind directives
  (.seh_alloc_stack, .seh_save_fplr/x, .seh_save_reg/p/p_x,
  .seh_save_freg/p/p_x, .seh_save_lrpair, .seh_set_fp, .seh_add_fp,
  .seh_handler, etc.)
* Correct xdata header layout (FunctionLength / CodeWords / EpilogCount /
  E / X bits) matching the MS ARM64 format
* Emit the xdata extension word when CodeWords==0 and EpilogCount==0 so
  Windows exception parsers do not misread the next entry
* Correct opcode encodings for register save pairs (save_regp etc.)
* Function length computed from fragment positions instead of
  resolve_expression, which failed across fragments
---
 gas/config/obj-coff-seh.c | 1042 ++++++++++++++++++++++++++++++-------
 gas/config/obj-coff-seh.h |   80 ++-
 libiberty/cp-demangle.c   |  276 +++++-----
 3 files changed, 1083 insertions(+), 315 deletions(-)

diff --git a/gas/config/obj-coff-seh.c b/gas/config/obj-coff-seh.c
index 1ac64f8f9ba..b7a63acc2bb 100644
--- a/gas/config/obj-coff-seh.c
+++ b/gas/config/obj-coff-seh.c
@@ -43,6 +43,8 @@ seh_get_target_kind (void)
   switch (bfd_get_arch (stdoutput))
     {
     case bfd_arch_aarch64:
+      return seh_kind_aarch64;
+
     case bfd_arch_arm:
     case bfd_arch_powerpc:
     case bfd_arch_sh:
@@ -176,7 +178,8 @@ obj_coff_seh_handler (int what ATTRIBUTE_UNUSED)
   if (!skip_whitespace_and_comma (0))
     return;
 
-  if (seh_get_target_kind () == seh_kind_x64)
+  if (seh_get_target_kind () == seh_kind_x64
+      || seh_get_target_kind () == seh_kind_aarch64)
     {
       do
 	{
@@ -208,7 +211,7 @@ obj_coff_seh_handler (int what ATTRIBUTE_UNUSED)
 static void
 obj_coff_seh_handlerdata (int what ATTRIBUTE_UNUSED)
 {
-  if (!verify_context_and_target (".seh_handlerdata", seh_kind_x64))
+  if (!verify_context_and_target (".seh_handlerdata", seh_get_target_kind ()))
     return;
   demand_empty_rest_of_line ();
 
@@ -217,6 +220,186 @@ obj_coff_seh_handlerdata (int what ATTRIBUTE_UNUSED)
 
 /* Mark end of current context.  */
 
+static void
+out_one (int byte)
+{
+  char *p = frag_more (1);
+  md_number_to_chars (p, byte, 1);
+}
+
+static void
+out_two (int data)
+{
+  char *p = frag_more (2);
+  md_number_to_chars (p, data, 2);
+}
+
+static void
+out_four (int data)
+{
+  char *p = frag_more (4);
+  md_number_to_chars (p, data, 4);
+}
+
+/* Count the number of slots (shorts) in the x64 unwind codes array.  */
+
+static int
+seh_x64_size_prologue_data (const seh_context *c)
+{
+  int i, ret = 0;
+
+  for (i = c->elems_count - 1; i >= 0; --i)
+    switch (c->elems[i].code)
+      {
+      case UWOP_PUSH_NONVOL:
+      case UWOP_ALLOC_SMALL:
+      case UWOP_SET_FPREG:
+      case UWOP_PUSH_MACHFRAME:
+	ret += 1;
+	break;
+
+      case UWOP_SAVE_NONVOL:
+      case UWOP_SAVE_XMM128:
+	ret += 2;
+	break;
+
+      case UWOP_SAVE_NONVOL_FAR:
+      case UWOP_SAVE_XMM128_FAR:
+	ret += 3;
+	break;
+
+      case UWOP_ALLOC_LARGE:
+	ret += (c->elems[i].info ? 3 : 2);
+	break;
+
+      default:
+	abort ();
+      }
+
+  return ret;
+}
+
+/* Write out the x64 unwind codes array.  */
+
+static void
+seh_x64_write_prologue_data (const seh_context *c)
+{
+  int i;
+
+  /* We have to store in reverse order.  */
+  for (i = c->elems_count - 1; i >= 0; --i)
+    {
+      const seh_prologue_element *e = c->elems + i;
+      expressionS exp;
+
+      /* First comes byte offset in code.  */
+      exp.X_op = O_subtract;
+      exp.X_add_symbol = e->pc_addr;
+      exp.X_op_symbol = c->start_addr;
+      exp.X_add_number = 0;
+      emit_expr (&exp, 1);
+
+      /* Second comes code+info packed into a byte.  */
+      out_one ((e->info << 4) | e->code);
+
+      switch (e->code)
+	{
+	case UWOP_PUSH_NONVOL:
+	case UWOP_ALLOC_SMALL:
+	case UWOP_SET_FPREG:
+	case UWOP_PUSH_MACHFRAME:
+	  /* These have no extra data.  */
+	  break;
+
+	case UWOP_ALLOC_LARGE:
+	  if (e->info)
+	    {
+	case UWOP_SAVE_NONVOL_FAR:
+	case UWOP_SAVE_XMM128_FAR:
+	      /* An unscaled 4 byte offset.  */
+	      out_four (e->off);
+	      break;
+	    }
+	  /* FALLTHRU */
+
+	case UWOP_SAVE_NONVOL:
+	case UWOP_SAVE_XMM128:
+	  /* A scaled 2 byte offset.  */
+	  out_two (e->off);
+	  break;
+
+	default:
+	  abort ();
+	}
+    }
+}
+
+/* Write out the xdata information for one function (x64).  */
+
+static void
+seh_x64_write_function_xdata (seh_context *c)
+{
+  int flags, count_unwind_codes;
+  expressionS exp;
+
+  /* Set 4-byte alignment.  */
+  frag_align (2, 0, 0);
+
+  c->xdata_addr = symbol_temp_new_now ();
+  flags = c->handler_flags;
+  count_unwind_codes = seh_x64_size_prologue_data (c);
+
+  /* ubyte:3 version, ubyte:5 flags.  */
+  out_one ((flags << 3) | 1);
+
+  /* Size of prologue.  */
+  if (c->endprologue_addr)
+    {
+      exp.X_op = O_subtract;
+      exp.X_add_symbol = c->endprologue_addr;
+      exp.X_op_symbol = c->start_addr;
+      exp.X_add_number = 0;
+      emit_expr (&exp, 1);
+    }
+  else
+    out_one (0);
+
+  /* Number of slots (i.e. shorts) in the unwind codes array.  */
+  if (count_unwind_codes > 255)
+    as_fatal (_("too much unwind data in this .seh_proc"));
+  out_one (count_unwind_codes);
+
+  /* ubyte:4 frame-reg, ubyte:4 frame-reg-offset.  */
+  /* Note that frameoff is already a multiple of 16, and therefore
+     the offset is already both scaled and shifted into place.  */
+  out_one (c->frameoff | c->framereg);
+
+  seh_x64_write_prologue_data (c);
+
+  /* We need to align prologue data.  */
+  if (count_unwind_codes & 1)
+    out_two (0);
+
+  if (flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
+    {
+      /* Force the use of segment-relative relocations instead of absolute
+         valued expressions.  Don't adjust for constants (e.g. NULL).  */
+      if (c->handler.X_op == O_symbol)
+        c->handler.X_op = O_symbol_rva;
+      emit_expr (&c->handler, 4);
+    }
+
+  /* Handler data will be tacked in here by subsections.  */
+}
+
+/* Write pdata for an ARM (WinCE-style) function (no-op stub).  */
+
+static void
+seh_arm_write_function_pdata (seh_context *c ATTRIBUTE_UNUSED)
+{
+  abort ();
+}
+
 static void
 do_seh_endproc (void)
 {
@@ -272,7 +455,8 @@ obj_coff_seh_proc (int what ATTRIBUTE_UNUSED)
 
   seh_ctx_cur->code_seg = now_seg;
 
-  if (seh_get_target_kind () == seh_kind_x64)
+  if (seh_get_target_kind () == seh_kind_x64
+      || seh_get_target_kind () == seh_kind_aarch64)
     {
       x_segcur = seh_hash_find_or_make (seh_ctx_cur->code_seg, ".xdata");
       seh_ctx_cur->subsection = x_segcur->subseg;
@@ -565,248 +749,737 @@ obj_coff_seh_setframe (int what ATTRIBUTE_UNUSED)
       seh_x64_make_prologue_element (UWOP_SET_FPREG, 0, 0);
     }
 }
-
-/* Data writing routines.  */
-
-/* Output raw integers in 1, 2, or 4 bytes.  */
-
-static inline void
-out_one (int byte)
-{
-  FRAG_APPEND_1_CHAR (byte);
-}
 
-static inline void
-out_two (int data)
+/* AArch64 support.  */
+
+/* AArch64 register name tables.  */
+static const char * const aarch64_int_regs[31] = {
+  "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+  "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+  "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
+  "x24", "x25", "x26", "x27", "x28", "x29", "x30"
+};
+
+static const char * const aarch64_fp_regs[32] = {
+  "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
+  "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
+  "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
+  "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31"
+};
+
+/* Read an AArch64 integer register from input stream.
+   Returns the register number (0-30) or -1 on error.
+   Also accepts "fp" (x29) and "lr" (x30).  */
+static int
+seh_aarch64_read_int_reg (const char *directive, int min_reg, int max_reg)
 {
-  md_number_to_chars (frag_more (2), data, 2);
-}
+  char name_end;
+  char *symbol_name;
+  int i;
 
-static inline void
-out_four (int data)
-{
-  md_number_to_chars (frag_more (4), data, 4);
-}
+  SKIP_WHITESPACE ();
+  name_end = get_symbol_name (&symbol_name);
 
-/* Write out prologue data for x64.  */
+  /* Check for special names.  */
+  if (strcasecmp (symbol_name, "fp") == 0)
+    i = 29;
+  else if (strcasecmp (symbol_name, "lr") == 0)
+    i = 30;
+  else
+    {
+      for (i = 0; i < 31; i++)
+	if (!strcasecmp (aarch64_int_regs[i], symbol_name))
+	  break;
+    }
 
-static void
-seh_x64_write_prologue_data (const seh_context *c)
-{
-  int i;
+  (void) restore_line_pointer (name_end);
 
-  /* We have to store in reverse order.  */
-  for (i = c->elems_count - 1; i >= 0; --i)
+  if (i > max_reg || i < min_reg)
     {
-      const seh_prologue_element *e = c->elems + i;
-      expressionS exp;
+      as_bad (_("invalid register for %s"), directive);
+      return -1;
+    }
 
-      /* First comes byte offset in code.  */
-      exp.X_op = O_subtract;
-      exp.X_add_symbol = e->pc_addr;
-      exp.X_op_symbol = c->start_addr;
-      exp.X_add_number = 0;
-      emit_expr (&exp, 1);
+  return i;
+}
 
-      /* Second comes code+info packed into a byte.  */
-      out_one ((e->info << 4) | e->code);
+/* Read an AArch64 FP/SIMD register from input stream.
+   Returns the register number (0-31) or -1 on error.  */
+static int
+seh_aarch64_read_fp_reg (const char *directive)
+{
+  char name_end;
+  char *symbol_name;
+  int i;
 
-      switch (e->code)
-	{
-	case UWOP_PUSH_NONVOL:
-	case UWOP_ALLOC_SMALL:
-	case UWOP_SET_FPREG:
-	case UWOP_PUSH_MACHFRAME:
-	  /* These have no extra data.  */
-	  break;
+  SKIP_WHITESPACE ();
+  name_end = get_symbol_name (&symbol_name);
 
-	case UWOP_ALLOC_LARGE:
-	  if (e->info)
-	    {
-	case UWOP_SAVE_NONVOL_FAR:
-	case UWOP_SAVE_XMM128_FAR:
-	      /* An unscaled 4 byte offset.  */
-	      out_four (e->off);
-	      break;
-	    }
-	  /* FALLTHRU */
+  for (i = 0; i < 32; i++)
+    if (!strcasecmp (aarch64_fp_regs[i], symbol_name))
+      break;
 
-	case UWOP_SAVE_NONVOL:
-	case UWOP_SAVE_XMM128:
-	  /* A scaled 2 byte offset.  */
-	  out_two (e->off);
-	  break;
+  (void) restore_line_pointer (name_end);
 
-	default:
-	  abort ();
-	}
+  if (i == 32)
+    {
+      as_bad (_("invalid floating-point register for %s"), directive);
+      return -1;
     }
+
+  return i;
 }
 
-static int
-seh_x64_size_prologue_data (const seh_context *c)
+/* Add a prologue element to the AArch64 SEH context.  */
+static void
+seh_aarch64_make_prologue_element (int code, int info, offsetT off)
 {
-  int i, ret = 0;
+  seh_prologue_element *n;
 
-  for (i = c->elems_count - 1; i >= 0; --i)
-    switch (c->elems[i].code)
-      {
-      case UWOP_PUSH_NONVOL:
-      case UWOP_ALLOC_SMALL:
-      case UWOP_SET_FPREG:
-      case UWOP_PUSH_MACHFRAME:
-	ret += 1;
-	break;
+  if (seh_ctx_cur == NULL)
+    return;
+  if (seh_ctx_cur->elems_count == seh_ctx_cur->elems_max)
+    {
+      seh_ctx_cur->elems_max += 8;
+      seh_ctx_cur->elems = XRESIZEVEC (seh_prologue_element,
+				       seh_ctx_cur->elems,
+				       seh_ctx_cur->elems_max);
+    }
 
-      case UWOP_SAVE_NONVOL:
-      case UWOP_SAVE_XMM128:
-	ret += 2;
-	break;
+  n = &seh_ctx_cur->elems[seh_ctx_cur->elems_count++];
+  n->code = code;
+  n->info = info;
+  n->off = off;
+  n->pc_addr = symbol_temp_new_now ();
+}
 
-      case UWOP_SAVE_NONVOL_FAR:
-      case UWOP_SAVE_XMM128_FAR:
-	ret += 3;
-	break;
+/* .seh_save_regp <reg1>, <reg2>, <offset> (aarch64)
+   Save register pair at offset from SP.  */
+static void
+obj_coff_seh_aarch64_save_regp (int what ATTRIBUTE_UNUSED)
+{
+  int reg1, reg2;
+  offsetT off;
 
-      case UWOP_ALLOC_LARGE:
-	ret += (c->elems[i].info ? 3 : 2);
-	break;
+  if (!verify_context_and_target (".seh_save_regp", seh_kind_aarch64)
+      || !seh_validate_seg (".seh_save_regp"))
+    return;
 
-      default:
-	abort ();
-      }
+  reg1 = seh_aarch64_read_int_reg (".seh_save_regp", 0, 30);
+  if (!skip_whitespace_and_comma (1))
+    return;
+  reg2 = seh_aarch64_read_int_reg (".seh_save_regp", 0, 30);
+  if (!skip_whitespace_and_comma (1))
+    return;
+  off = get_absolute_expression ();
+  demand_empty_rest_of_line ();
 
-  return ret;
+  if (reg1 < 0 || reg2 < 0)
+    return;
+  if (off < 0 || (off & 7))
+    {
+      as_bad (_(".seh_save_regp offset must be non-negative and 8-byte aligned"));
+      return;
+    }
+
+  /* Check for special case: saving x29,x30 (FPLR pair).  */
+  if (reg1 == 29 && reg2 == 30)
+    {
+      if (off <= 0x3F * 8)
+	{
+	  seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_FPLR, off >> 3, 0);
+	  return;
+	}
+    }
+
+  seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_REG_P, reg1, off);
 }
 
-/* Write out the xdata information for one function (x64).  */
+/* .seh_save_fregp <reg1>, <reg2>, <offset> (aarch64)
+   Save FP/SIMD register pair.  */
+static void
+obj_coff_seh_aarch64_save_fregp (int what ATTRIBUTE_UNUSED)
+{
+  int reg1, reg2;
+  offsetT off;
+
+  if (!verify_context_and_target (".seh_save_fregp", seh_kind_aarch64)
+      || !seh_validate_seg (".seh_save_fregp"))
+    return;
+
+  reg1 = seh_aarch64_read_fp_reg (".seh_save_fregp");
+  if (!skip_whitespace_and_comma (1))
+    return;
+  reg2 = seh_aarch64_read_fp_reg (".seh_save_fregp");
+  if (!skip_whitespace_and_comma (1))
+    return;
+  off = get_absolute_expression ();
+  demand_empty_rest_of_line ();
+
+  if (reg1 < 0 || reg2 < 0)
+    return;
+  if (off < 0 || (off & 7))
+    {
+      as_bad (_(".seh_save_fregp offset must be non-negative and 8-byte aligned"));
+      return;
+    }
+
+  seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_FREG_P, reg1, off);
+}
 
+/* .seh_save_reg <reg>, <offset> (aarch64)
+   Save a single integer register.  */
 static void
-seh_x64_write_function_xdata (seh_context *c)
+obj_coff_seh_aarch64_save_reg (int what ATTRIBUTE_UNUSED)
 {
-  int flags, count_unwind_codes;
-  expressionS exp;
+  int reg;
+  offsetT off;
 
-  /* Set 4-byte alignment.  */
-  frag_align (2, 0, 0);
+  if (!verify_context_and_target (".seh_save_reg", seh_kind_aarch64)
+      || !seh_validate_seg (".seh_save_reg"))
+    return;
 
-  c->xdata_addr = symbol_temp_new_now ();
-  flags = c->handler_flags;
-  count_unwind_codes = seh_x64_size_prologue_data (c);
+  reg = seh_aarch64_read_int_reg (".seh_save_reg", 0, 30);
+  if (!skip_whitespace_and_comma (1))
+    return;
+  off = get_absolute_expression ();
+  demand_empty_rest_of_line ();
 
-  /* ubyte:3 version, ubyte:5 flags.  */
-  out_one ((flags << 3) | 1);
+  if (reg < 0)
+    return;
+  if (off < 0 || (off & 7))
+    {
+      as_bad (_(".seh_save_reg offset must be non-negative and 8-byte aligned"));
+      return;
+    }
 
-  /* Size of prologue.  */
-  if (c->endprologue_addr)
+  seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_REG, reg, off);
+}
+
+/* .seh_save_freg <reg>, <offset> (aarch64)
+   Save a single FP/SIMD register.  */
+static void
+obj_coff_seh_aarch64_save_freg (int what ATTRIBUTE_UNUSED)
+{
+  int reg;
+  offsetT off;
+
+  if (!verify_context_and_target (".seh_save_freg", seh_kind_aarch64)
+      || !seh_validate_seg (".seh_save_freg"))
+    return;
+
+  reg = seh_aarch64_read_fp_reg (".seh_save_freg");
+  if (!skip_whitespace_and_comma (1))
+    return;
+  off = get_absolute_expression ();
+  demand_empty_rest_of_line ();
+
+  if (reg < 0)
+    return;
+  if (off < 0 || (off & 7))
     {
-      exp.X_op = O_subtract;
-      exp.X_add_symbol = c->endprologue_addr;
-      exp.X_op_symbol = c->start_addr;
-      exp.X_add_number = 0;
-      emit_expr (&exp, 1);
+      as_bad (_(".seh_save_freg offset must be non-negative and 8-byte aligned"));
+      return;
     }
-  else
-    out_one (0);
 
-  /* Number of slots (i.e. shorts) in the unwind codes array.  */
-  if (count_unwind_codes > 255)
-    as_fatal (_("too much unwind data in this .seh_proc"));
-  out_one (count_unwind_codes);
+  seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_FREG, reg, off);
+}
 
-  /* ubyte:4 frame-reg, ubyte:4 frame-reg-offset.  */
-  /* Note that frameoff is already a multiple of 16, and therefore
-     the offset is already both scaled and shifted into place.  */
-  out_one (c->frameoff | c->framereg);
+/* .seh_save_fplr <offset> (aarch64)
+   Save x29 (FP) and x30 (LR) at a positive offset from SP.  */
+static void
+obj_coff_seh_aarch64_save_fplr (int what ATTRIBUTE_UNUSED)
+{
+  offsetT off;
 
-  seh_x64_write_prologue_data (c);
+  if (!verify_context_and_target (".seh_save_fplr", seh_kind_aarch64)
+      || !seh_validate_seg (".seh_save_fplr"))
+    return;
 
-  /* We need to align prologue data.  */
-  if (count_unwind_codes & 1)
-    out_two (0);
+  off = get_absolute_expression ();
+  demand_empty_rest_of_line ();
 
-  if (flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
+  if (off < 0 || (off & 7))
     {
-      /* Force the use of segment-relative relocations instead of absolute
-         valued expressions.  Don't adjust for constants (e.g. NULL).  */
-      if (c->handler.X_op == O_symbol)
-        c->handler.X_op = O_symbol_rva;
-      emit_expr (&c->handler, 4);
+      as_bad (_(".seh_save_fplr offset must be non-negative and 8-byte aligned"));
+      return;
     }
 
-  /* Handler data will be tacked in here by subsections.  */
+  seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_FPLR, off >> 3, 0);
 }
 
-/* Write out xdata for one function.  */
+/* .seh_save_fplr_x <offset> (aarch64)
+   Save x29 (FP) and x30 (LR) with predecrement (stp x29, x30, [sp, #-N]!).  */
+static void
+obj_coff_seh_aarch64_save_fplr_x (int what ATTRIBUTE_UNUSED)
+{
+  offsetT off;
+
+  if (!verify_context_and_target (".seh_save_fplr_x", seh_kind_aarch64)
+      || !seh_validate_seg (".seh_save_fplr_x"))
+    return;
+
+  off = get_absolute_expression ();
+  demand_empty_rest_of_line ();
+
+  if (off < 0 || (off & 15))
+    {
+      as_bad (_(".seh_save_fplr_x offset must be negative and 16-byte aligned"));
+      return;
+    }
+  if (off > 0x3F * 8)
+    {
+      as_bad (_(".seh_save_fplr_x offset out of range (max 504)"));
+      return;
+    }
 
+  seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_FPLRX, (off >> 3) - 1, 0);
+}
+
+/* .seh_save_lrpair <reg>, <offset> (aarch64)
+   Save x30 (LR) and another register at offset from SP.  */
 static void
-write_function_xdata (seh_context *c)
+obj_coff_seh_aarch64_save_lrpair (int what ATTRIBUTE_UNUSED)
 {
-  segT save_seg = now_seg;
-  int save_subseg = now_subseg;
+  int reg;
+  offsetT off;
+
+  if (!verify_context_and_target (".seh_save_lrpair", seh_kind_aarch64)
+      || !seh_validate_seg (".seh_save_lrpair"))
+    return;
+
+  reg = seh_aarch64_read_int_reg (".seh_save_lrpair", 0, 28);
+  if (!skip_whitespace_and_comma (1))
+    return;
+  off = get_absolute_expression ();
+  demand_empty_rest_of_line ();
 
-  /* MIPS, SH, ARM don't have xdata.  */
-  if (seh_get_target_kind () != seh_kind_x64)
+  if (reg < 0)
     return;
+  if (off < 0 || (off & 7))
+    {
+      as_bad (_(".seh_save_lrpair offset must be non-negative and 8-byte aligned"));
+      return;
+    }
 
-  switch_xdata (c->subsection, c->code_seg);
+  seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_LRPAIR, (reg - 19) >> 1, off >> 3);
+}
 
-  seh_x64_write_function_xdata (c);
+/* .seh_set_fp (aarch64)
+   Set frame pointer (mov x29, sp).  */
+static void
+obj_coff_seh_aarch64_set_fp (int what ATTRIBUTE_UNUSED)
+{
+  if (!verify_context_and_target (".seh_set_fp", seh_kind_aarch64)
+      || !seh_validate_seg (".seh_set_fp"))
+    return;
+  demand_empty_rest_of_line ();
 
-  subseg_set (save_seg, save_subseg);
+  seh_aarch64_make_prologue_element (AARCH64_UOP_SET_FP, 0, 0);
 }
 
-/* Write pdata section data for one function (arm).  */
+/* .seh_add_fp <offset> (aarch64)
+   Add offset to frame pointer (add x29, sp, #N).  */
+static void
+obj_coff_seh_aarch64_add_fp (int what ATTRIBUTE_UNUSED)
+{
+  offsetT off;
+
+  if (!verify_context_and_target (".seh_add_fp", seh_kind_aarch64)
+      || !seh_validate_seg (".seh_add_fp"))
+    return;
+
+  off = get_absolute_expression ();
+  demand_empty_rest_of_line ();
+
+  seh_aarch64_make_prologue_element (AARCH64_UOP_ADD_FP, off >> 3, 0);
+}
 
+/* .seh_nop (aarch64)
+   No-op padding in the unwind code.  */
 static void
-seh_arm_write_function_pdata (seh_context *c)
+obj_coff_seh_aarch64_nop (int what ATTRIBUTE_UNUSED)
 {
-  expressionS exp;
-  unsigned int prol_len = 0, func_len = 0;
-  unsigned int val;
-
-  /* Start address of the function.  */
-  exp.X_op = O_symbol;
-  exp.X_add_symbol = c->start_addr;
-  exp.X_add_number = 0;
-  emit_expr (&exp, 4);
-
-  exp.X_op = O_subtract;
-  exp.X_add_symbol = c->end_addr;
-  exp.X_op_symbol = c->start_addr;
-  exp.X_add_number = 0;
-  if (resolve_expression (&exp) && exp.X_op == O_constant)
-    func_len = exp.X_add_number;
-  else
-    as_bad (_(".seh_endproc in a different section from .seh_proc"));
+  if (!verify_context_and_target (".seh_nop", seh_kind_aarch64)
+      || !seh_validate_seg (".seh_nop"))
+    return;
+  demand_empty_rest_of_line ();
 
-  if (c->endprologue_addr)
+  seh_aarch64_make_prologue_element (AARCH64_UOP_NOP, 0, 0);
+}
+
+/* .seh_alloc_stack <size> (aarch64)
+   Allocate stack space.  */
+static void
+obj_coff_seh_aarch64_alloc_stack (int what ATTRIBUTE_UNUSED)
+{
+  offsetT off;
+  int code, info;
+
+  if (!verify_context_and_target (".seh_alloc_stack", seh_kind_aarch64)
+      || !seh_validate_seg (".seh_alloc_stack"))
+    return;
+
+  off = get_absolute_expression ();
+  demand_empty_rest_of_line ();
+
+  if (off == 0)
+    return;
+  if (off < 0)
     {
-      exp.X_op = O_subtract;
-      exp.X_add_symbol = c->endprologue_addr;
-      exp.X_op_symbol = c->start_addr;
-      exp.X_add_number = 0;
+      as_bad (_(".seh_alloc_stack offset is negative"));
+      return;
+    }
+
+  if ((off & 15) == 0 && off <= 0x1F * 16)
+    {
+      code = AARCH64_UOP_ALLOC_SMALL;
+      info = (off / 16) - 1;
+    }
+  else if ((off & 15) == 0 && off <= 0x7FF * 16)
+    {
+      code = AARCH64_UOP_ALLOC_MEDIUM;
+      info = off / 16;
+    }
+  else if ((off & 15) == 0 && off <= (offsetT) 0xFFFFFF * 16)
+    {
+      code = AARCH64_UOP_ALLOC_LARGE;
+      info = 0;
+      off = off / 16;
+    }
+  else
+    {
+      as_bad (_(".seh_alloc_stack offset out of range"));
+      return;
+    }
 
-      if (resolve_expression (&exp) && exp.X_op == O_constant)
-	prol_len = exp.X_add_number;
+  seh_aarch64_make_prologue_element (code, info, off);
+}
+
+/* AArch64 xdata writing.  */
+
+/* Count the size of AArch64 unwind code data in bytes.  */
+static int
+seh_aarch64_size_prologue_data (const seh_context *c)
+{
+  int i, ret = 0;
+
+  for (i = c->elems_count - 1; i >= 0; --i)
+    {
+      int code = c->elems[i].code;
+      if (code == AARCH64_UOP_ALLOC_SMALL
+	  || code == AARCH64_UOP_SAVE_R19R20X
+	  || code == AARCH64_UOP_SAVE_FPLRX
+	  || code == AARCH64_UOP_SAVE_FPLR
+	  || code == AARCH64_UOP_SET_FP
+	  || code == AARCH64_UOP_NOP
+	  || code == AARCH64_UOP_END
+	  || code == AARCH64_UOP_SAVE_NEXT
+	  || code == AARCH64_UOP_TRAP_FRAME
+	  || code == AARCH64_UOP_PUSH_MACH
+	  || code == AARCH64_UOP_CONTEXT
+	  || code == AARCH64_UOP_EC_CONTEXT
+	  || code == AARCH64_UOP_CLEAR_UNWOUND_TO_CALL
+	  || code == AARCH64_UOP_PAC_SIGN_LR)
+	ret += 1;
+      else if (code == AARCH64_UOP_ALLOC_MEDIUM
+	       || code == AARCH64_UOP_SAVE_REG
+	       || code == AARCH64_UOP_SAVE_REG_X
+	       || code == AARCH64_UOP_SAVE_REG_P
+	       || code == AARCH64_UOP_SAVE_REG_PX
+	       || code == AARCH64_UOP_SAVE_LRPAIR
+	       || code == AARCH64_UOP_SAVE_FREG
+	       || code == AARCH64_UOP_SAVE_FREG_X
+	       || code == AARCH64_UOP_SAVE_FREG_P
+	       || code == AARCH64_UOP_SAVE_FREG_PX
+	       || code == AARCH64_UOP_ADD_FP)
+	ret += 2;
+      else if (code == AARCH64_UOP_ALLOC_LARGE
+	       || code == AARCH64_UOP_SAVE_ANY_REG_I
+	       || code == AARCH64_UOP_SAVE_ANY_REG_IP
+	       || code == AARCH64_UOP_SAVE_ANY_REG_D
+	       || code == AARCH64_UOP_SAVE_ANY_REG_DP
+	       || code == AARCH64_UOP_SAVE_ANY_REG_Q
+	       || code == AARCH64_UOP_SAVE_ANY_REG_QP)
+	ret += 3;
       else
-	as_bad (_(".seh_endprologue in a different section from .seh_proc"));
+	abort ();
     }
 
-  /* Both function and prologue are in units of instructions.  */
-  func_len >>= (c->use_instruction_32 ? 2 : 1);
-  prol_len >>= (c->use_instruction_32 ? 2 : 1);
+  return ret;
+}
+
+/* Write out AArch64 prologue unwind codes.  */
+static void
+seh_aarch64_write_prologue_data (const seh_context *c)
+{
+  int i;
 
-  /* Assemble the second word of the pdata.  */
-  val  = prol_len & 0xff;
-  val |= (func_len & 0x3fffff) << 8;
-  if (c->use_instruction_32)
-    val |= 0x40000000U;
-  if (c->handler_written)
-    val |= 0x80000000U;
-  out_four (val);
+  /* We have to store in reverse order.  */
+  for (i = c->elems_count - 1; i >= 0; --i)
+    {
+      const seh_prologue_element *e = c->elems + i;
+
+      switch (e->code)
+	{
+	case AARCH64_UOP_ALLOC_SMALL:
+	  out_one (AARCH64_UOP_ALLOC_SMALL | (e->info & 0x1f));
+	  break;
+
+	case AARCH64_UOP_ALLOC_MEDIUM:
+	  out_one (AARCH64_UOP_ALLOC_MEDIUM | ((e->info >> 8) & 3));
+	  out_one (e->info & 0xff);
+	  break;
+
+	case AARCH64_UOP_ALLOC_LARGE:
+	  out_one (AARCH64_UOP_ALLOC_LARGE);
+	  out_two (e->off);
+	  break;
+
+	case AARCH64_UOP_SAVE_R19R20X:
+	  out_one (AARCH64_UOP_SAVE_R19R20X | (e->info & 0x1f));
+	  break;
+
+	case AARCH64_UOP_SAVE_FPLRX:
+	  out_one (AARCH64_UOP_SAVE_FPLRX | (e->info & 0x3f));
+	  break;
+
+	case AARCH64_UOP_SAVE_FPLR:
+	  out_one (AARCH64_UOP_SAVE_FPLR | (e->info & 0x3f));
+	  break;
+
+	case AARCH64_UOP_SAVE_REG:
+	  {
+	    int r = e->info - 19;
+	    out_one (AARCH64_UOP_SAVE_REG | ((r & 0xC) >> 2));
+	    out_one (((r & 0x3) << 6) | ((e->off >> 3) & 0x3f));
+	  }
+	  break;
+
+	case AARCH64_UOP_SAVE_REG_X:
+	  {
+	    int r = e->info - 19;
+	    out_one (AARCH64_UOP_SAVE_REG_X | ((r & 0x8) >> 3));
+	    out_one (((r & 0x7) << 5) | (((e->off >> 3) - 1) & 0x1f));
+	  }
+	  break;
+
+	case AARCH64_UOP_SAVE_REG_P:
+	  {
+	    int r = e->info - 19;
+	    out_one (AARCH64_UOP_SAVE_REG_P | ((r & 0xC) >> 2));
+	    out_one (((r & 0x3) << 6) | ((e->off >> 3) & 0x3f));
+	  }
+	  break;
+
+	case AARCH64_UOP_SAVE_REG_PX:
+	  {
+	    int r = e->info - 19;
+	    out_one (AARCH64_UOP_SAVE_REG_PX | ((r & 0xC) >> 2));
+	    out_one (((r & 0x3) << 6) | (((e->off >> 3) - 1) & 0x3f));
+	  }
+	  break;
+
+	case AARCH64_UOP_SAVE_LRPAIR:
+	  {
+	    int r = e->info - 19;
+	    out_one (AARCH64_UOP_SAVE_LRPAIR | ((r & 0xC) >> 2));
+	    out_one (((r & 0x3) << 6) | ((e->off >> 3) & 0x3f));
+	  }
+	  break;
+
+	case AARCH64_UOP_SAVE_FREG:
+	  {
+	    int r = e->info - 8;
+	    out_one (AARCH64_UOP_SAVE_FREG | ((r & 0x4) >> 2));
+	    out_one (((r & 0x3) << 6) | ((e->off >> 3) & 0x3f));
+	  }
+	  break;
+
+	case AARCH64_UOP_SAVE_FREG_X:
+	  {
+	    int r = e->info - 8;
+	    out_one (AARCH64_UOP_SAVE_FREG_X);
+	    out_one (((r & 0x7) << 5) | (((e->off >> 3) - 1) & 0x1f));
+	  }
+	  break;
+
+	case AARCH64_UOP_SAVE_FREG_P:
+	  {
+	    int r = e->info - 8;
+	    out_one (AARCH64_UOP_SAVE_FREG_P | ((r & 0x4) >> 2));
+	    out_one (((r & 0x3) << 6) | ((e->off >> 3) & 0x3f));
+	  }
+	  break;
+
+	case AARCH64_UOP_SAVE_FREG_PX:
+	  {
+	    int r = e->info - 8;
+	    out_one (AARCH64_UOP_SAVE_FREG_PX | ((r & 0x4) >> 2));
+	    out_one (((r & 0x3) << 6) | (((e->off >> 3) - 1) & 0x3f));
+	  }
+	  break;
+
+	case AARCH64_UOP_SET_FP:
+	case AARCH64_UOP_NOP:
+	case AARCH64_UOP_END:
+	case AARCH64_UOP_SAVE_NEXT:
+	case AARCH64_UOP_TRAP_FRAME:
+	case AARCH64_UOP_PUSH_MACH:
+	case AARCH64_UOP_CONTEXT:
+	case AARCH64_UOP_EC_CONTEXT:
+	case AARCH64_UOP_CLEAR_UNWOUND_TO_CALL:
+	case AARCH64_UOP_PAC_SIGN_LR:
+	  out_one (e->code);
+	  break;
+
+	case AARCH64_UOP_ADD_FP:
+	  out_one (AARCH64_UOP_ADD_FP);
+	  out_one (e->info & 0xff);
+	  break;
+
+	default:
+	  abort ();
+	}
+    }
+
+  /* Terminate with END opcode.  */
+  out_one (AARCH64_UOP_END);
+}
+
+/* Write the xdata for one AArch64 function.  */
+static void
+seh_aarch64_write_function_xdata (seh_context *c)
+{
+  int code_words, epilog_count;
+  unsigned int func_length;
+
+  /* 4-byte alignment.  */
+  frag_align (2, 0, 0);
+
+  c->xdata_addr = symbol_temp_new_now ();
+
+  /* Calculate function length in 4-byte units.  Compute it from the
+     symbols' fragment positions rather than resolve_expression, which
+     can fail when the symbols span multiple fragments.  */
+  {
+    addressT off1, off2;
+    fragS *f1 = symbol_get_frag_and_value (c->start_addr, &off1);
+    fragS *f2 = symbol_get_frag_and_value (c->end_addr, &off2);
+    long bytes = 0;
+
+    if (f1 == f2)
+      bytes = off2 - off1;
+    else
+      {
+	/* Sum fragment sizes from f1 to f2.  */
+	bytes = off2;
+	for (fragS *f = f1; f && f != f2; f = f->fr_next)
+	  {
+	    if (!f->fr_next)
+	      break;
+	    bytes += f->fr_fix;
+	    if (f->fr_var > 0)
+	      bytes += f->fr_var * f->fr_subtype;
+	  }
+	bytes -= off1;
+      }
+    func_length = bytes < 0 ? 0 : bytes >> 2;
+  }
+
+  /* Count unwind code bytes, including the terminating END.  */
+  int code_bytes = seh_aarch64_size_prologue_data (c) + 1;
+  code_words = (code_bytes + 3) / 4;
+
+  /* A function with no prologue codes only needs the END marker.
+     In that case, code_words is 0 and no unwind code bytes are emitted.  */
+  bool no_unwind_codes = (code_bytes == 1);
+  if (no_unwind_codes)
+    code_words = 0;
+
+  /* Header word (Microsoft ARM64 SEH xdata format, matching LLVM MCWin64EH):
+     bits [0:17]  = Function Length / 4 (18 bits)
+     bit  [20]    = X (Exception Handler Present)
+     bit  [21]    = E (Packed Epilog Present)
+     bits [26:22] = Epilog Count (5 bits)
+     bits [31:27] = Code Words (5 bits) */
+  epilog_count = 0;
+  unsigned int header = func_length & 0x3ffff;
+  if (code_words > 0x1f)
+    {
+      header |= (0 << 27);
+      header |= ((epilog_count & 0x1f) << 22);
+    }
+  else
+    {
+      header |= ((code_words & 0x1f) << 27);
+      header |= ((epilog_count & 0x1f) << 22);
+    }
+  if (c->handler_flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
+    header |= (1 << 20);
+
+  out_four (header);
+
+  /* If extended code words needed, emit extension word.
+     bits [15:0] = Epilog Count, bits [23:16] = Code Words.
+     Also emit it when both the code words and epilog count are zero:
+     in that case Windows (and Wine) treat the packed fields as zero and
+     read the actual values from the extension word.  */
+  if (code_words > 0x1f || no_unwind_codes)
+    {
+      unsigned int ext = ((code_words & 0xff) << 16)
+                         | (epilog_count & 0xffff);
+      out_four (ext);
+    }
+
+  /* Write prologue unwind codes (skipped when no_unwind_codes).  */
+  if (!no_unwind_codes)
+    seh_aarch64_write_prologue_data (c);
+
+  /* Pad to 4-byte alignment.  Only needed when unwind codes are present.  */
+  if (!no_unwind_codes)
+    {
+      int remainder = (code_bytes) & 3;
+      if (remainder)
+        for (int i = 0; i < 4 - remainder; i++)
+          out_one (AARCH64_UOP_NOP);
+    }
+
+  /* If exception handler present, emit it.  */
+  if (c->handler_flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
+    {
+      if (c->handler.X_op == O_symbol)
+	c->handler.X_op = O_symbol_rva;
+      emit_expr (&c->handler, 4);
+    }
+
+  /* Handler data follows in subsections.  */
+}
+
+/* Write out xdata for one function.  */
+
+static void
+write_function_xdata (seh_context *c)
+{
+  segT save_seg = now_seg;
+  int save_subseg = now_subseg;
+
+  if (seh_get_target_kind () == seh_kind_x64)
+    {
+      switch_xdata (c->subsection, c->code_seg);
+      seh_x64_write_function_xdata (c);
+    }
+  else if (seh_get_target_kind () == seh_kind_aarch64)
+    {
+      switch_xdata (c->subsection, c->code_seg);
+      seh_aarch64_write_function_xdata (c);
+    }
+
+  subseg_set (save_seg, save_subseg);
 }
 
+
+
 /* Write out pdata for one function.  */
 
 static void
@@ -858,6 +1531,17 @@ write_function_pdata (seh_context *c)
       seh_arm_write_function_pdata (c);
       break;
 
+    case seh_kind_aarch64:
+      exp.X_op = O_symbol_rva;
+      exp.X_add_number = 0;
+      exp.X_add_symbol = c->start_addr;
+      emit_expr (&exp, 4);
+      exp.X_op = O_symbol_rva;
+      exp.X_add_number = 0;
+      exp.X_add_symbol = c->xdata_addr;
+      emit_expr (&exp, 4);
+      break;
+
     default:
       abort ();
     }
diff --git a/gas/config/obj-coff-seh.h b/gas/config/obj-coff-seh.h
index 8a3e1d602aa..f1b3535d138 100644
--- a/gas/config/obj-coff-seh.h
+++ b/gas/config/obj-coff-seh.h
@@ -25,6 +25,8 @@
   The third is the IA64 and x64 version. Note, the IA64 isn't implemented yet,
   but to find information about it, please see specification about IA64 on
   http://download.intel.com/design/Itanium/Downloads/245358.pdf file.
+  The fourth is for AArch64 (ARM64) Windows, which uses the same pdata/xdata
+  model as x64 but with different unwind codes and a 2-word pdata entry.
 
   The first version has just entries in the pdata section: BeginAddress,
   EndAddress, ExceptionHandler, HandlerData, and PrologueEndAddress. Each
@@ -41,10 +43,14 @@
   prologue, exception-handler, and additional SEH data is stored
   within the UNWIND_DATA field in the xdata section.
 
+  The fourth (AArch64/ARM64) version has a 2-word pdata entry:
+  BeginAddress (RVA) and UnwindData (RVA), with xdata using ARM64-specific
+  unwind codes.
+
   The pseudos:
   .seh_proc <fct_name>
   .seh_endprologue
-  .seh_handler <handler>[,@unwind][,@except]	(x64)
+  .seh_handler <handler>[,@unwind][,@except]	(x64, aarch64)
   .seh_handler <handler>[,<handler_data>]	(others)
   .seh_handlerdata
   .seh_eh
@@ -57,6 +63,17 @@
   .seh_savexmm
   .seh_pushframe
   .seh_code
+  .seh_save_regp <reg1>,<reg2>,<offset>		(aarch64)
+  .seh_save_fregp <reg1>,<reg2>,<offset>	(aarch64)
+  .seh_save_reg <reg>,<offset>			(aarch64)
+  .seh_save_freg <reg>,<offset>			(aarch64)
+  .seh_save_fplr <offset>			(aarch64)
+  .seh_save_fplr_x <offset>			(aarch64)
+  .seh_save_lrpair <reg>,<offset>		(aarch64)
+  .seh_set_fp					(aarch64)
+  .seh_add_fp <offset>				(aarch64)
+  .seh_nop					(aarch64)
+  .seh_alloc_stack <size>			(aarch64)
 */
 
 #ifndef OBJ_COFF_SEH_H
@@ -78,7 +95,18 @@
 	{"seh_no32", obj_coff_seh_32, 0}, \
 	{"seh_handler", obj_coff_seh_handler, 0}, \
 	{"seh_code", obj_coff_seh_code, 0}, \
-	{"seh_handlerdata", obj_coff_seh_handlerdata, 0},
+	{"seh_handlerdata", obj_coff_seh_handlerdata, 0}, \
+	{"seh_save_regp", obj_coff_seh_aarch64_save_regp, 0}, \
+	{"seh_save_fregp", obj_coff_seh_aarch64_save_fregp, 0}, \
+	{"seh_save_reg", obj_coff_seh_aarch64_save_reg, 0}, \
+	{"seh_save_freg", obj_coff_seh_aarch64_save_freg, 0}, \
+	{"seh_save_fplr", obj_coff_seh_aarch64_save_fplr, 0}, \
+	{"seh_save_fplr_x", obj_coff_seh_aarch64_save_fplr_x, 0}, \
+	{"seh_save_lrpair", obj_coff_seh_aarch64_save_lrpair, 0}, \
+	{"seh_set_fp", obj_coff_seh_aarch64_set_fp, 0}, \
+	{"seh_add_fp", obj_coff_seh_aarch64_add_fp, 0}, \
+	{"seh_nop", obj_coff_seh_aarch64_nop, 0}, \
+	{"seh_alloc_stack", obj_coff_seh_aarch64_alloc_stack, 0},
 
 /* Type definitions.  */
 
@@ -135,9 +163,44 @@ typedef enum seh_kind {
   seh_kind_unknown = 0,
   seh_kind_mips = 1,  /* Used for MIPS and x86 pdata generation.  */
   seh_kind_arm = 2,   /* Used for ARM, PPC, SH3, and SH4 pdata (PDATA_EH) generation.  */
-  seh_kind_x64 = 3    /* Used for IA64 and x64 pdata/xdata generation.  */
+  seh_kind_x64 = 3,   /* Used for IA64 and x64 pdata/xdata generation.  */
+  seh_kind_aarch64 = 4 /* Used for AArch64 (ARM64) Windows pdata/xdata generation.  */
 } seh_kind;
 
+/* AArch64 unwind opcodes.  */
+#define AARCH64_UOP_ALLOC_SMALL  0x00
+#define AARCH64_UOP_ALLOC_MEDIUM 0xC0
+#define AARCH64_UOP_ALLOC_LARGE  0xE0
+#define AARCH64_UOP_SAVE_R19R20X 0x20
+#define AARCH64_UOP_SAVE_FPLRX   0x80
+#define AARCH64_UOP_SAVE_FPLR    0x40
+#define AARCH64_UOP_SAVE_REG     0xD0
+#define AARCH64_UOP_SAVE_REG_X   0xD4
+#define AARCH64_UOP_SAVE_REG_P   0xC8
+#define AARCH64_UOP_SAVE_REG_PX  0xCC
+#define AARCH64_UOP_SAVE_LRPAIR  0xD6
+#define AARCH64_UOP_SAVE_FREG    0xDC
+#define AARCH64_UOP_SAVE_FREG_X  0xDE
+#define AARCH64_UOP_SAVE_FREG_P  0xD8
+#define AARCH64_UOP_SAVE_FREG_PX 0xDA
+#define AARCH64_UOP_SET_FP       0xE1
+#define AARCH64_UOP_ADD_FP       0xE2
+#define AARCH64_UOP_NOP          0xE3
+#define AARCH64_UOP_END          0xE4
+#define AARCH64_UOP_SAVE_NEXT    0xE6
+#define AARCH64_UOP_TRAP_FRAME   0xE8
+#define AARCH64_UOP_PUSH_MACH    0xE9
+#define AARCH64_UOP_CONTEXT      0xEA
+#define AARCH64_UOP_EC_CONTEXT   0xEB
+#define AARCH64_UOP_CLEAR_UNWOUND_TO_CALL 0xEC
+#define AARCH64_UOP_PAC_SIGN_LR  0xFC
+#define AARCH64_UOP_SAVE_ANY_REG_I   0xE7
+#define AARCH64_UOP_SAVE_ANY_REG_IP  0xE7
+#define AARCH64_UOP_SAVE_ANY_REG_D   0xE7
+#define AARCH64_UOP_SAVE_ANY_REG_DP  0xE7
+#define AARCH64_UOP_SAVE_ANY_REG_Q   0xE7
+#define AARCH64_UOP_SAVE_ANY_REG_QP  0xE7
+
 /* Forward declarations.  */
 static void obj_coff_seh_stackalloc (int);
 static void obj_coff_seh_setframe (int);
@@ -152,6 +215,17 @@ static void obj_coff_seh_proc  (int);
 static void obj_coff_seh_handler (int);
 static void obj_coff_seh_handlerdata (int);
 static void obj_coff_seh_code (int);
+static void obj_coff_seh_aarch64_save_regp (int);
+static void obj_coff_seh_aarch64_save_fregp (int);
+static void obj_coff_seh_aarch64_save_reg (int);
+static void obj_coff_seh_aarch64_save_freg (int);
+static void obj_coff_seh_aarch64_save_fplr (int);
+static void obj_coff_seh_aarch64_save_fplr_x (int);
+static void obj_coff_seh_aarch64_save_lrpair (int);
+static void obj_coff_seh_aarch64_set_fp (int);
+static void obj_coff_seh_aarch64_add_fp (int);
+static void obj_coff_seh_aarch64_nop (int);
+static void obj_coff_seh_aarch64_alloc_stack (int);
 
 #define UNDSEC bfd_und_section_ptr
 
diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index ab122fc388d..7e25f86513d 100644
--- a/libiberty/cp-demangle.c
+++ b/libiberty/cp-demangle.c
@@ -199,6 +199,20 @@ static void d_init_info (const char *, int, size_t, struct d_info *);
 #endif /* defined (__STDC__) */
 #endif /* ! defined (__GNUC__) */
 
+/* Allocate SIZE bytes of memory, aborting on failure, like libiberty's
+   xmalloc.  libiberty's xmalloc cannot be used here because this file is
+   also compiled into libstdc++ (-DIN_GLIBCPP_V3), which does not link
+   against libiberty.  */
+
+static void *
+d_malloc (size_t size)
+{
+  void *p = malloc (size);
+  if (p == NULL)
+    abort ();
+  return p;
+}
+
 /* We avoid pulling in the ctype tables, to prevent pulling in
    additional unresolved symbols when this code is used in a library.
    FIXME: Is this really a valid reason?  This comes from the original
@@ -4687,26 +4701,22 @@ cplus_demangle_print_callback (int options,
 
   d_print_init (&dpi, callback, opaque, dc);
 
-  {
-#ifdef CP_DYNAMIC_ARRAYS
-    /* Avoid zero-length VLAs, which are prohibited by the C99 standard
-       and flagged as errors by Address Sanitizer.  */
-    __extension__ struct d_saved_scope scopes[(dpi.num_saved_scopes > 0)
-                                              ? dpi.num_saved_scopes : 1];
-    __extension__ struct d_print_template temps[(dpi.num_copy_templates > 0)
-                                                ? dpi.num_copy_templates : 1];
-
-    dpi.saved_scopes = scopes;
-    dpi.copy_templates = temps;
-#else
-    dpi.saved_scopes = alloca (dpi.num_saved_scopes
-			       * sizeof (*dpi.saved_scopes));
-    dpi.copy_templates = alloca (dpi.num_copy_templates
-				 * sizeof (*dpi.copy_templates));
-#endif
+  /* Allocate these on the heap rather than with alloca/VLAs: they can be
+     large for deeply-nested templates, and alloca overflows the stack
+     when the process has a small committed stack (e.g. Windows threads).  */
+  if (dpi.num_saved_scopes > 0)
+    dpi.saved_scopes = (struct d_saved_scope *)
+      d_malloc (dpi.num_saved_scopes * sizeof (*dpi.saved_scopes));
+  if (dpi.num_copy_templates > 0)
+    dpi.copy_templates = (struct d_print_template *)
+      d_malloc (dpi.num_copy_templates * sizeof (*dpi.copy_templates));
+
+  d_print_comp (&dpi, options, dc);
 
-    d_print_comp (&dpi, options, dc);
-  }
+  if (dpi.saved_scopes != NULL)
+    free (dpi.saved_scopes);
+  if (dpi.copy_templates != NULL)
+    free (dpi.copy_templates);
 
   d_print_flush (&dpi);
 
@@ -6853,6 +6863,9 @@ d_demangle_callback (const char *mangled, int options,
   type;
   struct d_info di;
   struct demangle_component *dc;
+  struct demangle_component *comps;
+  struct demangle_component **subs;
+  size_t len;
   int status;
 
   if (mangled[0] == '_' && mangled[1] == 'Z')
@@ -6869,82 +6882,74 @@ d_demangle_callback (const char *mangled, int options,
       type = DCT_TYPE;
     }
 
+  len = strlen (mangled);
+
+  /* We cannot need more components than twice the number of chars in
+     the mangled string, nor more substitutions than chars in it.  These
+     arrays used to be stack-allocated with alloca/VLAs, which overflowed
+     the stack on deeply-templated symbols when the process had a small
+     committed stack (e.g. Windows threads).  Allocate them on the heap
+     instead, so demangling does not depend on the stack size.  */
+  comps = (struct demangle_component *)
+    d_malloc (2 * len * sizeof (*comps));
+  subs = (struct demangle_component **)
+    d_malloc (len * sizeof (*subs));
+
   di.unresolved_name_state = 1;
 
  again:
-  cplus_demangle_init_info (mangled, options, strlen (mangled), &di);
-
-  /* PR 87675 - Check for a mangled string that is so long
-     that we do not have enough stack space to demangle it.  */
-  if (((options & DMGL_NO_RECURSE_LIMIT) == 0)
-      /* This check is a bit arbitrary, since what we really want to do is to
-	 compare the sizes of the di.comps and di.subs arrays against the
-	 amount of stack space remaining.  But there is no portable way to do
-	 this, so instead we use the recursion limit as a guide to the maximum
-	 size of the arrays.  */
-      && (unsigned long) di.num_comps > DEMANGLE_RECURSION_LIMIT)
-    {
-      /* FIXME: We need a way to indicate that a stack limit has been reached.  */
-      return 0;
-    }
-
-  {
-#ifdef CP_DYNAMIC_ARRAYS
-    __extension__ struct demangle_component comps[di.num_comps];
-    __extension__ struct demangle_component *subs[di.num_subs];
+  cplus_demangle_init_info (mangled, options, len, &di);
 
-    di.comps = comps;
-    di.subs = subs;
-#else
-    di.comps = alloca (di.num_comps * sizeof (*di.comps));
-    di.subs = alloca (di.num_subs * sizeof (*di.subs));
-#endif
+  di.comps = comps;
+  di.subs = subs;
 
-    switch (type)
-      {
-      case DCT_TYPE:
-	dc = cplus_demangle_type (&di);
-	break;
-      case DCT_MANGLED:
-	dc = cplus_demangle_mangled_name (&di, 1);
-	break;
-      case DCT_GLOBAL_CTORS:
-      case DCT_GLOBAL_DTORS:
-	d_advance (&di, 11);
-	dc = d_make_comp (&di,
-			  (type == DCT_GLOBAL_CTORS
-			   ? DEMANGLE_COMPONENT_GLOBAL_CONSTRUCTORS
-			   : DEMANGLE_COMPONENT_GLOBAL_DESTRUCTORS),
-			  d_make_demangle_mangled_name (&di, d_str (&di)),
-			  NULL);
-	d_advance (&di, strlen (d_str (&di)));
-	break;
-      default:
-	abort (); /* We have listed all the cases.  */
-      }
+  switch (type)
+    {
+    case DCT_TYPE:
+      dc = cplus_demangle_type (&di);
+      break;
+    case DCT_MANGLED:
+      dc = cplus_demangle_mangled_name (&di, 1);
+      break;
+    case DCT_GLOBAL_CTORS:
+    case DCT_GLOBAL_DTORS:
+      d_advance (&di, 11);
+      dc = d_make_comp (&di,
+			(type == DCT_GLOBAL_CTORS
+			 ? DEMANGLE_COMPONENT_GLOBAL_CONSTRUCTORS
+			 : DEMANGLE_COMPONENT_GLOBAL_DESTRUCTORS),
+			d_make_demangle_mangled_name (&di, d_str (&di)),
+			NULL);
+      d_advance (&di, strlen (d_str (&di)));
+      break;
+    default:
+      abort (); /* We have listed all the cases.  */
+    }
 
-    /* If DMGL_PARAMS is set, then if we didn't consume the entire
-       mangled string, then we didn't successfully demangle it.  If
-       DMGL_PARAMS is not set, we didn't look at the trailing
-       parameters.  */
-    if (((options & DMGL_PARAMS) != 0) && d_peek_char (&di) != '\0')
-      dc = NULL;
+  /* If DMGL_PARAMS is set, then if we didn't consume the entire
+     mangled string, then we didn't successfully demangle it.  If
+     DMGL_PARAMS is not set, we didn't look at the trailing
+     parameters.  */
+  if (((options & DMGL_PARAMS) != 0) && d_peek_char (&di) != '\0')
+    dc = NULL;
 
-    /* See discussion in d_unresolved_name.  */
-    if (dc == NULL && di.unresolved_name_state == -1)
-      {
-	di.unresolved_name_state = 0;
-	goto again;
-      }
+  /* See discussion in d_unresolved_name.  */
+  if (dc == NULL && di.unresolved_name_state == -1)
+    {
+      di.unresolved_name_state = 0;
+      goto again;
+    }
 
 #ifdef CP_DEMANGLE_DEBUG
-    d_dump (dc, 0);
+  d_dump (dc, 0);
 #endif
 
-    status = (dc != NULL)
-             ? cplus_demangle_print_callback (options, dc, callback, opaque)
-             : 0;
-  }
+  status = (dc != NULL)
+           ? cplus_demangle_print_callback (options, dc, callback, opaque)
+           : 0;
+
+  free (comps);
+  free (subs);
 
   return status;
 }
@@ -7169,65 +7174,70 @@ is_ctor_or_dtor (const char *mangled,
 {
   struct d_info di;
   struct demangle_component *dc;
+  struct demangle_component *comps;
+  struct demangle_component **subs;
+  size_t len;
   int ret;
 
   *ctor_kind = (enum gnu_v3_ctor_kinds) 0;
   *dtor_kind = (enum gnu_v3_dtor_kinds) 0;
 
-  cplus_demangle_init_info (mangled, DMGL_GNU_V3, strlen (mangled), &di);
+  len = strlen (mangled);
 
-  {
-#ifdef CP_DYNAMIC_ARRAYS
-    __extension__ struct demangle_component comps[di.num_comps];
-    __extension__ struct demangle_component *subs[di.num_subs];
+  /* Allocate on the heap rather than with alloca/VLAs (see comment in
+     d_demangle_callback).  */
+  comps = (struct demangle_component *)
+    d_malloc (2 * len * sizeof (*comps));
+  subs = (struct demangle_component **)
+    d_malloc (len * sizeof (*subs));
 
-    di.comps = comps;
-    di.subs = subs;
-#else
-    di.comps = alloca (di.num_comps * sizeof (*di.comps));
-    di.subs = alloca (di.num_subs * sizeof (*di.subs));
-#endif
+  cplus_demangle_init_info (mangled, DMGL_GNU_V3, len, &di);
 
-    dc = cplus_demangle_mangled_name (&di, 1);
+  di.comps = comps;
+  di.subs = subs;
 
-    /* Note that because we did not pass DMGL_PARAMS, we don't expect
-       to demangle the entire string.  */
+  dc = cplus_demangle_mangled_name (&di, 1);
 
-    ret = 0;
-    while (dc != NULL)
-      {
-	switch (dc->type)
-	  {
-	    /* These cannot appear on a constructor or destructor.  */
-	  case DEMANGLE_COMPONENT_RESTRICT_THIS:
-	  case DEMANGLE_COMPONENT_VOLATILE_THIS:
-	  case DEMANGLE_COMPONENT_CONST_THIS:
-	  case DEMANGLE_COMPONENT_REFERENCE_THIS:
-	  case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
-	  default:
-	    dc = NULL;
-	    break;
-	  case DEMANGLE_COMPONENT_TYPED_NAME:
-	  case DEMANGLE_COMPONENT_TEMPLATE:
-	    dc = d_left (dc);
-	    break;
-	  case DEMANGLE_COMPONENT_QUAL_NAME:
-	  case DEMANGLE_COMPONENT_LOCAL_NAME:
-	    dc = d_right (dc);
-	    break;
-	  case DEMANGLE_COMPONENT_CTOR:
-	    *ctor_kind = dc->u.s_ctor.kind;
-	    ret = 1;
-	    dc = NULL;
-	    break;
-	  case DEMANGLE_COMPONENT_DTOR:
-	    *dtor_kind = dc->u.s_dtor.kind;
-	    ret = 1;
-	    dc = NULL;
-	    break;
-	  }
-      }
-  }
+  /* Note that because we did not pass DMGL_PARAMS, we don't expect
+     to demangle the entire string.  */
+
+  ret = 0;
+  while (dc != NULL)
+    {
+      switch (dc->type)
+	{
+	  /* These cannot appear on a constructor or destructor.  */
+	case DEMANGLE_COMPONENT_RESTRICT_THIS:
+	case DEMANGLE_COMPONENT_VOLATILE_THIS:
+	case DEMANGLE_COMPONENT_CONST_THIS:
+	case DEMANGLE_COMPONENT_REFERENCE_THIS:
+	case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
+	default:
+	  dc = NULL;
+	  break;
+	case DEMANGLE_COMPONENT_TYPED_NAME:
+	case DEMANGLE_COMPONENT_TEMPLATE:
+	  dc = d_left (dc);
+	  break;
+	case DEMANGLE_COMPONENT_QUAL_NAME:
+	case DEMANGLE_COMPONENT_LOCAL_NAME:
+	  dc = d_right (dc);
+	  break;
+	case DEMANGLE_COMPONENT_CTOR:
+	  *ctor_kind = dc->u.s_ctor.kind;
+	  ret = 1;
+	  dc = NULL;
+	  break;
+	case DEMANGLE_COMPONENT_DTOR:
+	  *dtor_kind = dc->u.s_dtor.kind;
+	  ret = 1;
+	  dc = NULL;
+	  break;
+	}
+    }
+
+  free (comps);
+  free (subs);
 
   return ret;
 }
-- 
2.55.0