[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