[PATCH] aarch64-w64-mingw32: Complete Windows on ARM support
coshvji cujmlqef <[email protected]> Sun, 2 Aug 2026 20:25:12 +0800
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <CAJkdwqFFEUdP-Em7vN7UKTyy8L_BDchsvOTOhwP9p7KKdic_rw@mail.gmail.com> |
From 6608769b5bc3b1aeac2ef03668698a6177175100 Mon Sep 17 00:00:00 2001 From: trcrsired <[email protected]> Date: Thu, 30 Jul 2026 13:51:58 +0800 Subject: [PATCH] aarch64-w64-mingw32: Complete Windows on ARM support 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 | 66 +++- 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 | 195 +++++++---- 21 files changed, 844 insertions(+), 199 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 d19ca305d82..550edc1b4dd 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..c2f1fa639d4 100644 --- a/gcc/config/aarch64/cygming.h +++ b/gcc/config/aarch64/cygming.h @@ -45,18 +45,53 @@ 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 +/* 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 +105,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 +170,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 +243,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 +274,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 +296,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..14cb8912e89 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,35 @@ _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) { + CONTEXT new_ctx; RtlUnwindEx (this_frame, (PVOID) ms_exc->ExceptionInformation[2], - ms_exc, gcc_exc, ms_orig_context, + ms_exc, gcc_exc, &new_ctx, ms_disp->HistoryTable); 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 +322,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 +337,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 +355,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 +370,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 +507,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 +522,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__) */ + -- 2.55.0
0001-aarch64-w64-mingw32-Complete-Windows-on-ARM-support.patch
(application/octet-stream, 53.9 KB)
From 6608769b5bc3b1aeac2ef03668698a6177175100 Mon Sep 17 00:00:00 2001 From: trcrsired <[email protected]> Date: Thu, 30 Jul 2026 13:51:58 +0800 Subject: [PATCH] aarch64-w64-mingw32: Complete Windows on ARM support 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 | 66 +++- 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 | 195 +++++++---- 21 files changed, 844 insertions(+), 199 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 d19ca305d82..550edc1b4dd 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..c2f1fa639d4 100644 --- a/gcc/config/aarch64/cygming.h +++ b/gcc/config/aarch64/cygming.h @@ -45,18 +45,53 @@ 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 +/* 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 +105,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 +170,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 +243,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 +274,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 +296,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..14cb8912e89 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,35 @@ _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) { + CONTEXT new_ctx; RtlUnwindEx (this_frame, (PVOID) ms_exc->ExceptionInformation[2], - ms_exc, gcc_exc, ms_orig_context, + ms_exc, gcc_exc, &new_ctx, ms_disp->HistoryTable); 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 +322,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 +337,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 +355,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 +370,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 +507,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 +522,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__) */ + -- 2.55.0