[PATCH] aarch64-w64-mingw32: Complete Windows on ARM support

trcrsired <[email protected]> Wed, 5 Aug 2026 02:03:48 +0800
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
Implements the remaining pieces to make GCC usable on Windows on ARM
(aarch64-w64-mingw32):

* C++ SEH exception handling: new aarch64-abi-ms.cc SEH unwind emitter,
  enable TARGET_SEH for the mingw target, fix libgcc unwind-seh.c for
  aarch64, and build libgcc_s_seh-1.dll.
* Stack probing: probe in 4KB chunks for large frames in
  aarch64_add_offset.
* ASLR: always enable ASLR for the toolchain binaries (required on WOA).
* Disable the lp64 multilib for Windows targets (LLP64 only).
* mingw: fix RTL sharing of dllimport symbols.
---
 fixincludes/configure                       |   3 +-
 fixincludes/configure.ac                    |   3 +-
 gcc/common/config/aarch64/aarch64-common.cc |  23 ++
 gcc/config.gcc                              |  14 +-
 gcc/config.host                             |   6 +-
 gcc/config/aarch64/aarch64-abi-ms-protos.h  |   4 +
 gcc/config/aarch64/aarch64-abi-ms.cc        | 348 ++++++++++++++++++++
 gcc/config/aarch64/aarch64-abi-ms.h         |   9 +-
 gcc/config/aarch64/aarch64-mingw32.h        |  31 ++
 gcc/config/aarch64/aarch64-protos.h         |   4 +
 gcc/config/aarch64/aarch64.cc               |  64 +++-
 gcc/config/aarch64/cygming.h                |  69 +++-
 gcc/config/aarch64/t-aarch64-mingw          |   8 +-
 gcc/config/mingw/mingw32.h                  |  30 +-
 gcc/config/mingw/winnt-dll.cc               |  10 +-
 gcc/config/mingw/winnt.cc                   | 130 ++++----
 gcc/configure                               |  41 ++-
 gcc/configure.ac                            |  41 ++-
 libgcc/config.host                          |   7 +
 libgcc/config/aarch64/t-mingw               |   6 +
 libgcc/unwind-seh.c                         | 203 ++++++++----
 libiberty/cp-demangle.c                     | 276 ++++++++--------
 22 files changed, 999 insertions(+), 331 deletions(-)
 create mode 100644 gcc/config/aarch64/aarch64-mingw32.h

diff --git a/fixincludes/configure b/fixincludes/configure
index 3e572537c3d..e14512da312 100755
--- a/fixincludes/configure
+++ b/fixincludes/configure
@@ -4817,8 +4817,7 @@ fi
 else
   case $host in
 	i?86-*-msdosdjgpp* | \
-	i?86-*-mingw32* | \
-	x86_64-*-mingw32* | \
+	*-*-mingw32* | \
 	*-*-beos* | \
         *-*-*vms*)
 		TARGET=twoprocess
diff --git a/fixincludes/configure.ac b/fixincludes/configure.ac
index 30bef64b5ec..88bcae6b710 100644
--- a/fixincludes/configure.ac
+++ b/fixincludes/configure.ac
@@ -50,8 +50,7 @@ else
 fi],
 [case $host in
 	i?86-*-msdosdjgpp* | \
-	i?86-*-mingw32* | \
-	x86_64-*-mingw32* | \
+	*-*-mingw32* | \
 	*-*-beos* | \
         *-*-*vms*)
 		TARGET=twoprocess
diff --git a/gcc/common/config/aarch64/aarch64-common.cc b/gcc/common/config/aarch64/aarch64-common.cc
index fc7e84ae966..ce0eec85328 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 "";
 }
 
+/* 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
+
+  /* Use SEH for aarch64-w64-mingw32 when SEH tables are enabled.  */
+  if (opts->x_flag_unwind_tables)
+    return UI_SEH;
+
+  if (DWARF2_UNWIND_INFO)
+    return UI_DWARF2;
+
+  return UI_SJLJ;
+}
+
+#undef TARGET_EXCEPT_UNWIND_INFO
+#define TARGET_EXCEPT_UNWIND_INFO aarch64_except_unwind_info
+
 struct gcc_targetm_common targetm_common = TARGETM_COMMON_INITIALIZER;
 
 #undef AARCH64_CPU_NAME_LENGTH
diff --git a/gcc/config.gcc b/gcc/config.gcc
index 0477bf22c8a..e80e212e453 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -1325,12 +1325,13 @@ aarch64-*-mingw*)
 	    tm_file="${tm_file} mingw/mingw-pthread.h"
 	    ;;
 	esac
-	tm_file="${tm_file} aarch64/cygming.h"
-	tm_file="${tm_file} mingw/mingw32.h"
-	tm_file="${tm_file} mingw/mingw-stdint.h"
-	tm_file="${tm_file} mingw/winnt.h"
-	tm_file="${tm_file} mingw/winnt-dll.h"
-	tmake_file="${tmake_file} aarch64/t-aarch64"
+ 	tm_file="${tm_file} aarch64/cygming.h"
+ 	tm_file="${tm_file} mingw/mingw32.h"
+ 	tm_file="${tm_file} mingw/mingw-stdint.h"
+ 	tm_file="${tm_file} mingw/winnt.h"
+ 	tm_file="${tm_file} mingw/winnt-dll.h"
+ 	tm_file="${tm_file} aarch64/aarch64-mingw32.h"
+ 	tmake_file="${tmake_file} aarch64/t-aarch64"
 	tmake_file="${tmake_file} aarch64/t-aarch64-mingw"
         native_system_header_dir=/mingw/include
 	target_gtfiles="$target_gtfiles \$(srcdir)/config/mingw/winnt.cc"
@@ -1989,6 +1990,7 @@ i[34567]86-*-elf*)
 	tm_file="${tm_file} i386/unix.h i386/att.h elfos.h newlib-stdint.h i386/i386elf.h"
 	;;
 x86_64-*-elf*)
+    tmake_file="${tmake_file} i386/t-x86_64-elf"
 	tm_file="${tm_file} i386/unix.h i386/att.h elfos.h newlib-stdint.h i386/i386elf.h i386/x86-64.h"
 	;;
 x86_64-*-rtems*)
diff --git a/gcc/config.host b/gcc/config.host
index c9be68acb82..8eb3c4d4879 100644
--- a/gcc/config.host
+++ b/gcc/config.host
@@ -100,7 +100,7 @@ esac
 
 case ${host} in
   aarch64*-*-freebsd* | aarch64*-*-linux* | aarch64*-*-fuchsia* |\
-  aarch64*-*-darwin*)
+  aarch64*-*-darwin* | aarch64*-*-mingw* | aarch64*-*-cygwin* )
     case ${target} in
       aarch64*-*-*)
 	host_extra_gcc_objs="driver-aarch64.o"
@@ -223,14 +223,14 @@ case ${host} in
         ;;
     esac
     ;;
-  i[34567]86-*-cygwin* | x86_64-*-cygwin*)
+  *-*-cygwin*)
     host_xm_file=i386/xm-cygwin.h
     out_host_hook_obj=host-cygwin.o
     host_xmake_file="${host_xmake_file} i386/x-cygwin"
     host_exeext=.exe
     host_lto_plugin_soname=cyglto_plugin.dll
     ;;
-  i[34567]86-*-mingw32* | x86_64-*-mingw*)
+  *-*-mingw*)
     host_xm_file=i386/xm-mingw32.h
     host_xmake_file="${host_xmake_file} ${host_xmake_mingw} i386/x-mingw32"
     host_extra_gcc_objs="${host_extra_gcc_objs} ${host_extra_gcc_objs_mingw} driver-mingw32.o"
diff --git a/gcc/config/aarch64/aarch64-abi-ms-protos.h b/gcc/config/aarch64/aarch64-abi-ms-protos.h
index f08ea29d0c2..1adcf880e16 100644
--- a/gcc/config/aarch64/aarch64-abi-ms-protos.h
+++ b/gcc/config/aarch64/aarch64-abi-ms-protos.h
@@ -31,4 +31,8 @@ extern tree aarch64_ms_variadic_abi_canonical_va_list_type (tree type);
 extern int aarch64_arg_partial_bytes (cumulative_args_t,
 				      const function_arg_info &);
 
+#ifdef TARGET_SEH
+extern void aarch64_pe_seh_unwind_emit (FILE *, rtx_insn *);
+#endif
+
 #endif
diff --git a/gcc/config/aarch64/aarch64-abi-ms.cc b/gcc/config/aarch64/aarch64-abi-ms.cc
index 1eaab4246d3..0b51f4dc723 100644
--- a/gcc/config/aarch64/aarch64-abi-ms.cc
+++ b/gcc/config/aarch64/aarch64-abi-ms.cc
@@ -32,7 +32,12 @@
 #include "regs.h"
 #include "function-abi.h"
 #include "builtins.h"
+#include "memmodel.h"
+#include "output.h"
+#include "emit-rtl.h"
+#include "rtl-iter.h"
 #include "aarch64-abi-ms-protos.h"
+#include "config/mingw/winnt.h"
 
 /* Iterate through the target-specific builtin types for va_list.
    IDX denotes the iterator, *PTREE is set to the result type of
@@ -104,3 +109,346 @@ aarch64_arg_partial_bytes (cumulative_args_t pcum_v,
 
   return 0;
 }
+
+
+/* AArch64 SEH unwind emission.
+
+   This function is called for each frame-related insn and emits the
+   appropriate ARM64 SEH assembly directives based on REG_CFA_* notes
+   and the insn pattern.
+
+   ARM64 SEH directives differ from x64:
+     .seh_save_fplr   <offset>   - stp x29, x30, [sp, #offset]
+     .seh_save_fplr_x <offset>   - stp x29, x30, [sp, #-offset]!
+     .seh_save_regp   <r1>,<r2>,<offset> - stp pair at SP+offset
+     .seh_save_reg    <r>,<offset>        - str reg at SP+offset
+     .seh_save_fregp  <d1>,<d2>,<offset>  - stp d pair at SP+offset
+     .seh_save_freg   <d>,<offset>        - str d at SP+offset
+     .seh_alloc_stack <size>   - sub sp, sp, #size
+     .seh_set_fp               - mov x29, sp
+     .seh_add_fp      <offset> - add x29, sp, #offset
+     .seh_save_lrpair <r>,<offset> - stp x30, <r>, [sp, #offset]
+     .seh_nop                  - no-op padding
+*/
+
+/* Emit aarch64 SEH directives for one frame-related expression PAT.
+   Handles PARALLELs of SETs (including the storewb_pre_pair_8 combined
+   alloc+save), plain alloc_stack, set_fp/add_fp, register saves, and the
+   store_pair_8 pattern expressed as (set (mem:V2x8QI ...) (unspec [...])).
+   All offsets are relative to the current SP.  */
+
+static void
+seh_aarch64_emit_expr (FILE *out_file, struct seh_frame_state *seh, rtx pat)
+{
+  rtx dest, src;
+
+  if (pat == NULL_RTX)
+    return;
+
+  if (GET_CODE (pat) == PARALLEL || GET_CODE (pat) == SEQUENCE)
+    {
+      int i, n = XVECLEN (pat, 0);
+
+      /* Combined alloc_stack + save_fplr:
+	 (parallel [(set sp sp-N) (set mem[sp-N] x29) (set mem[sp-N+8] x30)])
+	 This is the pre-indexed stp x29, x30, [sp, #-N]! which performs both
+	 the stack allocation AND the register save.  Emit a single
+	 .seh_save_fplr_x, not a separate .seh_alloc_stack.  */
+      if (n == 3
+	  && GET_CODE (XVECEXP (pat, 0, 0)) == SET
+	  && GET_CODE (XVECEXP (pat, 0, 1)) == SET
+	  && GET_CODE (XVECEXP (pat, 0, 2)) == SET
+	  && SET_DEST (XVECEXP (pat, 0, 0)) == stack_pointer_rtx
+	  && GET_CODE (SET_SRC (XVECEXP (pat, 0, 0))) == PLUS
+	  && XEXP (SET_SRC (XVECEXP (pat, 0, 0)), 0) == stack_pointer_rtx
+	  && CONST_INT_P (XEXP (SET_SRC (XVECEXP (pat, 0, 0)), 1))
+	  && INTVAL (XEXP (SET_SRC (XVECEXP (pat, 0, 0)), 1)) < 0)
+	{
+	  rtx set1 = XVECEXP (pat, 0, 1);
+	  rtx set2 = XVECEXP (pat, 0, 2);
+	  rtx reg1 = MEM_P (SET_DEST (set1)) ? SET_SRC (set1)
+	    : (REG_P (SET_DEST (set1)) ? SET_DEST (set1) : NULL_RTX);
+	  rtx reg2 = MEM_P (SET_DEST (set2)) ? SET_SRC (set2)
+	    : (REG_P (SET_DEST (set2)) ? SET_DEST (set2) : NULL_RTX);
+	  if (reg1 && reg2)
+	    {
+	      unsigned int r1 = REGNO (reg1), r2 = REGNO (reg2);
+	      HOST_WIDE_INT size
+		= -INTVAL (XEXP (SET_SRC (XVECEXP (pat, 0, 0)), 1));
+	      if (seh->cfa_reg == stack_pointer_rtx)
+		seh->cfa_offset += size;
+	      seh->sp_offset += size;
+
+	      if ((r1 == 29 && r2 == 30) || (r1 == 30 && r2 == 29))
+		{
+		  fprintf (out_file, "\t.seh_save_fplr_x\t"
+			   HOST_WIDE_INT_PRINT_DEC "\n", size);
+		  return;
+		}
+
+	      /* Pre-indexed stp of a non-FPLR pair, e.g.
+		 "stp d8, d9, [sp, #-64]!" or "stp x19, x20, [sp, #-64]!".
+		 GAS has no save_regp_x/save_fregp_x combined directive, so
+		 split into an allocation followed by the register-pair save
+		 at offset 0 relative to the (new) SP.  */
+	      if ((FP_REGNUM_P (r1) && FP_REGNUM_P (r2))
+		  || (!FP_REGNUM_P (r1) && !FP_REGNUM_P (r2)))
+		{
+		  fprintf (out_file, "\t.seh_alloc_stack\t"
+			   HOST_WIDE_INT_PRINT_DEC "\n", size);
+		  if (FP_REGNUM_P (r1))
+		    fprintf (out_file, "\t.seh_save_fregp\td%d, d%d, 0\n",
+			     r1 - V0_REGNUM, r2 - V0_REGNUM);
+		  else
+		    fprintf (out_file, "\t.seh_save_regp\tx%d, x%d, 0\n",
+			     r1, r2);
+		  return;
+		}
+	    }
+	}
+
+      /* stp pair as a PARALLEL of two SETs:
+	 (parallel [(set mem[sp+off] reg1) (set mem[sp+off+8] reg2)])
+	 Emit .seh_save_regp / .seh_save_fplr.  */
+      if (n == 2
+	  && GET_CODE (XVECEXP (pat, 0, 0)) == SET
+	  && GET_CODE (XVECEXP (pat, 0, 1)) == SET)
+	{
+	  rtx set0 = XVECEXP (pat, 0, 0);
+	  rtx set1 = XVECEXP (pat, 0, 1);
+	  rtx mem0 = MEM_P (SET_DEST (set0)) ? SET_DEST (set0)
+	    : (MEM_P (SET_SRC (set0)) ? SET_SRC (set0) : NULL_RTX);
+	  rtx mem1 = MEM_P (SET_DEST (set1)) ? SET_DEST (set1)
+	    : (MEM_P (SET_SRC (set1)) ? SET_SRC (set1) : NULL_RTX);
+	  rtx reg0 = REG_P (SET_SRC (set0)) ? SET_SRC (set0)
+	    : (REG_P (SET_DEST (set0)) ? SET_DEST (set0) : NULL_RTX);
+	  rtx reg1 = REG_P (SET_SRC (set1)) ? SET_SRC (set1)
+	    : (REG_P (SET_DEST (set1)) ? SET_DEST (set1) : NULL_RTX);
+	  if (mem0 && mem1 && reg0 && reg1)
+	    {
+	      /* Both mems should be (plus sp N) or (sp) with an 8-byte
+		 stride.  */
+	      rtx a0 = XEXP (mem0, 0), a1 = XEXP (mem1, 0);
+	      HOST_WIDE_INT off0 = 0, off1 = 0;
+	      bool same_base = false;
+
+	      /* Both mems should be sp-based, e.g. (reg sp) and (plus sp 8).  */
+	      rtx base0 = (GET_CODE (a0) == PLUS) ? XEXP (a0, 0) : a0;
+	      rtx base1 = (GET_CODE (a1) == PLUS) ? XEXP (a1, 0) : a1;
+	      if (base0 == base1 && base0 == stack_pointer_rtx)
+		{
+		  off0 = (GET_CODE (a0) == PLUS && CONST_INT_P (XEXP (a0, 1)))
+		    ? INTVAL (XEXP (a0, 1)) : 0;
+		  off1 = (GET_CODE (a1) == PLUS && CONST_INT_P (XEXP (a1, 1)))
+		    ? INTVAL (XEXP (a1, 1)) : 0;
+
+		  /* SEH offset is relative to the current SP.  */
+		  unsigned int r0 = REGNO (reg0), r1 = REGNO (reg1);
+		  if ((r0 == 29 && r1 == 30) || (r0 == 30 && r1 == 29))
+		    fprintf (out_file, "\t.seh_save_fplr\t"
+			     HOST_WIDE_INT_PRINT_DEC "\n", off0);
+		  else if (FP_REGNUM_P (r0) && FP_REGNUM_P (r1))
+		    fprintf (out_file, "\t.seh_save_fregp\td%d, d%d, "
+			     HOST_WIDE_INT_PRINT_DEC "\n",
+			     r0 - V0_REGNUM, r1 - V0_REGNUM, off0);
+		  else if (!FP_REGNUM_P (r0) && !FP_REGNUM_P (r1))
+		    fprintf (out_file, "\t.seh_save_regp\tx%d, x%d, "
+			     HOST_WIDE_INT_PRINT_DEC "\n", r0, r1, off0);
+		  return;
+		}
+	    }
+	}
+
+      for (i = 0; i < n; ++i)
+	{
+	  rtx ele = XVECEXP (pat, 0, i);
+	  if (GET_CODE (ele) == SET || GET_CODE (ele) == PARALLEL
+	      || GET_CODE (ele) == SEQUENCE)
+	    seh_aarch64_emit_expr (out_file, seh, ele);
+	}
+      return;
+    }
+
+  if (GET_CODE (pat) != SET)
+    return;
+
+  dest = SET_DEST (pat);
+  src = SET_SRC (pat);
+
+  /* sp = sp + N (alloc_stack for N < 0).  */
+  if (dest == stack_pointer_rtx
+      && GET_CODE (src) == PLUS
+      && XEXP (src, 0) == stack_pointer_rtx
+      && CONST_INT_P (XEXP (src, 1)))
+    {
+      HOST_WIDE_INT delta = INTVAL (XEXP (src, 1));
+      if (delta < 0)
+	{
+	  delta = -delta;
+	  if (seh->cfa_reg == stack_pointer_rtx)
+	    seh->cfa_offset += delta;
+	  seh->sp_offset += delta;
+	  fprintf (out_file, "\t.seh_alloc_stack\t"
+		   HOST_WIDE_INT_PRINT_DEC "\n", delta);
+	}
+      return;
+    }
+
+  /* Frame pointer setup: x29 = sp or x29 = sp + N.  */
+  if (dest == hard_frame_pointer_rtx)
+    {
+      if (src == stack_pointer_rtx)
+	{
+	  fprintf (out_file, "\t.seh_set_fp\n");
+	  seh->cfa_reg = hard_frame_pointer_rtx;
+	  seh->cfa_offset = 0;
+	  return;
+	}
+      if (GET_CODE (src) == PLUS && XEXP (src, 0) == stack_pointer_rtx
+	  && CONST_INT_P (XEXP (src, 1)))
+	{
+	  fprintf (out_file, "\t.seh_add_fp\t"
+		   HOST_WIDE_INT_PRINT_DEC "\n", INTVAL (XEXP (src, 1)));
+	  seh->cfa_reg = hard_frame_pointer_rtx;
+	  seh->cfa_offset = 0;
+	  return;
+	}
+      return;
+    }
+
+  /* Register save to memory: mem = reg.  The SEH offset is relative to
+     the current SP (after preceding alloc_stack), so emit the raw memory
+     offset directly.  */
+  if (MEM_P (dest) && REG_P (src))
+    {
+      unsigned int regno = REGNO (src);
+      HOST_WIDE_INT offset = 0;
+      rtx addr = XEXP (dest, 0);
+
+      if (GET_CODE (addr) == PLUS && CONST_INT_P (XEXP (addr, 1)))
+	offset = INTVAL (XEXP (addr, 1));
+      else if (GET_CODE (addr) == PRE_DEC && XEXP (addr, 0) == stack_pointer_rtx)
+	{
+	  /* str reg, [sp, #-N]! : both allocates and stores.  */
+	  HOST_WIDE_INT step = GET_MODE_SIZE (GET_MODE (dest)).to_constant ();
+	  offset = -step;
+	}
+      else if (GET_CODE (addr) == REG)
+	offset = 0;
+
+      seh->reg_offset[regno] = offset;
+
+      if (FP_REGNUM_P (regno))
+	fprintf (out_file, "\t.seh_save_freg\td%d, "
+		 HOST_WIDE_INT_PRINT_DEC "\n", regno - V0_REGNUM, offset);
+      else if (regno >= 0 && regno <= 30)
+	fprintf (out_file, "\t.seh_save_reg\tx%d, "
+		 HOST_WIDE_INT_PRINT_DEC "\n", regno, offset);
+      return;
+    }
+
+  /* Store pair expressed as (set (mem:V2x8QI addr) (unspec [(reg) (reg)] UNSPEC_STP)).  */
+  if (MEM_P (dest) && GET_CODE (src) == UNSPEC
+      && XVECLEN (src, 0) == 2
+      && REG_P (XVECEXP (src, 0, 0)) && REG_P (XVECEXP (src, 0, 1)))
+    {
+      unsigned int r1 = REGNO (XVECEXP (src, 0, 0));
+      unsigned int r2 = REGNO (XVECEXP (src, 0, 1));
+      HOST_WIDE_INT offset = 0;
+      rtx addr = XEXP (dest, 0);
+
+      if (GET_CODE (addr) == PLUS && CONST_INT_P (XEXP (addr, 1)))
+	offset = INTVAL (XEXP (addr, 1));
+      else if (GET_CODE (addr) == PRE_DEC && XEXP (addr, 0) == stack_pointer_rtx)
+	offset = -16;
+
+      if ((r1 == 29 && r2 == 30) || (r1 == 30 && r2 == 29))
+	fprintf (out_file, "\t.seh_save_fplr\t"
+		 HOST_WIDE_INT_PRINT_DEC "\n", offset);
+      else if (FP_REGNUM_P (r1) && FP_REGNUM_P (r2))
+	fprintf (out_file, "\t.seh_save_fregp\td%d, d%d, "
+		 HOST_WIDE_INT_PRINT_DEC "\n", r1 - V0_REGNUM, r2 - V0_REGNUM, offset);
+      else if (!FP_REGNUM_P (r1) && !FP_REGNUM_P (r2))
+	fprintf (out_file, "\t.seh_save_regp\tx%d, x%d, "
+		 HOST_WIDE_INT_PRINT_DEC "\n", r1, r2, offset);
+      return;
+    }
+}
+
+void
+aarch64_pe_seh_unwind_emit (FILE *out_file, rtx_insn *insn)
+{
+  rtx note;
+  struct seh_frame_state *seh;
+  bool handled_one = false;
+
+  if (!TARGET_SEH)
+    return;
+
+  seh = cfun->machine->seh;
+
+  if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS)
+    {
+      fputs ("\t.seh_endproc\n", out_file);
+      seh->in_cold_section = true;
+      return;
+    }
+
+  if (NOTE_P (insn) || !RTX_FRAME_RELATED_P (insn))
+    return;
+
+  if (seh->after_prologue)
+    return;
+
+  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+    {
+      rtx pat;
+
+      switch (REG_NOTE_KIND (note))
+	{
+	case REG_FRAME_RELATED_EXPR:
+	  /* The insn pattern is too complex (e.g. store_pair_8 UNSPEC);
+	     the note carries the canonical PARALLEL of SETs.  */
+	  seh_aarch64_emit_expr (out_file, seh, XEXP (note, 0));
+	  handled_one = true;
+	  break;
+
+	case REG_CFA_ADJUST_CFA:
+	  pat = XEXP (note, 0);
+	  if (pat == NULL_RTX)
+	    pat = PATTERN (insn);
+	  if (GET_CODE (pat) == PARALLEL)
+	    pat = XVECEXP (pat, 0, 0);
+	  seh_aarch64_emit_expr (out_file, seh, pat);
+	  handled_one = true;
+	  break;
+
+	case REG_CFA_OFFSET:
+	  pat = XEXP (note, 0);
+	  if (pat == NULL_RTX)
+	    pat = single_set (insn);
+	  seh_aarch64_emit_expr (out_file, seh, pat);
+	  handled_one = true;
+	  break;
+
+	case REG_CFA_REGISTER:
+	case REG_CFA_DEF_CFA:
+	case REG_CFA_EXPRESSION:
+	  /* Frame pointer setup and other complex cases are handled
+	     from the instruction pattern directly.  */
+	  break;
+
+	default:
+	  break;
+	}
+    }
+
+  /* If no REG_CFA note described the insn, examine the instruction
+     pattern directly.  The aarch64 prologue emits frame-related insns
+     (such as the plain "sub sp, sp, N" and "mov x29, sp") without
+     REG_CFA notes.  */
+  if (!handled_one)
+    seh_aarch64_emit_expr (out_file, seh, PATTERN (insn));
+}
+
+
diff --git a/gcc/config/aarch64/aarch64-abi-ms.h b/gcc/config/aarch64/aarch64-abi-ms.h
index 1e1fb3fa400..da539c50ad6 100644
--- a/gcc/config/aarch64/aarch64-abi-ms.h
+++ b/gcc/config/aarch64/aarch64-abi-ms.h
@@ -37,11 +37,10 @@ along with GCC; see the file COPYING3.  If not see
    aarch64-w64-mingw32 target.  */
 #define ASM_OUTPUT_TYPE_DIRECTIVE(STREAM, NAME, TYPE)
 
-/* Structured Exception Handling (SEH) is not yet supported by binutils
-   so adding seh_endproc as an assembly comment to mark the end of a
-   function.  */
-#define ASM_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL) \
-  fprintf (FILE, "\t" ASM_COMMENT_START "  seh_endproc\n")
+/* Use the shared mingw SEH end function which emits .seh_endproc
+   for SEH-enabled functions.  */
+#undef ASM_DECLARE_FUNCTION_SIZE
+#define ASM_DECLARE_FUNCTION_SIZE  mingw_pe_end_function
 
 /* Long double is 64 bit for Coff targets.
    Reference:
diff --git a/gcc/config/aarch64/aarch64-mingw32.h b/gcc/config/aarch64/aarch64-mingw32.h
new file mode 100644
index 00000000000..af3890b4afc
--- /dev/null
+++ b/gcc/config/aarch64/aarch64-mingw32.h
@@ -0,0 +1,31 @@
+/* Operating system specific defines for AArch64 Windows-on-ARM targets.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_AARCH64_MINGW32_H
+#define GCC_AARCH64_MINGW32_H
+
+/* Windows on ARM64 requires the DYNAMIC_BASE (ASLR) characteristic on
+   every PE image; the loader rejects images without it.  The generic
+   mingw LINK_SPEC maps -no-pie to --disable-dynamicbase, which would
+   produce such images.  Override that here so ASLR is always kept for
+   aarch64-w64-mingw32 regardless of the -no-pie/-pie setting.  */
+#undef LINK_SPEC_DISABLE_DYNAMICBASE
+#define LINK_SPEC_DISABLE_DYNAMICBASE ""
+
+#endif /* GCC_AARCH64_MINGW32_H */
diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h
index bcc833cfaa1..727ed315b12 100644
--- a/gcc/config/aarch64/aarch64-protos.h
+++ b/gcc/config/aarch64/aarch64-protos.h
@@ -1315,4 +1315,8 @@ extern unsigned aarch64_stack_alignment (const_tree exp, unsigned align);
 extern rtx aarch64_gen_compare_zero_and_branch (rtx_code code, rtx x,
 						rtx_code_label *label);
 
+#ifdef TARGET_SEH
+extern void aarch64_pe_seh_unwind_emit (FILE *, rtx_insn *);
+#endif
+
 #endif /* GCC_AARCH64_PROTOS_H */
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 3041a6ee62a..9eafa31c236 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -5616,6 +5616,40 @@ aarch64_add_offset (scalar_int_mode mode, rtx dest, rtx src,
 	      || !reg_overlap_mentioned_p (temp1, dest));
   gcc_assert (temp2 == NULL_RTX || !reg_overlap_mentioned_p (dest, temp2));
 
+  /* On Windows (PE/COFF) the OS grows the stack via guard pages, so a
+     stack allocation that moves SP by more than one page must touch each
+     intervening page to hit the guard page; otherwise a large frame that
+     jumps over it faults with an access violation.  This is required
+     regardless of -fstack-clash-protection, matching the behaviour of
+     the LLVM backend for Windows on AArch64.  Probe in 4096-byte (one
+     page) chunks.  */
+  if (TARGET_PECOFF
+      && dest == stack_pointer_rtx
+      && src == stack_pointer_rtx
+      && offset.is_constant ()
+      && offset.to_constant () < 0
+      && -offset.to_constant () >= 4096)
+    {
+      HOST_WIDE_INT size = -offset.to_constant ();
+      const HOST_WIDE_INT probe_interval = 4096;
+      /* Emit each probe chunk directly (do not recurse back into the
+	 probing logic).  Pass TEMP1 so the single-chunk adjustments can
+	 use it, with EMIT_MOVE_IMM false to keep the chunks as direct
+	 immediate subtractions where possible.  */
+      rtx temp = temp1 ? temp1 : temp2;
+      while (size >= probe_interval)
+	{
+	  aarch64_add_offset_1 (mode, dest, src, -probe_interval, temp,
+				frame_related_p, true);
+	  emit_stack_probe (dest);
+	  size -= probe_interval;
+	}
+      if (size > 0)
+	aarch64_add_offset_1 (mode, dest, src, -size, temp,
+			      frame_related_p, true);
+      return;
+    }
+
   /* Try using ADDVL or ADDPL to add the whole value.  */
   if (src != const0_rtx && aarch64_sve_addvl_addpl_immediate_p (offset))
     {
@@ -5855,6 +5889,8 @@ aarch64_sub_sp (rtx temp1, rtx temp2, poly_int64 delta,
 		aarch64_isa_mode force_isa_mode,
 		bool frame_related_p, bool emit_move_imm = true)
 {
+  /* Stack probing for Windows on ARM64 is handled inside
+     aarch64_add_offset, which this function calls.  */
   aarch64_add_offset (Pmode, stack_pointer_rtx, stack_pointer_rtx, -delta,
 		      temp1, temp2, force_isa_mode, frame_related_p,
 		      emit_move_imm);
@@ -10401,14 +10437,21 @@ aarch64_allocate_and_probe_stack_space (rtx temp1, rtx temp2,
     }
 
   /* If SIZE is not large enough to require probing, just adjust the stack and
-     exit.  */
-  if (known_lt (poly_size, min_probe_threshold)
-      || !flag_stack_clash_protection)
-    {
-      aarch64_sub_sp (temp1, temp2, poly_size, force_isa_mode,
-		      frame_related_p);
-      return;
-    }
+      exit.  */
+   if (known_lt (poly_size, min_probe_threshold)
+       || !flag_stack_clash_protection)
+     {
+       /* On Windows (PE/COFF) the OS grows the stack via guard pages, so a
+	 stack allocation that moves SP by more than one page must touch each
+	 intervening page to hit the guard page; otherwise a large frame that
+	 jumps over it faults with an access violation.  This is required
+	 regardless of -fstack-clash-protection, matching the behaviour of
+	 the LLVM backend for Windows on AArch64.  Probing is handled inside
+	 aarch64_sub_sp -> aarch64_add_offset.  */
+       aarch64_sub_sp (temp1, temp2, poly_size, force_isa_mode,
+		       frame_related_p);
+       return;
+     }
 
   HOST_WIDE_INT size;
   /* Handle the SVE non-constant case first.  */
@@ -26895,6 +26938,11 @@ aarch64_declare_function_name (FILE *stream, const char* name,
   ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "function");
   ASM_OUTPUT_FUNCTION_LABEL (stream, name, fndecl);
 
+#ifdef SUBTARGET_ASM_UNWIND_INIT
+  if (TARGET_AARCH64_MS_ABI)
+    SUBTARGET_ASM_UNWIND_INIT (stream);
+#endif
+
   cfun->machine->label_is_assembled = true;
 }
 
diff --git a/gcc/config/aarch64/cygming.h b/gcc/config/aarch64/cygming.h
index bb3c750df69..442c2449c4f 100644
--- a/gcc/config/aarch64/cygming.h
+++ b/gcc/config/aarch64/cygming.h
@@ -45,18 +45,56 @@ 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.  */
+/* Enable SEH for Windows on ARM64.  */
 #undef TARGET_SEH
-#define TARGET_SEH 0
+#define TARGET_SEH  flag_unwind_tables
 
 #define SSE_REGNO_P(N) (gcc_unreachable (), 0)
 #define GENERAL_REGNO_P(N) (gcc_unreachable (), 0)
-#define SEH_MAX_FRAME_SIZE (gcc_unreachable (), 0)
+
+/* ARM64 SEH can represent up to 1MB - 4 bytes per unwind fragment.  */
+#define SEH_MAX_FRAME_SIZE ((1U << 20) - 4)
+
+/* 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
+#define SUBTARGET_ASM_UNWIND_INIT  mingw_pe_seh_init
+
+/* Always limit stack alignment to STACK_BOUNDARY: AArch64 has no DRAP
+   support (cannot realign the stack).  When SEH is active it is also
+   incompatible with DRAP, but the limitation applies regardless.  */
+#undef MAX_STACK_ALIGNMENT
+#define MAX_STACK_ALIGNMENT STACK_BOUNDARY
 
 #undef TARGET_PECOFF
 #define TARGET_PECOFF 1
 
+/* Windows executables use the .exe suffix, matching i386/cygming.h.  */
+#define TARGET_EXECUTABLE_SUFFIX ".exe"
+
+/* Force shared libgcc for aarch64-w64-mingw32 so libstdc++ links against
+   libgcc_s_seh-1.dll at runtime (needed for SEH unwinding to work).  */
+#undef SHARED_LIBGCC_SPEC
+#define SHARED_LIBGCC_SPEC \
+  "%{static|static-libgcc:-lgcc -lgcc_eh} \
+   %{!static: \
+     %{!static-libgcc: \
+       %{!shared: \
+         %{!shared-libgcc:-lgcc_s -lgcc} \
+         %{shared-libgcc:-lgcc_s -lgcc} \
+        } \
+       %{shared:-lgcc_s -lgcc} \
+      } \
+    } "
+
 #include <stdbool.h>
 #ifdef __MINGW32__
 #include <stdio.h>
@@ -70,6 +108,12 @@ still needed for compilation.  */
 #define TARGET_ASM_UNIQUE_SECTION mingw_pe_unique_section
 #define TARGET_ENCODE_SECTION_INFO  mingw_pe_encode_section_info
 
+/* Local and global relocs can be placed always into readonly memory
+   for PE-COFF targets.  */
+#undef TARGET_ASM_RELOC_RW_MASK
+#define TARGET_ASM_RELOC_RW_MASK i386_pe_reloc_rw_mask
+extern int i386_pe_reloc_rw_mask (void);
+
 #define TARGET_VALID_DLLIMPORT_ATTRIBUTE_P mingw_pe_valid_dllimport_attribute_p
 
 /* Output function declarations at the end of the file.  */
@@ -129,6 +173,8 @@ still needed for compilation.  */
       builtin_define ("__fastcall=__attribute__((__fastcall__))");	\
       builtin_define ("__thiscall=__attribute__((__thiscall__))");	\
       builtin_define ("__cdecl=__attribute__((__cdecl__))");		\
+      if (TARGET_SEH)							\
+      builtin_define ("__SEH__");					\
     }									\
   while (0)
 
@@ -200,7 +246,7 @@ still needed for compilation.  */
 #undef  SUBTARGET_OVERRIDE_OPTIONS
 #define SUBTARGET_OVERRIDE_OPTIONS			\
   do {							\
-    flag_stack_check = STATIC_BUILTIN_STACK_CHECK;	\
+    flag_unwind_tables = 1;				\
   } while (0)
 
 #define SUBTARGET_ATTRIBUTE_TABLE \
@@ -231,6 +277,16 @@ still needed for compilation.  */
     aarch64_declare_function_name (STREAM, NAME, DECL);			\
   } while (0)
 
+#undef ASM_DECLARE_COLD_FUNCTION_NAME
+#define ASM_DECLARE_COLD_FUNCTION_NAME(STREAM, NAME, DECL) \
+  do {							       \
+    mingw_pe_declare_type (STREAM, NAME, TREE_PUBLIC (DECL), 1); \
+    mingw_pe_seh_cold_init (STREAM, NAME);			    \
+  } while (0)
+
+#undef ASM_DECLARE_COLD_FUNCTION_SIZE
+#define ASM_DECLARE_COLD_FUNCTION_SIZE(STREAM, NAME, DECL)	\
+  mingw_pe_end_cold_function (STREAM, NAME, DECL)
 
 /* Define this to be nonzero if static stack checking is supported.  */
 #define STACK_CHECK_STATIC_BUILTIN 1
@@ -243,8 +299,7 @@ still needed for compilation.  */
 #undef GOT_ALIAS_SET
 #define GOT_ALIAS_SET mingw_GOT_alias_set ()
 
-#define PE_COFF_LEGITIMIZE_EXTERN_DECL(RTX) \
-  (GET_CODE (RTX) == SYMBOL_REF && SYMBOL_REF_WEAK (RTX))
+#define PE_COFF_LEGITIMIZE_EXTERN_DECL(RTX) 1
 
 #define HAVE_64BIT_POINTERS 1
 
diff --git a/gcc/config/aarch64/t-aarch64-mingw b/gcc/config/aarch64/t-aarch64-mingw
index c92efe4f5f5..6d205d8fb90 100644
--- a/gcc/config/aarch64/t-aarch64-mingw
+++ b/gcc/config/aarch64/t-aarch64-mingw
@@ -18,8 +18,10 @@
 # along with GCC; see the file COPYING3.  If not see
 # <http://www.gnu.org/licenses/>.
 
-aarch64-abi-ms.o: \
-  $(srcdir)/config/aarch64/aarch64-abi-ms.cc \
-  $(TREE_H)
+aarch64-abi-ms.o: $(srcdir)/config/aarch64/aarch64-abi-ms.cc \
+  $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \
+  $(REGS_H) memmodel.h output.h $(FUNCTION_ABI_H) \
+  $(srcdir)/config/aarch64/aarch64-abi-ms-protos.h \
+  $(srcdir)/config/mingw/winnt.h
 	$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
 		$(srcdir)/config/aarch64/aarch64-abi-ms.cc
diff --git a/gcc/config/mingw/mingw32.h b/gcc/config/mingw/mingw32.h
index 1d319b77bf8..2a2665f112f 100644
--- a/gcc/config/mingw/mingw32.h
+++ b/gcc/config/mingw/mingw32.h
@@ -180,20 +180,22 @@ along with GCC; see the file COPYING3.  If not see
   %(shared_libgcc_undefs)"
 
 /* Include in the mingw32 libraries with libgcc */
-#ifdef ENABLE_SHARED_LIBGCC
-#define SHARED_LIBGCC_SPEC " \
- %{static|static-libgcc:-lgcc -lgcc_eh} \
- %{!static: \
-   %{!static-libgcc: \
-     %{!shared: \
-       %{!shared-libgcc:-lgcc -lgcc_eh} \
-       %{shared-libgcc:-lgcc_s -lgcc} \
-      } \
-     %{shared:-lgcc_s -lgcc} \
-    } \
-  } "
-#else
-#define SHARED_LIBGCC_SPEC " -lgcc "
+#ifndef SHARED_LIBGCC_SPEC
+# ifdef ENABLE_SHARED_LIBGCC
+#  define SHARED_LIBGCC_SPEC " \
+  %{static|static-libgcc:-lgcc -lgcc_eh} \
+  %{!static: \
+    %{!static-libgcc: \
+      %{!shared: \
+        %{!shared-libgcc:-lgcc -lgcc_eh} \
+        %{shared-libgcc:-lgcc_s -lgcc} \
+       } \
+      %{shared:-lgcc_s -lgcc} \
+     } \
+   } "
+# else
+#  define SHARED_LIBGCC_SPEC " -lgcc "
+# endif
 #endif
 #ifdef TARGET_USING_MCFGTHREAD
 #define MCFGTHREAD_SPEC  " -lmcfgthread -lkernel32 -lntdll "
diff --git a/gcc/config/mingw/winnt-dll.cc b/gcc/config/mingw/winnt-dll.cc
index a28ae267fc9..661e526050f 100644
--- a/gcc/config/mingw/winnt-dll.cc
+++ b/gcc/config/mingw/winnt-dll.cc
@@ -159,7 +159,10 @@ legitimize_pe_coff_extern_decl (rtx symbol, bool want_reg)
   gcc_assert (SYMBOL_REF_DECL (symbol));
   imp_decl = get_dllimport_decl (SYMBOL_REF_DECL (symbol), false);
 
-  x = DECL_RTL (imp_decl);
+  /* DECL_RTL is a shared rtx that is cached per refptr symbol.  Copy it so
+     that each use gets its own MEM, otherwise the same rtx object ends up in
+     multiple insns which breaks RTL sharing verification.  */
+  x = copy_rtx (DECL_RTL (imp_decl));
   if (want_reg)
     x = force_reg (Pmode, x);
   return x;
@@ -177,7 +180,10 @@ legitimize_dllimport_symbol (rtx symbol, bool want_reg)
   gcc_assert (SYMBOL_REF_DECL (symbol));
   imp_decl = get_dllimport_decl (SYMBOL_REF_DECL (symbol), true);
 
-  x = DECL_RTL (imp_decl);
+  /* DECL_RTL is a shared rtx that is cached per __imp_ symbol.  Copy it so
+     that each use gets its own MEM, otherwise the same rtx object ends up in
+     multiple insns which breaks RTL sharing verification.  */
+  x = copy_rtx (DECL_RTL (imp_decl));
   if (want_reg)
     x = force_reg (Pmode, x);
   return x;
diff --git a/gcc/config/mingw/winnt.cc b/gcc/config/mingw/winnt.cc
index 66d7450652d..360b18628ec 100644
--- a/gcc/config/mingw/winnt.cc
+++ b/gcc/config/mingw/winnt.cc
@@ -925,87 +925,101 @@ mingw_pe_seh_end_prologue (FILE *f)
   fputs ("\t.seh_endprologue\n", f);
 }
 
-/* Emit assembler directives to reconstruct the SEH state.  */
+/* Emit assembler directives to reconstruct the SEH state for
+   a cold section.  On x64 this reconstructs the full prologue;
+   on aarch64 the per-insn emitter handles prologue directives.  */
 
 void
 mingw_pe_seh_cold_init (FILE *f, const char *name)
 {
   struct seh_frame_state *seh;
-  HOST_WIDE_INT alloc_offset, offset;
 
   if (!TARGET_SEH)
     return;
   if (cfun->is_thunk)
     return;
   seh = cfun->machine->seh;
+  gcc_assert (seh != NULL);
+
+  seh->in_cold_section = true;
 
   fputs ("\t.seh_proc\t", f);
   assemble_name (f, name);
   fputc ('\n', f);
 
-  /* In the normal case, the frame pointer is near the bottom of the frame
-     so we can do the full stack allocation and set it afterwards.  There
-     is an exception if the function overflows the SEH maximum frame size
-     or accesses prior frames so, in this case, we need to pre-allocate a
-     small chunk of stack before setting it.  */
-  offset = seh->sp_offset - INCOMING_FRAME_SP_OFFSET;
-  if (offset < SEH_MAX_FRAME_SIZE && !crtl->accesses_prior_frames)
-    alloc_offset = seh->sp_offset;
-  else
-    alloc_offset = MIN (seh->cfa_offset + 240, seh->sp_offset);
-
-  offset = alloc_offset - INCOMING_FRAME_SP_OFFSET;
-  if (offset > 0)
-    fprintf (f, "\t.seh_stackalloc\t" HOST_WIDE_INT_PRINT_DEC "\n", offset);
+#if defined(__x86_64__)
+  {
+    HOST_WIDE_INT alloc_offset, offset;
+
+    /* In the normal case, the frame pointer is near the bottom of the frame
+       so we can do the full stack allocation and set it afterwards.  There
+       is an exception if the function overflows the SEH maximum frame size
+       or accesses prior frames so, in this case, we need to pre-allocate a
+       small chunk of stack before setting it.  */
+    offset = seh->sp_offset - INCOMING_FRAME_SP_OFFSET;
+    if (offset < SEH_MAX_FRAME_SIZE && !crtl->accesses_prior_frames)
+      alloc_offset = seh->sp_offset;
+    else
+      alloc_offset = MIN (seh->cfa_offset + 240, seh->sp_offset);
+
+    offset = alloc_offset - INCOMING_FRAME_SP_OFFSET;
+    if (offset > 0)
+      fprintf (f, "\t.seh_stackalloc\t" HOST_WIDE_INT_PRINT_DEC "\n", offset);
+
+    for (int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+      if (seh->reg_offset[regno] > 0 && seh->reg_offset[regno] <= alloc_offset)
+	{
+	  if (SSE_REGNO_P (regno))
+	    fputs ("\t.seh_savexmm\t", f);
+	  else if (GENERAL_REGNO_P (regno))
+	    fputs ("\t.seh_savereg\t", f);
+	  else
+	    gcc_unreachable ();
+	  print_reg (gen_rtx_REG (DImode, regno), 0, f);
+	  fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n",
+		   alloc_offset - seh->reg_offset[regno]);
+	}
 
-  for (int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-    if (seh->reg_offset[regno] > 0 && seh->reg_offset[regno] <= alloc_offset)
+    if (seh->cfa_reg != stack_pointer_rtx)
       {
-	if (SSE_REGNO_P (regno))
-	  fputs ("\t.seh_savexmm\t", f);
-	else if (GENERAL_REGNO_P (regno))
-	  fputs ("\t.seh_savereg\t", f);
-	else
-	  gcc_unreachable ();
-	print_reg (gen_rtx_REG (DImode, regno), 0, f);
-	fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n",
-		 alloc_offset - seh->reg_offset[regno]);
-      }
+	offset = alloc_offset - seh->cfa_offset;
 
-  if (seh->cfa_reg != stack_pointer_rtx)
-    {
-      offset = alloc_offset - seh->cfa_offset;
+	gcc_assert ((offset & 15) == 0);
+	gcc_assert (IN_RANGE (offset, 0, 240));
 
-      gcc_assert ((offset & 15) == 0);
-      gcc_assert (IN_RANGE (offset, 0, 240));
-
-      fputs ("\t.seh_setframe\t", f);
-      print_reg (seh->cfa_reg, 0, f);
-      fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n", offset);
-    }
+	fputs ("\t.seh_setframe\t", f);
+	print_reg (seh->cfa_reg, 0, f);
+	fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n", offset);
+      }
 
-  if (alloc_offset != seh->sp_offset)
-    {
-      offset = seh->sp_offset - alloc_offset;
-      if (offset > 0 && offset < SEH_MAX_FRAME_SIZE)
-	fprintf (f, "\t.seh_stackalloc\t" HOST_WIDE_INT_PRINT_DEC "\n", offset);
+    if (alloc_offset != seh->sp_offset)
+      {
+	offset = seh->sp_offset - alloc_offset;
+	if (offset > 0 && offset < SEH_MAX_FRAME_SIZE)
+	  fprintf (f, "\t.seh_stackalloc\t" HOST_WIDE_INT_PRINT_DEC "\n", offset);
 
-      for (int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-	if (seh->reg_offset[regno] > alloc_offset)
-	  {
-	    if (SSE_REGNO_P (regno))
-	      fputs ("\t.seh_savexmm\t", f);
-	    else if (GENERAL_REGNO_P (regno))
-	      fputs ("\t.seh_savereg\t", f);
-	    else
-	      gcc_unreachable ();
-	    print_reg (gen_rtx_REG (DImode, regno), 0, f);
-	    fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n",
-		     seh->sp_offset - seh->reg_offset[regno]);
-	  }
-    }
+	for (int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+	  if (seh->reg_offset[regno] > alloc_offset)
+	    {
+	      if (SSE_REGNO_P (regno))
+		fputs ("\t.seh_savexmm\t", f);
+	      else if (GENERAL_REGNO_P (regno))
+		fputs ("\t.seh_savereg\t", f);
+	      else
+		gcc_unreachable ();
+	      print_reg (gen_rtx_REG (DImode, regno), 0, f);
+	      fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n",
+		       seh->sp_offset - seh->reg_offset[regno]);
+	    }
+      }
 
-  fputs ("\t.seh_endprologue\n", f);
+    fputs ("\t.seh_endprologue\n", f);
+  }
+#elif defined(__aarch64__)
+  /* Prologue is emitted by aarch64_pe_seh_unwind_emit per insn.  */
+#else
+  gcc_unreachable ();
+#endif
 }
 
 /* Emit an assembler directive for the end of the function.  */
diff --git a/gcc/configure b/gcc/configure
index eea63c9721c..af28cc8acc1 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -28761,21 +28761,32 @@ $as_echo "#define HAVE_AS_MABI_OPTION 1" >>confdefs.h
         as_fn_error $? "Assembler does not support -mabi=ilp32.\
                      Upgrade the Assembler." "$LINENO" 5
       fi
-      if test x"$with_multilib_list" = xdefault; then
-        TM_MULTILIB_CONFIG=lp64
-      else
-        aarch64_multilibs=`echo $with_multilib_list | sed -e 's/,/ /g'`
-        for aarch64_multilib in ${aarch64_multilibs}; do
-          case ${aarch64_multilib} in
-            ilp32)
-              as_fn_error $? "Assembler does not support -mabi=ilp32.\
-                            Upgrade the Assembler." "$LINENO" 5
-              ;;
-            *)
-              ;;
-          esac
-        done
-      fi
+      case "$target" in
+        aarch64*-*-mingw* | aarch64*-*-cygwin* | aarch64*-*-pe*)
+          # Windows on AArch64 has a single LLP64 ABI; the COFF assembler
+          # only understands -mabi=llp64, so the -mabi=lp64 probe above
+          # always fails here.  Keep multilibs enabled (so the driver does
+          # not fall back to single-ABI assumptions) but do not invent a
+          # redundant lp64 multilib variant.
+          ;;
+        *)
+          if test x"$with_multilib_list" = xdefault; then
+            TM_MULTILIB_CONFIG=lp64
+          else
+            aarch64_multilibs=`echo $with_multilib_list | sed -e 's/,/ /g'`
+            for aarch64_multilib in ${aarch64_multilibs}; do
+              case ${aarch64_multilib} in
+                ilp32)
+                  as_fn_error $? "Assembler does not support -mabi=ilp32.\
+                                Upgrade the Assembler." "$LINENO" 5
+                  ;;
+                *)
+                  ;;
+              esac
+            done
+          fi
+          ;;
+      esac
     fi
     # Check if we have binutils support for relocations types needed by -fpic
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for -fpic relocs" >&5
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 58576227136..1d9e6f2f9d6 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -4552,21 +4552,32 @@ case "$target" in
         AC_MSG_ERROR([Assembler does not support -mabi=ilp32.\
                      Upgrade the Assembler.])
       fi
-      if test x"$with_multilib_list" = xdefault; then
-        TM_MULTILIB_CONFIG=lp64
-      else
-        aarch64_multilibs=`echo $with_multilib_list | sed -e 's/,/ /g'`
-        for aarch64_multilib in ${aarch64_multilibs}; do
-          case ${aarch64_multilib} in
-            ilp32)
-              AC_MSG_ERROR([Assembler does not support -mabi=ilp32.\
-                            Upgrade the Assembler.])
-              ;;
-            *)
-              ;;
-          esac
-        done
-      fi
+      case "$target" in
+        aarch64*-*-mingw* | aarch64*-*-cygwin* | aarch64*-*-pe*)
+          # Windows on AArch64 has a single LLP64 ABI; the COFF assembler
+          # only understands -mabi=llp64, so the -mabi=lp64 probe above
+          # always fails here.  Keep multilibs enabled (so the driver does
+          # not fall back to single-ABI assumptions) but do not invent a
+          # redundant lp64 multilib variant.
+          ;;
+        *)
+          if test x"$with_multilib_list" = xdefault; then
+            TM_MULTILIB_CONFIG=lp64
+          else
+            aarch64_multilibs=`echo $with_multilib_list | sed -e 's/,/ /g'`
+            for aarch64_multilib in ${aarch64_multilibs}; do
+              case ${aarch64_multilib} in
+                ilp32)
+                  AC_MSG_ERROR([Assembler does not support -mabi=ilp32.\
+                                Upgrade the Assembler.])
+                  ;;
+                *)
+                  ;;
+              esac
+            done
+          fi
+          ;;
+      esac
     fi
     # Check if we have binutils support for relocations types needed by -fpic
     gcc_GAS_CHECK_FEATURE([-fpic relocs], gcc_cv_as_aarch64_picreloc,,
diff --git a/libgcc/config.host b/libgcc/config.host
index 319d6cfe0ef..29b2bcc4a05 100644
--- a/libgcc/config.host
+++ b/libgcc/config.host
@@ -459,7 +459,14 @@ aarch64-*-mingw*)
 	    tmake_thr_file="mingw/t-mingw-mcfgthread"
 	    ;;
 	esac
+	# Shared libgcc DLL install dir depends on cross/native build.
+	if test x${build} = x${host} ; then
+		tmake_dlldir_file="i386/t-dlldir"
+	else
+		tmake_dlldir_file="i386/t-dlldir-x"
+	fi
 	tmake_file="${tmake_file} mingw/t-seh-eh ${tmake_thr_file}"
+	tmake_file="${tmake_file} ${tmake_dlldir_file} i386/t-slibgcc-cygming i386/t-slibgcc-mingw i386/t-cygming i386/t-mingw32"
 	tmake_file="${tmake_file} t-dfprules"
 	tmake_file="${tmake_file} ${cpu_type}/t-aarch64"
 	tmake_file="${tmake_file} ${cpu_type}/t-mingw"
diff --git a/libgcc/config/aarch64/t-mingw b/libgcc/config/aarch64/t-mingw
index a72a2f70177..8c48852baff 100644
--- a/libgcc/config/aarch64/t-mingw
+++ b/libgcc/config/aarch64/t-mingw
@@ -1,2 +1,8 @@
 # Bypass hidden attribute warnings in MinGW until it is implemented
 LIBGCC2_CFLAGS += -Wno-error=attributes
+
+# AArch64 has no 80-bit extended precision floating point.
+LIBGCC2_CFLAGS += -D__NO_BINARY80__
+
+# Include LSE atomics in the shared libgcc (needed by mingw-w64 CRT).
+libgcc-s-objects += $(LSE_OBJS) lse-init$(objext)
diff --git a/libgcc/unwind-seh.c b/libgcc/unwind-seh.c
index 3bd0c8a4e03..9512c3db008 100644
--- a/libgcc/unwind-seh.c
+++ b/libgcc/unwind-seh.c
@@ -30,12 +30,6 @@
 
 #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__
-#error "Unsupported architecture."
-#endif
-
 /* Define GCC's exception codes.  See
      http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx
    In particular, MS defines bits:
@@ -52,7 +46,7 @@
      [0] = _Unwind_Exception pointer
      [1] = target frame
      [2] = target ip
-     [3] = target rdx
+     [3] = target rdx (x64) / x1 (aarch64)
 */
 
 #define STATUS_USER_DEFINED		(1U << 29)
@@ -65,7 +59,8 @@
 #define STATUS_GCC_UNWIND		GCC_EXCEPTION (1)
 #define STATUS_GCC_FORCED		GCC_EXCEPTION (2)
 
-
+#if defined(__x86_64__)
+
 struct _Unwind_Context
 {
   _Unwind_Word cfa;
@@ -74,8 +69,6 @@ struct _Unwind_Context
   PDISPATCHER_CONTEXT disp;
 };
 
-/* Get the value of register INDEX as saved in CONTEXT.  */
-
 _Unwind_Word
 _Unwind_GetGR (struct _Unwind_Context *c, int index)
 {
@@ -84,8 +77,6 @@ _Unwind_GetGR (struct _Unwind_Context *c, int index)
   return c->reg[index];
 }
 
-/* Overwrite the saved value for register INDEX in CONTEXT with VAL.  */
-
 void
 _Unwind_SetGR (struct _Unwind_Context *c, int index, _Unwind_Word val)
 {
@@ -94,37 +85,25 @@ _Unwind_SetGR (struct _Unwind_Context *c, int index, _Unwind_Word val)
   c->reg[index] = val;
 }
 
-/* Get the value of the CFA as saved in CONTEXT.  */
-
 _Unwind_Word
 _Unwind_GetCFA (struct _Unwind_Context *c)
 {
   return c->cfa;
 }
 
-/* Retrieve the return address for CONTEXT.  */
-
 _Unwind_Ptr
 _Unwind_GetIP (struct _Unwind_Context *c)
 {
   return c->ra;
 }
 
-/* Retrieve the return address and flag whether that IP is before
-   or after first not yet fully executed instruction.  */
-
 _Unwind_Ptr
 _Unwind_GetIPInfo (struct _Unwind_Context *c, int *ip_before_insn)
 {
-  /* ??? Is there a concept of a signal context properly?  There's
-     obviously an UNWP_PUSH_MACHFRAME opcode, but the runtime might
-     have arranged for that not to matter, really.  */
   *ip_before_insn = 0;
   return c->ra;
 }
 
-/* Overwrite the return address for CONTEXT with VAL.  */
-
 void
 _Unwind_SetIP (struct _Unwind_Context *c, _Unwind_Ptr val)
 {
@@ -166,7 +145,100 @@ _Unwind_GetTextRelBase (struct _Unwind_Context *c)
   return c->disp->ImageBase;
 }
 
-
+#elif defined(__aarch64__)
+
+/* AArch64 (ARM64) Windows SEH context structure.
+   References LLVM libunwind's Unwind-seh.cpp for register mapping.  */
+
+struct _Unwind_Context
+{
+  _Unwind_Word cfa;
+  _Unwind_Word ra;
+  _Unwind_Word reg[2];
+  PDISPATCHER_CONTEXT disp;
+};
+
+_Unwind_Word
+_Unwind_GetGR (struct _Unwind_Context *c, int index)
+{
+  if (index < 0 || index >= 2)
+    abort ();
+  return c->reg[index];
+}
+
+void
+_Unwind_SetGR (struct _Unwind_Context *c, int index, _Unwind_Word val)
+{
+  if (index < 0 || index >= 2)
+    abort ();
+  c->reg[index] = val;
+}
+
+_Unwind_Word
+_Unwind_GetCFA (struct _Unwind_Context *c)
+{
+  return c->cfa;
+}
+
+_Unwind_Ptr
+_Unwind_GetIP (struct _Unwind_Context *c)
+{
+  return c->ra;
+}
+
+_Unwind_Ptr
+_Unwind_GetIPInfo (struct _Unwind_Context *c, int *ip_before_insn)
+{
+  *ip_before_insn = 0;
+  return c->ra;
+}
+
+void
+_Unwind_SetIP (struct _Unwind_Context *c, _Unwind_Ptr val)
+{
+  c->ra = val;
+}
+
+void *
+_Unwind_GetLanguageSpecificData (struct _Unwind_Context *c)
+{
+  return c->disp->HandlerData;
+}
+
+_Unwind_Ptr
+_Unwind_GetRegionStart (struct _Unwind_Context *c)
+{
+  return (c->disp->FunctionEntry->BeginAddress
+	  + (_Unwind_Ptr)c->disp->ImageBase);
+}
+
+void *
+_Unwind_FindEnclosingFunction (void *pc)
+{
+  PRUNTIME_FUNCTION entry;
+  ULONG64 ImageBase;
+
+  entry = RtlLookupFunctionEntry ((ULONG64)pc, &ImageBase, NULL);
+
+  return (entry ? (void *)(entry->BeginAddress + ImageBase) : NULL);
+}
+
+_Unwind_Ptr
+_Unwind_GetDataRelBase (struct _Unwind_Context *c ATTRIBUTE_UNUSED)
+{
+  return 0;
+}
+
+_Unwind_Ptr
+_Unwind_GetTextRelBase (struct _Unwind_Context *c)
+{
+  return c->disp->ImageBase;
+}
+
+#else
+#error "Unsupported architecture for SEH."
+#endif
+
 /* The two-phase unwind process that GCC uses is ordered differently
    from the two-phase unwind process that SEH uses.  The mechansism
    that GCC uses is to have the filter return _URC_HANDER_FOUND; the
@@ -205,33 +277,45 @@ _GCC_specific_handler (PEXCEPTION_RECORD ms_exc, void *this_frame,
 
   if (ms_flags & EXCEPTION_TARGET_UNWIND)
     {
-      /* This frame is known to be the target frame.  We've already
-         "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.  */
+#if defined(__x86_64__)
       ms_disp->ContextRecord->Rdx = ms_exc->ExceptionInformation[3];
+#elif defined(__aarch64__)
+      /* Following LLVM libunwind Unwind-seh.cpp for aarch64 register mapping.  */
+      ms_disp->ContextRecord->X1 = ms_exc->ExceptionInformation[3];
+#endif
       return ExceptionContinueSearch;
     }
 
   if (ms_code == STATUS_GCC_UNWIND)
     {
-      /* This is a colliding exception that we threw so that we could
-         cancel the already in-flight exception and stop in a frame
-	 that wanted to perform some unwind action.  The only relevant
-	 test is that we're the target frame.  */
       if (ms_exc->ExceptionInformation[1] == (_Unwind_Ptr) this_frame)
 	{
+#if defined(__x86_64__)
+	  /* This is a colliding exception that we threw so that we could
+	     cancel the already in-flight exception and stop in a frame
+	     that wanted to perform some unwind action.  The only relevant
+	     test is that we're the target frame.  */
 	  RtlUnwindEx (this_frame, (PVOID) ms_exc->ExceptionInformation[2],
 		       ms_exc, gcc_exc, ms_orig_context,
 		       ms_disp->HistoryTable);
+#elif defined(__aarch64__)
+	  CONTEXT new_ctx;
+	  RtlUnwindEx (this_frame, (PVOID) ms_exc->ExceptionInformation[2],
+		       ms_exc, gcc_exc, &new_ctx,
+		       ms_disp->HistoryTable);
+#endif
 	  abort ();
 	}
       return ExceptionContinueSearch;
     }
 
+#if defined(__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[0] = 0xdeadbeef;
   gcc_context.reg[1] = 0xdeadbeef;
   gcc_context.disp = ms_disp;
 
@@ -248,17 +332,11 @@ _GCC_specific_handler (PEXCEPTION_RECORD ms_exc, void *this_frame,
        goto phase2;
     }
 
-  /* ??? TODO: handling non-gcc user-defined exceptions as foreign.  */
   if (ms_code != STATUS_GCC_THROW)
     return ExceptionContinueSearch;
 
   if (ms_flags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND))
     {
-      /* This is Phase 2.  */
-      /* We know this isn't the target frame because we've already tested
-	 EXCEPTION_TARGET_UNWIND.  The remaining possibility is that the
-	 gcc personality has unwind code to run.  */
-
       gcc_action = _UA_CLEANUP_PHASE;
     phase2:
       gcc_reason = gcc_per (1, gcc_action, gcc_exc->exception_class,
@@ -269,23 +347,16 @@ _GCC_specific_handler (PEXCEPTION_RECORD ms_exc, void *this_frame,
 
       if (gcc_reason == _URC_INSTALL_CONTEXT)
 	{
-	  /* Scratch space for the bits for the unwind catch.  */
 	  ms_exc->ExceptionInformation[1] = (_Unwind_Ptr) this_frame;
 	  ms_exc->ExceptionInformation[2] = gcc_context.ra;
 	  ms_exc->ExceptionInformation[3] = gcc_context.reg[1];
 
-	  /* Cancel the current exception by raising another.  */
 	  RaiseException (STATUS_GCC_UNWIND, EXCEPTION_NONCONTINUABLE,
 			  4, ms_exc->ExceptionInformation);
-
-	  /* Is RaiseException declared noreturn?  */
 	}
-
-      /* In _Unwind_RaiseException_Phase2 we return _URC_FATAL_PHASE2_ERROR. */
     }
   else
     {
-      /* This is Phase 1.  */
       gcc_reason = gcc_per (1, _UA_SEARCH_PHASE, gcc_exc->exception_class,
 			    gcc_exc, &gcc_context);
 
@@ -294,9 +365,6 @@ _GCC_specific_handler (PEXCEPTION_RECORD ms_exc, void *this_frame,
 
       if (gcc_reason == _URC_HANDLER_FOUND)
 	{
-	  /* We really need some of the information that GCC's personality
-	     routines compute during phase 2 right now, like the target IP.
-	     Go ahead and ask for it now, and cache it.  */
 	  gcc_reason = gcc_per (1, _UA_CLEANUP_PHASE | _UA_HANDLER_FRAME,
 				gcc_exc->exception_class, gcc_exc,
 				&gcc_context);
@@ -312,13 +380,10 @@ _GCC_specific_handler (PEXCEPTION_RECORD ms_exc, void *this_frame,
 	  ms_exc->ExceptionInformation[2] = gcc_context.ra;
 	  ms_exc->ExceptionInformation[3] = gcc_context.reg[1];
 
-	  /* Begin phase 2.  Perform the unwinding.  */
 	  RtlUnwindEx (this_frame, (PVOID)gcc_context.ra, ms_exc,
-		       (PVOID)gcc_context.reg[0], ms_orig_context,
+		       gcc_exc, ms_orig_context,
 		       ms_disp->HistoryTable);
 	}
-
-      /* In _Unwind_RaiseException we return _URC_FATAL_PHASE1_ERROR.  */
     }
   abort ();
 }
@@ -452,6 +517,7 @@ _Unwind_Backtrace(_Unwind_Trace_Fn trace,
 
   while (1)
     {
+#if defined(__x86_64__)
       gcc_context.disp->ControlPc = ms_context.Rip;
       gcc_context.disp->FunctionEntry
 	= RtlLookupFunctionEntry (ms_context.Rip, &gcc_context.disp->ImageBase,
@@ -466,18 +532,39 @@ _Unwind_Backtrace(_Unwind_Trace_Fn trace,
 			    &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;
 
-      /* 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)
 	return _URC_END_OF_STACK;
+#elif defined(__aarch64__)
+      gcc_context.disp->ControlPc = ms_context.Pc;
+      gcc_context.disp->FunctionEntry
+	= RtlLookupFunctionEntry (ms_context.Pc, &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.Pc,
+			    gcc_context.disp->FunctionEntry, &ms_context,
+			    &gcc_context.disp->HandlerData,
+			    &gcc_context.disp->EstablisherFrame, NULL);
+
+      gcc_context.ra = ms_context.Pc;
+      gcc_context.cfa = ms_context.Sp;
+
+      if (trace (&gcc_context, trace_argument) != _URC_NO_REASON)
+	return _URC_FATAL_PHASE1_ERROR;
+
+      if (ms_context.Pc == 0)
+	return _URC_END_OF_STACK;
+#endif
     }
 }
 #endif /* __SEH__  && !defined (__USING_SJLJ_EXCEPTIONS__)  */
+
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