[PATCH v7 3/3] aarch64: Implement Structured Exception Handling (SEH) on AArch64
Saurabh Jha
saurabh.jha@arm.com
Thu Mar 26 16:57:57 GMT 2026
On 3/19/2026 11:16 AM, Evgeny Karpov wrote:
> The patch reuses shared helpers for SEH and implements SEH on AArch64.
> The implementation is based on
> (https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170)
> and pdata/xdata SEH records are emitted from md_finish.
>
> When .pdata/.xdata is emitted, the function size is required.
> Function sizes are calculated as late as possible, and the code segment needs
> to be relaxed to be able to calculate the function sizes.
>
> Initially, obj_coff_generate_pdata was declared in write_object_file.
> Before the change, obj_coff_generate_pdata was used only to validate
> syntax, which was sufficient for that purpose. However, that location
> seems incorrect, as it is too late to emit .pdata/.xdata records
> in the AArch64 case.
>
> md_finish has been declared for AArch64 and extended with
> seh_aarch64_write_data to emit .pdata/.xdata records after all
> assembly has been completed.
>
> Signed-off-by: Evgeny Karpov <evgeny@kmaps.co>
>
> gas/ChangeLog:
> * gas/config/obj-coff-seh-shared.c (defined): Update.
> * gas/config/obj-coff.c (defined): Update.
> * gas/config/tc-aarch64.c (defined): Add OBJ_COFF guard.
> (aarch64_md_finish): Add.
> * gas/config/tc-aarch64.h (defined): Add OBJ_COFF guard.
> (md_finish): Add.
> (aarch64_md_finish): Add.
> (seh_aarch64_write_data): Add.
> * gas/write.c: Update.
> * gas/write.h (subsegs_finish_section): Update.
> * gas/config/obj-coff-seh-aarch64.c: New file.
> * gas/config/obj-coff-seh-aarch64.h: New file.
> ---
> gas/config/obj-coff-seh-aarch64.c | 889 ++++++++++++++++++++++++++++++
> gas/config/obj-coff-seh-aarch64.h | 265 +++++++++
> gas/config/obj-coff-seh-shared.c | 5 +
> gas/config/obj-coff.c | 4 +
> gas/config/tc-aarch64.c | 10 +
> gas/config/tc-aarch64.h | 6 +
> gas/write.c | 2 +-
> gas/write.h | 1 +
> 8 files changed, 1181 insertions(+), 1 deletion(-)
> create mode 100644 gas/config/obj-coff-seh-aarch64.c
> create mode 100644 gas/config/obj-coff-seh-aarch64.h
>
> diff --git a/gas/config/obj-coff-seh-aarch64.c b/gas/config/obj-coff-seh-aarch64.c
> new file mode 100644
> index 00000000000..6b5354f6544
> --- /dev/null
> +++ b/gas/config/obj-coff-seh-aarch64.c
> @@ -0,0 +1,889 @@
> +/* SEH .pdata/.xdata COFF object file format on AArch64
> + Copyright (C) 2026 Free Software Foundation, Inc.
> +
> + This file is part of GAS.
> +
> + GAS 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.
> +
> + GAS 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 GAS; see the file COPYING. If not, write to the Free
> + Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
> + 02110-1301, USA. */
> +
> +#include "obj-coff-seh-aarch64.h"
> +
> +static struct seh_aarch64_context *seh_ctx_root = NULL;
> +static bool in_seh_proc = false;
> +
> +struct aarch64_unwind_code_pack_info {
> + const char *directive;
> + unsigned offset_bits;
> + unsigned reg_bits;
> + unsigned code_bits;
> + unsigned code;
> + unsigned offset_right_shift;
> + unsigned offset;
> + unsigned reg_right_shift;
> + unsigned reg_offset;
> + unsigned size;
> +};
> +
> +static const struct aarch64_unwind_code_pack_info
> +aarch64_unwind_code_pack_data[] = {
> +/* Unwind codes packing for AArch64 is described at
> + https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#unwind-codes
> + and calculated in seh_aarch64_add_unwind_element function. */
> + {
> + .directive = NULL, .offset_bits = 5, .reg_bits = 0,
> + .code_bits = 3, .code = AARCH64_UNOP_ALLOCS, .offset_right_shift = 4,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1
> + },
> + {
> + .directive = NULL, .offset_bits = 11, .reg_bits = 0,
> + .code_bits = 5, .code = AARCH64_UNOP_ALLOCM, .offset_right_shift = 4,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 2
> + },
> + {
> + .directive = NULL, .offset_bits = 24, .reg_bits = 0,
> + .code_bits = 8, .code = AARCH64_UNOP_ALLOCL, .offset_right_shift = 4,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 4
> + },
> + {
> + .directive = ".seh_save_reg", .offset_bits = 6, .reg_bits = 4,
> + .code_bits = 6, .code = AARCH64_UNOP_SAVEREG, .offset_right_shift = 3,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 19, .size = 2
> + },
> + {
> + .directive = ".seh_save_reg_x", .offset_bits = 5, .reg_bits = 4,
> + .code_bits = 7, .code = AARCH64_UNOP_SAVEREGX, .offset_right_shift = 3,
> + .offset = 1, .reg_right_shift = 0, .reg_offset = 19, .size = 2
> + },
> + {
> + .directive = ".seh_save_regp", .offset_bits = 6, .reg_bits = 4,
> + .code_bits = 6, .code = AARCH64_UNOP_SAVEREGP, .offset_right_shift = 3,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 19, .size = 2
> + },
> + {
> + .directive = ".seh_save_regp_x", .offset_bits = 6, .reg_bits = 4,
> + .code_bits = 6, .code = AARCH64_UNOP_SAVEREGPX, .offset_right_shift = 3,
> + .offset = 1, .reg_right_shift = 0, .reg_offset = 19, .size = 2
> + },
> + {
> + .directive = ".seh_save_fregp", .offset_bits = 6, .reg_bits = 3,
> + .code_bits = 7, .code = AARCH64_UNOP_SAVEFREGP, .offset_right_shift = 3,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 8, .size = 2
> + },
> + {
> + .directive = ".seh_save_fregp_x", .offset_bits = 6, .reg_bits = 3,
> + .code_bits = 7, .code = AARCH64_UNOP_SAVEFREGPX, .offset_right_shift = 3,
> + .offset = 1, .reg_right_shift = 0, .reg_offset = 8, .size = 2
> + },
> + {
> + .directive = ".seh_save_freg", .offset_bits = 6, .reg_bits = 3,
> + .code_bits = 7, .code = AARCH64_UNOP_SAVEFREG, .offset_right_shift = 3,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 8, .size = 2
> + },
> + {
> + .directive = ".seh_save_freg_x", .offset_bits = 5, .reg_bits = 3,
> + .code_bits = 8, .code = AARCH64_UNOP_SAVEFREGX, .offset_right_shift = 3,
> + .offset = 1, .reg_right_shift = 0, .reg_offset = 8, .size = 2
> + },
> + {
> + .directive = ".seh_save_lrpair", .offset_bits = 6, .reg_bits = 3,
> + .code_bits = 7, .code = AARCH64_UNOP_SAVELRPAIR, .offset_right_shift = 3,
> + .offset = 0, .reg_right_shift = 1, .reg_offset = 19, .size = 2
> + },
> + {
> + .directive = ".seh_save_fplr", .offset_bits = 6, .reg_bits = 0,
> + .code_bits = 2, .code = AARCH64_UNOP_SAVEFPLR, .offset_right_shift = 3,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1
> + },
> + {
> + .directive = ".seh_save_fplr_x", .offset_bits = 6, .reg_bits = 0,
> + .code_bits = 2, .code = AARCH64_UNOP_SAVEFPLRX, .offset_right_shift = 3,
> + .offset = 1, .reg_right_shift = 0, .reg_offset = 0, .size = 1
> + },
> + {
> + .directive = ".seh_save_r19r20_x", .offset_bits = 5, .reg_bits = 0,
> + .code_bits = 3, .code = AARCH64_UNOP_SAVER19R20X, .offset_right_shift = 3,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1
> + },
> + {
> + .directive = ".seh_add_fp", .offset_bits = 8, .reg_bits = 0,
> + .code_bits = 8, .code = AARCH64_UNOP_ADDFP, .offset_right_shift = 0,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 2
> + },
> + {
> + .directive = ".seh_set_fp", .offset_bits = 0, .reg_bits = 0,
> + .code_bits = 8, .code = AARCH64_UNOP_SETFP, .offset_right_shift = 0,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1
> + },
> + {
> + .directive = ".seh_save_next", .offset_bits = 0, .reg_bits = 0,
> + .code_bits = 8, .code = AARCH64_UNOP_SAVENEXT, .offset_right_shift = 0,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1
> + },
> + {
> + .directive = ".seh_nop", .offset_bits = 0, .reg_bits = 0,
> + .code_bits = 8, .code = AARCH64_UNOP_NOP, .offset_right_shift = 0,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1
> + },
> + {
> + .directive = ".seh_pac_sign_lr", .offset_bits = 0, .reg_bits = 0,
> + .code_bits = 8, .code = AARCH64_UNOP_PACSIGNLR, .offset_right_shift = 0,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1
> + },
> + {
> + .directive = NULL, .offset_bits = 0, .reg_bits = 0,
> + .code_bits = 8, .code = AARCH64_UNOP_END, .offset_right_shift = 0,
> + .offset = 0, .reg_right_shift = 0, .reg_offset = 0, .size = 1
> + },
> +};
> +
> +/* Set for current context the default handler. */
> +static void
> +obj_coff_seh_handler (const int what ATTRIBUTE_UNUSED)
> +{
> + char *symbol_name;
> + char name_end;
> +
> + if (!verify_context (".seh_handler"))
> + return;
> +
> + if (*input_line_pointer == 0 || *input_line_pointer == '\n')
> + as_bad (_(".seh_handler requires a handler"));
> +
> + SKIP_WHITESPACE ();
> +
> + if (*input_line_pointer == '@')
> + {
> + name_end = get_symbol_name (&symbol_name);
> +
> + seh_ctx_cur->handler.X_op = O_constant;
> + seh_ctx_cur->handler.X_add_number = 0;
> +
> + if (strcasecmp (symbol_name, "@0") == 0
> + || strcasecmp (symbol_name, "@null") == 0)
> + ;
If we're not doing anything in this if body, why do we have it? Is it to
be able to write the bottom conditionals in an easier way?yhn
> + else if (strcasecmp (symbol_name, "@1") == 0)
> + seh_ctx_cur->handler.X_add_number = 1;
> + else
> + as_bad (_("unknown constant value '%s' for handler"), symbol_name);
> +
> + (void) restore_line_pointer (name_end);
> + }
> + else
> + expression (&seh_ctx_cur->handler);
> +
> + seh_ctx_cur->handler_data.X_op = O_constant;
> + seh_ctx_cur->handler_data.X_add_number = 0;
> + seh_ctx_cur->xdata_header.x = 1;
> +
> + while (skip_whitespace_and_comma (0))
> + {
> + name_end = get_symbol_name (&symbol_name);
> + (void) restore_line_pointer (name_end);
> + }
> +}
> +
> +/* Switch to subsection for handler data for exception region. */
> +static void
> +obj_coff_seh_handlerdata (const int what ATTRIBUTE_UNUSED)
> +{
> + demand_empty_rest_of_line ();
> +
> + switch_xdata (seh_ctx_cur->subsection + 1, seh_ctx_cur->code_seg);
> +}
> +
> +/* Obtain available unwind element. */
> +static void
> +seh_aarch64_add_unwind_element (const seh_aarch64_unwind_types unwind_type,
> + unsigned offset, unsigned reg)
> +{
> + gas_assert (in_seh_proc);
> +
> + const struct aarch64_unwind_code_pack_info *unwind_code_pack_info
> + = aarch64_unwind_code_pack_data + unwind_type;
> + unsigned value_offset_bits = 0;
> +
> + if ((seh_ctx_cur->unwind_codes_byte_count
> + + unwind_code_pack_info->size) > AARCH64_MAX_UNWIND_CODES_SIZE)
> + as_bad (_("no unwind element available."));
> +
> + seh_aarch64_unwind_code *aarch64_element;
> + aarch64_element = seh_ctx_cur->unwind_codes
> + + seh_ctx_cur->unwind_codes_count++;
> + aarch64_element->value = 0;
> +
> + if (unwind_code_pack_info->offset_bits)
> + {
> + offset = (offset >> unwind_code_pack_info->offset_right_shift)
> + - unwind_code_pack_info->offset;
> + offset &= (1 << unwind_code_pack_info->offset_bits) - 1;
> + aarch64_element->value |= offset << value_offset_bits;
> + value_offset_bits += unwind_code_pack_info->offset_bits;
> + }
> +
> + if (unwind_code_pack_info->reg_bits)
> + {
> + reg = (reg >> unwind_code_pack_info->reg_right_shift)
> + - unwind_code_pack_info->reg_offset;
> + reg &= (1 << unwind_code_pack_info->reg_bits) - 1;
> + aarch64_element->value |= reg << value_offset_bits;
> + value_offset_bits += unwind_code_pack_info->reg_bits;
> + }
> +
> + if (unwind_code_pack_info->code_bits)
> + {
> + unsigned code = unwind_code_pack_info->code;
> + code &= (1 << unwind_code_pack_info->code_bits) - 1;
> + aarch64_element->value |= code << value_offset_bits;
> + }
> +
> + aarch64_element->type = unwind_type;
> + seh_ctx_cur->unwind_codes_byte_count += unwind_code_pack_info->size;
> +}
> +
> +/* Mark begin of new context. */
> +static void
> +obj_coff_seh_proc (const int what ATTRIBUTE_UNUSED)
> +{
> + char *symbol_name;
> + char name_end;
> +
> + if (in_seh_proc)
> + as_bad (_("previous SEH entry not closed (missing .seh_endproc)"));
> +
> + if (*input_line_pointer == 0 || *input_line_pointer == '\n')
> + as_bad (_(".seh_proc requires function label name"));
> +
> +
> + if (!seh_ctx_root)
> + {
> + seh_ctx_root = XCNEW (seh_context);
> + seh_ctx_cur = seh_ctx_root;
> + }
> + else
> + {
> + seh_ctx_cur->next = XCNEW (seh_context);
> + seh_ctx_cur = seh_ctx_cur->next;
> + }
> +
> + seh_ctx_cur->next = NULL;
> + seh_ctx_cur->code_seg = now_seg;
> +
> + /* The current implementation always use a pair of .pdata and .xdata
> + records. */
> + const bool use_xdata = true;
> +
> + if (use_xdata)
> + {
> + x_segcur = seh_hash_find_or_make (seh_ctx_cur->code_seg, ".xdata");
> + seh_ctx_cur->subsection = x_segcur->subseg;
> + x_segcur->subseg += 2;
> +
> + /* Initialize an empty .xdata record. */
> + seh_ctx_cur->unwind_codes_count = 0;
> + seh_ctx_cur->unwind_codes_byte_count = 0;
> + seh_ctx_cur->epilogue_scopes_count = 0;
> + seh_ctx_cur->epilogue_scopes_capacity = 0;
> + seh_ctx_cur->epilogue_scopes = NULL;
> + seh_ctx_cur->xdata_header.x = 0;
> + }
> +
> + SKIP_WHITESPACE ();
> +
> + name_end = get_symbol_name (&symbol_name);
> + seh_ctx_cur->func_name = xstrdup (symbol_name);
> + (void) restore_line_pointer (name_end);
> +
> + demand_empty_rest_of_line ();
> +
> + seh_ctx_cur->start_addr = symbol_temp_new_now ();
> + in_seh_proc = true;
> +}
> +
> +/* Mark end of prologue for current context. */
> +static void
> +obj_coff_seh_endprologue (const int what ATTRIBUTE_UNUSED)
> +{
> + if (!verify_context (".seh_endprologue")
> + || !seh_validate_seg (".seh_endprologue"))
> + return;
> + demand_empty_rest_of_line ();
> +
> + if (seh_ctx_cur->endprologue_addr != NULL)
> + as_warn (_("duplicate .seh_endprologue in .seh_proc block"));
> + else
> + seh_ctx_cur->endprologue_addr = symbol_temp_new_now ();
> +
> + /* Unwind codes need to be reversed. */
> + for (unsigned i = 0, n = seh_ctx_cur->unwind_codes_count; i < n / 2; ++i)
> + {
> + seh_aarch64_unwind_code *unwind_codes = seh_ctx_cur->unwind_codes;
> + const seh_aarch64_unwind_code temp = unwind_codes[i];
> + unwind_codes[i] = unwind_codes[n-i-1];
> + unwind_codes[n-i-1] = temp;
> + }
> +
> + seh_aarch64_add_unwind_element (unwind_end, 0, 0);
> +}
> +
> +/* Mark end of current context. */
> +static void
> +obj_coff_seh_endproc (const int what ATTRIBUTE_UNUSED)
> +{
> + demand_empty_rest_of_line ();
> + if (!in_seh_proc)
> + as_bad (_(".seh_endproc used without .seh_proc"));
> +
> + seh_validate_seg (".seh_endproc");
> +
> + seh_ctx_cur->end_addr = symbol_temp_new_now ();
> + in_seh_proc = false;
> +}
> +
> +static void
> +obj_coff_seh_startepilogue (const int what ATTRIBUTE_UNUSED)
> +{
> + if (!verify_context (".seh_startepilogue")
> + || !seh_validate_seg (".seh_startepilogue"))
> + return;
> + demand_empty_rest_of_line ();
> +
> + const unsigned max_epilogue_scopes = AARCH64_MAX_EPILOGUE_SCOPES;
> + if (seh_ctx_cur->epilogue_scopes_count >= max_epilogue_scopes)
> + as_bad (_("no epilogue scopes available."));
> +
> + symbolS *epilogue_start_addr = symbol_temp_new_now ();
> + expressionS exp;
> + exp.X_op = O_subtract;
> + exp.X_add_symbol = epilogue_start_addr;
> + exp.X_op_symbol = seh_ctx_cur->start_addr;
> + exp.X_add_number = 0;
> +
> + if (!resolve_expression (&exp) || exp.X_op != O_constant
> + || exp.X_add_number < 0)
> + as_bad (_(".seh_startepilog offset expression for %s "
> + "does not evaluate to a non-negative constant"),
> + S_GET_NAME (epilogue_start_addr));
> +
> + if (seh_ctx_cur->epilogue_scopes_count
> + >= seh_ctx_cur->epilogue_scopes_capacity)
> + {
> + const unsigned initial_capacity = 32;
> + if (seh_ctx_cur->epilogue_scopes_capacity)
> + seh_ctx_cur->epilogue_scopes_capacity *= 2;
> + else
> + seh_ctx_cur->epilogue_scopes_capacity = initial_capacity;
> +
> + seh_ctx_cur->epilogue_scopes
> + = XRESIZEVEC (seh_aarch64_epilogue_scope, seh_ctx_cur->epilogue_scopes,
> + seh_ctx_cur->epilogue_scopes_capacity);
> + }
> +
> + seh_aarch64_epilogue_scope *epilogue_scope = seh_ctx_cur->epilogue_scopes
> + + seh_ctx_cur->epilogue_scopes_count;
> + epilogue_scope->epilogue_start_offset = exp.X_add_number / 4;
> + epilogue_scope->reserved = 0;
> + epilogue_scope->epilogue_start_index = seh_ctx_cur->unwind_codes_byte_count;
> + seh_ctx_cur->epilogue_scopes_count++;
> +}
> +
> +static void
> +obj_coff_seh_endepilogue (const int what ATTRIBUTE_UNUSED)
> +{
> + if (!verify_context (".seh_endepilogue")
> + || !seh_validate_seg (".seh_endepilogue"))
> + return;
> +
> + demand_empty_rest_of_line ();
> +
> + expressionS exp;
> + symbolS *epilogue_end_addr = symbol_temp_new_now ();
> + exp.X_op = O_subtract;
> + exp.X_add_symbol = epilogue_end_addr;
> + exp.X_op_symbol = seh_ctx_cur->start_addr;
> + exp.X_add_number = 0;
> +
> + if (!resolve_expression (&exp) || exp.X_op != O_constant
> + || exp.X_add_number < 0)
> + as_bad (_(".seh_endepilogue offset expression for %s "
> + "does not evaluate to a non-negative constant"),
> + S_GET_NAME (epilogue_end_addr));
> +
> + seh_aarch64_epilogue_scope *epilogue_scope = seh_ctx_cur->epilogue_scopes
> + + seh_ctx_cur->epilogue_scopes_count - 1;
> +
> + epilogue_scope->epilogue_end_offset = exp.X_add_number;
> +
> + /* End code. */
> + seh_aarch64_add_unwind_element (unwind_end, 0, 0);
> +}
> +
> +/* End-of-file hook. */
> +static void
> +free_seh_ctx (struct seh_aarch64_context *seh_ctx)
> +{
> + free (seh_ctx->func_name);
> + const seh_aarch64_func_fragment *fragment = seh_ctx->func_fragment.next;
> + while (fragment)
> + {
> + const seh_aarch64_func_fragment *next = fragment->next;
> + XDELETE (fragment);
> + fragment = next;
> + }
> + XDELETEVEC (seh_ctx->epilogue_scopes);
> + free (seh_ctx);
> +}
> +
> +static void
> +obj_coff_seh_save_reg (const int type)
> +{
> + gas_assert (type >= 0 && type <= unwind_last_type);
> +
> + const struct aarch64_unwind_code_pack_info *unwind_code_pack_info
> + = aarch64_unwind_code_pack_data + type;
> +
> + if (!unwind_code_pack_info->directive
> + || !seh_validate_seg (unwind_code_pack_info->directive))
> + return;
> +
> + SKIP_WHITESPACE ();
> +
> + char *symbol_name = NULL;
> + unsigned reg = -1;
> +
> + if (unwind_code_pack_info->reg_bits)
> + {
> + char name_end = get_symbol_name (&symbol_name);
> + reg = atoi (symbol_name + 1);
> + (void) restore_line_pointer (name_end);
> +
> + if (!skip_whitespace_and_comma (1))
> + return;
> +
> + if (reg > 30)
> + as_bad (_("register number is out of range"));
> + }
> +
> + offsetT off = -1;
> + if (unwind_code_pack_info->offset_bits)
> + {
> + off = get_absolute_expression ();
> +
> + if (off < 0)
> + as_bad (_("offset is negative"));
> + }
> +
> + demand_empty_rest_of_line ();
> +
> + seh_aarch64_add_unwind_element (type, off, reg);
> +}
> +
> +/* Add a stack-allocation token to current context. */
> +static void
> +obj_coff_seh_stackalloc (const int what ATTRIBUTE_UNUSED)
> +{
> + const offsetT off = get_absolute_expression ();
> + demand_empty_rest_of_line ();
> +
> + /* aarch64 offset should be encoded in multiples of sixteen. */
> + if ((off & 0xf) != 0)
> + as_bad (_(".seh_stackalloc offset < 16-byte stack alignment"));
> +
> + if (off < 0x200)
> + seh_aarch64_add_unwind_element (unwind_alloc_s, off, 0);
> + else if (off < 0x8000)
> + seh_aarch64_add_unwind_element (unwind_alloc_m, off, 0);
> + else if (off < 0x10000000)
> + seh_aarch64_add_unwind_element (unwind_alloc_l, off, 0);
> + else
> + as_bad (_(".seh_stackalloc offset out of range"));
> +}
> +
> +/* Data writing routines. */
> +static void
> +seh_aarch64_emit_epilog_scopes (const seh_context *seh_ctx,
> + const uint64_t fragment_offset,
> + const unsigned prolog_size,
> + const unsigned first_fragment_scope,
> + const unsigned last_fragment_scope,
> + const bool has_phantom_prolog)
> +{
> + unsigned start_index_offset = 0;
> + const seh_aarch64_epilogue_scope *scopes = seh_ctx->epilogue_scopes;
> + if (first_fragment_scope < seh_ctx->epilogue_scopes_count)
> + start_index_offset = scopes[first_fragment_scope].epilogue_start_index
> + - prolog_size;
> + if (has_phantom_prolog)
> + {
> + if (start_index_offset == 0)
> + as_bad (_("start index offset for the epilogue cannot be 0 when "
> + "phantom prolog is used"));
> + --start_index_offset;
> + }
> +
> + for (unsigned i = first_fragment_scope; i < last_fragment_scope; ++i)
> + {
> + seh_aarch64_epilogue_scope scope = seh_ctx->epilogue_scopes[i];
> + scope.epilogue_start_offset_reduced = (scope.epilogue_start_offset
> + - fragment_offset) >> 2;
> + scope.epilogue_start_index -= start_index_offset;
> + uint32_t scope_code;
> + memcpy (&scope_code, &scope, sizeof (scope_code));
> + md_number_to_chars (frag_more (4), scope_code, 4);
> + }
> +}
> +
> +static void
> +seh_aarch64_emit_unwind_codes (const seh_context *seh_ctx,
> + const unsigned prolog_size,
> + const unsigned first_epilog_index,
> + const unsigned last_epilog_index,
> + const bool has_phantom_prolog)
> +{
> + unsigned total_byte_count = 0;
> +
> + if (has_phantom_prolog)
> + {
> + ++total_byte_count;
> + md_number_to_chars (frag_more (1), AARCH64_UNOP_ENDC, 1);
> + }
> +
> + unsigned unwind_bytes_offset = 0;
> + for (unsigned i = 0; i < seh_ctx->unwind_codes_count; ++i)
> + {
> + const seh_aarch64_unwind_code *code = seh_ctx->unwind_codes
> + + i;
> + const unsigned byte_count
> + = aarch64_unwind_code_pack_data[code->type].size;
> + unwind_bytes_offset += byte_count;
> +
> + if (unwind_bytes_offset > last_epilog_index)
> + break;
> +
> + if (unwind_bytes_offset > prolog_size
> + && unwind_bytes_offset <= first_epilog_index)
> + continue;
> +
> + /* emit unwind code bytes in big endian. */
> + number_to_chars_bigendian (frag_more (byte_count), code->value,
> + byte_count);
> + total_byte_count += byte_count;
> + }
> +
> + /* handle word alignment. */
> + unsigned required_padding = (4 - total_byte_count % 4) % 4;
> + if (required_padding)
> + {
> + /* Use AARCH64_UNOP_NOP for alignment. */
> + const uint32_t nop_chain = (AARCH64_UNOP_NOP << 24)
> + | (AARCH64_UNOP_NOP << 16)
> + | (AARCH64_UNOP_NOP << 8)
> + | AARCH64_UNOP_NOP;
> +
> + md_number_to_chars (frag_more (required_padding), nop_chain,
> + required_padding);
> + }
> +}
> +
> +static bool
> +seh_function_size (const struct seh_aarch64_context *seh_ctx,
> + uintptr_t *size)
> +{
> + fragS *start_frag, *end_frag;
> + addressT start_offset, end_offset;
> + start_frag = symbol_get_frag_and_value (seh_ctx->start_addr, &start_offset);
> + end_frag = symbol_get_frag_and_value (seh_ctx->end_addr, &end_offset);
> +
> + intptr_t func_size = end_frag->fr_address + end_offset
> + - start_frag->fr_address - start_offset;
> + if (func_size < 0)
> + return false;
> +
> + *size = func_size;
> + return true;
> +}
> +
> +/* Write out the xdata information for one function. */
> +static void
> +seh_aarch64_write_function_xdata (struct seh_aarch64_context *seh_ctx)
> +{
Is there a way to break down this function? Perhaps around your
conditionals inside the while (true) loop?
> + if (!seh_ctx->unwind_codes_byte_count)
> + return;
> +
> + const segT save_seg = now_seg;
> + const subsegT save_subseg = now_subseg;
> +
> + switch_xdata (seh_ctx->subsection, seh_ctx->code_seg);
> +
> + /* Set 4-byte alignment. */
> + frag_align (2, 0, 0);
> +
> + uintptr_t func_size = 0;
> + if (!seh_function_size (seh_ctx, &func_size))
> + {
> + as_bad (_("the function size for %s has not been evaluated"),
> + seh_ctx->func_name);
> + return;
> + }
> +
> + /* The large functions should be split into fragments smaller than 1MB with
> + 4 bytes alignment.
> + https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#large-functions. */
> + const unsigned max_frag_size = (1 << 20) - 4;
> + const bool is_fragmented_function = func_size > max_frag_size;
> +
> + /* [first_fragment_scope, last_fragment_scope). */
> + unsigned prolog_insn_count = 0;
> + for (unsigned i = 0; i < seh_ctx->unwind_codes_count; ++i)
> + {
> + if (seh_ctx->unwind_codes[i].type == unwind_end)
> + {
> + prolog_insn_count = i + 1;
> + break;
> + }
> + }
> +
> + unsigned prolog_size;
> + if (seh_ctx->epilogue_scopes_count)
> + prolog_size = seh_ctx->epilogue_scopes[0].epilogue_start_index;
> + else
> + prolog_size = seh_ctx->unwind_codes_byte_count;
> +
> + seh_aarch64_func_fragment *fragment;
> + fragment = &seh_ctx->func_fragment;
> + uintptr_t fragment_offset = 0;
> + unsigned first_fragment_scope = 0;
> + unsigned last_fragment_scope = 0;
> + while (true)
> + {
> + fragment->xdata_addr = symbol_temp_new_now ();
> + fragment->offset = fragment_offset;
> + fragment->next = NULL;
> +
> + uintptr_t frag_size = func_size - fragment_offset;
> + if (frag_size > max_frag_size)
> + frag_size = max_frag_size;
> +
> + const bool is_first_frag = fragment_offset == 0;
> + const bool is_last_frag = (fragment_offset + frag_size) == func_size;
> +
> + if (!is_fragmented_function)
> + last_fragment_scope = seh_ctx->epilogue_scopes_count;
> + else
> + {
> + first_fragment_scope = last_fragment_scope;
> + for (unsigned i = first_fragment_scope;
> + i < seh_ctx->epilogue_scopes_count; ++i)
> + {
> + const seh_aarch64_epilogue_scope *scope
> + = seh_ctx->epilogue_scopes;
> + scope += i;
> + if (scope->epilogue_start_offset >= (fragment_offset + frag_size))
> + break;
> +
> + if (scope->epilogue_end_offset >= (fragment_offset + frag_size))
> + {
> + frag_size = scope->epilogue_start_offset - fragment_offset;
> + break;
> + }
> +
> + if (scope->epilogue_start_offset >= fragment_offset)
> + last_fragment_scope = i + 1;
> + }
> + }
> +
> + seh_aarch64_xdata_header *header = &seh_ctx->xdata_header;
> + const seh_aarch64_epilogue_scope *scopes = seh_ctx->epilogue_scopes;
> +
> + const uint32_t func_length_encoded = frag_size >> 2;
> + header->func_length = func_length_encoded;
> + header->vers = 0;
> + header->e = 0;
> + header->code_words = 0;
> + header->epilogue_count = 0;
> +
> + header->ext_code_words = 0;
> + header->ext_epilogue_count = last_fragment_scope
> + - first_fragment_scope;
> + header->reserved = 0;
> +
> + unsigned first_epilog_index = 0;
> + unsigned last_epilog_index = 0;
> + if (!header->ext_epilogue_count)
> + {
> + first_epilog_index = prolog_size;
> + last_epilog_index = prolog_size;
> + }
> + else
> + {
> + const seh_aarch64_epilogue_scope *scope;
> + scope = scopes + first_fragment_scope;
> + first_epilog_index = scope->epilogue_start_index;
> + if (last_fragment_scope == seh_ctx->epilogue_scopes_count)
> + last_epilog_index = seh_ctx->unwind_codes_byte_count;
> + else
> + {
> + scope = scopes + last_fragment_scope;
> + last_epilog_index = scope->epilogue_start_index;
> + }
> + }
> +
> + unsigned unwind_bytes = 0;
> + if (is_first_frag || is_last_frag)
> + unwind_bytes += prolog_size;
> +
> + if (header->ext_epilogue_count)
> + unwind_bytes += last_epilog_index - first_epilog_index;
> +
> + const bool has_phantom_prolog = is_fragmented_function && is_last_frag;
> + if (has_phantom_prolog && unwind_bytes)
> + {
> + /* One more epilogue scope and unwind code are emitted with phantom
> + prolog. */
> + unwind_bytes += 1;
> + ++header->ext_epilogue_count;
> + }
> +
> + /* Calculate the number of code words with 4-byte alignment. */
> + header->ext_code_words = (unwind_bytes + 3) / 4;
> +
> + if ((header->ext_code_words == 0 && header->ext_epilogue_count == 0)
> + || header->ext_code_words > 31
> + || header->ext_epilogue_count > 31)
> + md_number_to_chars (frag_more (8),
> + seh_ctx->xdata_header_value, 8);
> + else
> + {
> + header->code_words = header->ext_code_words;
> + header->epilogue_count = header->ext_epilogue_count;
> + if (header->epilogue_count == 1)
> + {
> + header->e = 1;
> + if (has_phantom_prolog)
> + header->ext_epilogue_count = 0;
> + else
> + {
> + const seh_aarch64_epilogue_scope *scope;
> + scope = scopes + first_fragment_scope;
> + header->ext_epilogue_count = scope->epilogue_start_index;
> + }
> + }
> + md_number_to_chars (frag_more (4),
> + seh_ctx->xdata_header_value, 4);
> + }
> +
> + if (header->ext_epilogue_count && !header->e)
> + {
> + seh_aarch64_emit_epilog_scopes (seh_ctx,
> + fragment_offset, prolog_size,
> + first_fragment_scope,
> + last_fragment_scope,
> + has_phantom_prolog);
> + if (has_phantom_prolog)
> + {
> + const uint32_t epilog_start_index_encoded = 1 << 22;
> + const uint32_t epilog_start_offset_encoded
> + = (frag_size - prolog_insn_count * 4) >> 2;
> + md_number_to_chars (frag_more (4),
> + epilog_start_index_encoded
> + | epilog_start_offset_encoded, 4);
> + }
> + }
> +
> + if (header->ext_code_words)
> + seh_aarch64_emit_unwind_codes (seh_ctx, prolog_size, first_epilog_index,
> + last_epilog_index, has_phantom_prolog);
> +
> + if (header->x == 1)
> + {
> + if (seh_ctx->handler.X_op == O_symbol)
> + seh_ctx->handler.X_op = O_symbol_rva;
> +
> + emit_expr (&seh_ctx->handler, 4);
> + }
> +
> + fragment_offset += frag_size;
> + if (fragment_offset == func_size)
> + break;
> +
> + fragment->next = XCNEW (seh_aarch64_func_fragment);
> + fragment = fragment->next;
> + }
> +
> + subseg_set (save_seg, save_subseg);
> +}
> +
> +/* Write out pdata for one function. */
> +static void
> +seh_aarch64_write_function_pdata (const seh_context *seh_ctx)
> +{
> + expressionS exp;
> + const segT save_seg = now_seg;
> + const subsegT save_subseg = now_subseg;
> + memset (&exp, 0, sizeof (expressionS));
> + switch_pdata (seh_ctx->code_seg);
> +
> + if (seh_ctx->unwind_codes_byte_count)
> + {
> + const seh_aarch64_func_fragment *fragment = &seh_ctx->func_fragment;
> + while (fragment)
> + {
> + exp.X_op = O_symbol_rva;
> + exp.X_add_number = fragment->offset;
> + exp.X_add_symbol = seh_ctx->start_addr;
> + emit_expr (&exp, 4);
> +
> + exp.X_op = O_symbol_rva;
> + /* TODO: Implementing packed unwind data. */
> + exp.X_add_number = 0;
> + exp.X_add_symbol = fragment->xdata_addr;
> + emit_expr (&exp, 4);
> + fragment = fragment->next;
> + }
> + }
> +
> + subseg_set (save_seg, save_subseg);
> +}
> +
> +void
> +seh_aarch64_write_data (void)
> +{
> + if (in_seh_proc)
> + as_bad (_("open SEH entry at end of file (missing .seh_endproc)"));
> +
> + if (!seh_ctx_root)
> + return;
> +
> + struct seh_aarch64_context *seh_ctx = seh_ctx_root;
> + seh_ctx_root = NULL;
> +
> + /* Relax the segment to be able to calculate the function sizes. */
> + subsegs_finish_section (seh_ctx->code_seg);
> + const segment_info_type *seginfo = seg_info (seh_ctx->code_seg);
> + relax_segment (seginfo->frchainP->frch_root, seh_ctx->code_seg, 0);
> +
> + while (seh_ctx)
> + {
> + seh_aarch64_write_function_xdata (seh_ctx);
> + seh_aarch64_write_function_pdata (seh_ctx);
> + struct seh_aarch64_context *next = seh_ctx->next;
> + free_seh_ctx (seh_ctx);
> + seh_ctx = next;
> + }
> +}
> +
> +void
> +obj_coff_seh_do_final (void)
> +{
> +}
> diff --git a/gas/config/obj-coff-seh-aarch64.h b/gas/config/obj-coff-seh-aarch64.h
> new file mode 100644
> index 00000000000..bda7e00adc5
> --- /dev/null
> +++ b/gas/config/obj-coff-seh-aarch64.h
> @@ -0,0 +1,265 @@
> +/* SEH .pdata/.xdata COFF object file format on AArch64
> + Copyright (C) 2026 Free Software Foundation, Inc.
> +
> + This file is part of GAS.
> +
> + GAS 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.
> +
> + GAS 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 GAS; see the file COPYING. If not, write to the Free
> + Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
> + 02110-1301, USA. */
> +
> +/* SEH COFF AArch64 implementation partially intersects with the x64
> + version, however it has a different extension to the unwind codes.
> + It emits SEH data to pdata and xdata sections. In some cases SEH
> + data could be emitted to a packed record in the pdata section
> + without the need for data in the xdata section. However, the packed
> + pdata record is not implemented yet. */
> +
> +#ifndef OBJ_COFF_SEH_AARCH64_H
> +#define OBJ_COFF_SEH_AARCH64_H
> +
> +typedef enum seh_aarch64_unwind_types
> +{
Can you add a link to the documentation of the unwind types?
Thanks,
Saurabh
> + unwind_alloc_s,
> + unwind_alloc_m,
> + unwind_alloc_l,
> + unwind_save_reg,
> + unwind_save_reg_x,
> + unwind_save_regp,
> + unwind_save_regp_x,
> + unwind_save_fregp,
> + unwind_save_fregp_x,
> + unwind_save_freg,
> + unwind_save_freg_x,
> + unwind_save_lrpair,
> + unwind_save_fplr,
> + unwind_save_fplr_x,
> + unwind_save_r19r20_x,
> + unwind_add_fp,
> + unwind_set_fp,
> + unwind_save_next,
> + unwind_nop,
> + unwind_pac_sign_lr,
> + unwind_end,
> + unwind_end_c,
> + unwind_last_type = unwind_end_c
> +} seh_aarch64_unwind_types;
> +
> +#define SEH_CMDS \
> + /* Start a function that contains SEH. */ \
> + {"seh_proc", obj_coff_seh_proc, 0}, \
> + \
> + /* End a function that contains SEH. */ \
> + {"seh_endproc", obj_coff_seh_endproc, 0}, \
> + \
> + /* End a SEH prolog with unwinding codes. */ \
> + {"seh_endprologue", obj_coff_seh_endprologue, 0}, \
> + \
> + /* Allocate stack. */ \
> + {"seh_stackalloc", obj_coff_seh_stackalloc, 0}, \
> + \
> + /* Set a SEH handler. */ \
> + {"seh_handler", obj_coff_seh_handler, 0}, \
> + \
> + /* Set a SEH handler data. */ \
> + {"seh_handlerdata", obj_coff_seh_handlerdata, 0}, \
> + \
> + /* Start a SEH epilogue. */ \
> + {"seh_startepilogue", obj_coff_seh_startepilogue, 0}, \
> + \
> + /* End a SEH epilogue. */ \
> + {"seh_endepilogue", obj_coff_seh_endepilogue, 0}, \
> + \
> + /* Save an 'x' register. */ \
> + {"seh_save_reg", obj_coff_seh_save_reg, unwind_save_reg}, \
> + \
> + /* Save an 'x' register with a pre-indexed offset. */ \
> + {"seh_save_reg_x", obj_coff_seh_save_reg, unwind_save_reg_x}, \
> + \
> + /* Save an 'x' register pair. */ \
> + {"seh_save_regp", obj_coff_seh_save_reg, unwind_save_regp}, \
> + \
> + /* Save an 'x' register pair with a pre-indexed offset. */ \
> + {"seh_save_regp_x", obj_coff_seh_save_reg, unwind_save_regp_x}, \
> + \
> + /* Save an 'x' register and lr. */ \
> + {"seh_save_lrpair", obj_coff_seh_save_reg, unwind_save_lrpair}, \
> + \
> + /* Save a 'd' register pair. */ \
> + {"seh_save_fregp", obj_coff_seh_save_reg, unwind_save_fregp}, \
> + \
> + /* Save a 'd' register pair with a pre-indexed offset. */ \
> + {"seh_save_fregp_x", obj_coff_seh_save_reg, unwind_save_fregp_x}, \
> + \
> + /* Save a 'd' register. */ \
> + {"seh_save_freg", obj_coff_seh_save_reg, unwind_save_freg}, \
> + \
> + /* Save a 'd' register with a pre-indexed offset. */ \
> + {"seh_save_freg_x", obj_coff_seh_save_reg, unwind_save_freg_x}, \
> + \
> + /* Save fp and lr registers. */ \
> + {"seh_save_fplr", obj_coff_seh_save_reg, unwind_save_fplr}, \
> + \
> + /* Save fp and lr registers with a pre-indexed offset. */ \
> + {"seh_save_fplr_x", obj_coff_seh_save_reg, unwind_save_fplr_x}, \
> + \
> + /* Save x19 and x20 registers with a pre-indexed offset. */ \
> + {"seh_save_r19r20_x", obj_coff_seh_save_reg, unwind_save_r19r20_x}, \
> + \
> + /* Set fp by sp + offset. */ \
> + {"seh_add_fp", obj_coff_seh_save_reg, unwind_add_fp}, \
> + \
> + /* Unwind operation is not required. */ \
> + {"seh_nop", obj_coff_seh_save_reg, unwind_nop}, \
> + \
> + /* Sign the return address in lr with pacibsp. */ \
> + {"seh_pac_sign_lr", obj_coff_seh_save_reg, unwind_pac_sign_lr}, \
> + \
> + /* Set fp by sp. */ \
> + {"seh_set_fp", obj_coff_seh_save_reg, unwind_set_fp}, \
> + \
> + /* Save next register pair. */ \
> + {"seh_save_next", obj_coff_seh_save_reg, unwind_save_next},
> +
> +/* AArch64 exceptions handling and unwinding structures.
> + https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling#pdata-records. */
> +
> +typedef struct seh_aarch64_unwind_code
> +{
> + unsigned value;
> + seh_aarch64_unwind_types type;
> +} seh_aarch64_unwind_code;
> +
> +typedef struct seh_aarch64_packed_unwind_data
> +{
> + uint32_t flag : 2;
> + uint32_t func_length : 11;
> + uint32_t frame_size : 9;
> + uint32_t cr : 2;
> + uint32_t h : 1;
> + uint32_t regI : 4;
> + uint32_t regF : 3;
> +} seh_aarch64_packed_unwind_data;
> +
> +typedef struct seh_aarch64_except_info
> +{
> + uint32_t flag : 2;
> + uint32_t except_info_rva : 30;
> +} seh_aarch64_except_info;
> +
> +typedef union seh_aarch64_unwind_info
> +{
> + seh_aarch64_except_info except_info;
> + seh_aarch64_packed_unwind_data packed_unwind_data;
> +} seh_aarch64_unwind_info;
> +
> +typedef struct seh_aarch64_xdata_header
> +{
> + uint32_t func_length : 18;
> + uint32_t vers : 2;
> + uint32_t x : 1;
> + uint32_t e : 1;
> + uint32_t epilogue_count : 5;
> + uint32_t code_words : 5;
> + uint32_t ext_epilogue_count : 16;
> + uint32_t ext_code_words : 8;
> + uint32_t reserved : 8;
> +} seh_aarch64_xdata_header;
> +
> +typedef struct seh_aarch64_epilogue_scope
> +{
> + uint32_t epilogue_start_offset_reduced : 18;
> + uint32_t reserved : 4;
> + uint32_t epilogue_start_index : 10;
> + bfd_vma epilogue_start_offset;
> + bfd_vma epilogue_end_offset;
> +} seh_aarch64_epilogue_scope;
> +
> +typedef struct seh_aarch64_func_fragment
> +{
> + bfd_vma offset;
> + symbolS *xdata_addr;
> + struct seh_aarch64_func_fragment *next;
> +} seh_aarch64_func_fragment;
> +
> +/* AARCH64_MAX_UNWIND_CODES is limited by
> + seh_aarch64_xdata_header::ext_code_words. */
> +#define AARCH64_MAX_UNWIND_CODES (255 * 4)
> +#define AARCH64_MAX_UNWIND_CODES_SIZE (255 * 4)
> +/* AARCH64_MAX_EPILOGUE_SCOPES is limited by
> + seh_aarch64_xdata_header::ext_epilogue_count. */
> +#define AARCH64_MAX_EPILOGUE_SCOPES 65535
> +
> +typedef struct seh_aarch64_context
> +{
> + struct seh_aarch64_context *next;
> +
> + /* Initial code-segment. */
> + segT code_seg;
> + /* Function name. */
> + char *func_name;
> + /* BeginAddress. */
> + symbolS *start_addr;
> + /* EndAddress. */
> + symbolS *end_addr;
> + /* PrologueEnd. */
> + symbolS *endprologue_addr;
> + /* ExceptionHandler. */
> + expressionS handler;
> + /* ExceptionHandlerData. */
> + expressionS handler_data;
> +
> + subsegT subsection;
> +
> + union {
> + seh_aarch64_xdata_header xdata_header;
> + valueT xdata_header_value;
> + };
> + unsigned unwind_codes_count;
> + unsigned unwind_codes_byte_count;
> + seh_aarch64_unwind_code unwind_codes[AARCH64_MAX_UNWIND_CODES];
> + unsigned epilogue_scopes_count;
> + unsigned epilogue_scopes_capacity;
> + seh_aarch64_epilogue_scope *epilogue_scopes;
> + expressionS except_handler;
> + expressionS except_handler_data;
> + /* The function fragments. */
> + seh_aarch64_func_fragment func_fragment;
> +} seh_context;
> +
> +/* aarch64 unwind code prefixes. */
> +
> +#define AARCH64_UNOP_ALLOCS 0b000U
> +#define AARCH64_UNOP_SAVER19R20X 0b001U
> +#define AARCH64_UNOP_SAVEFPLR 0b01U
> +#define AARCH64_UNOP_SAVEFPLRX 0b10U
> +#define AARCH64_UNOP_ALLOCM 0b11000U
> +#define AARCH64_UNOP_SAVEREGP 0b110010U
> +#define AARCH64_UNOP_SAVEREGPX 0b110011U
> +#define AARCH64_UNOP_SAVEREG 0b110100U
> +#define AARCH64_UNOP_SAVEREGX 0b1101010U
> +#define AARCH64_UNOP_SAVELRPAIR 0b1101011U
> +#define AARCH64_UNOP_SAVEFREGP 0b1101100U
> +#define AARCH64_UNOP_SAVEFREGPX 0b1101101U
> +#define AARCH64_UNOP_SAVEFREG 0b1101110U
> +#define AARCH64_UNOP_SAVEFREGX 0b11011110U
> +#define AARCH64_UNOP_ALLOCL 0b11100000U
> +#define AARCH64_UNOP_SETFP 0b11100001U
> +#define AARCH64_UNOP_ADDFP 0b11100010U
> +#define AARCH64_UNOP_NOP 0b11100011U
> +#define AARCH64_UNOP_END 0b11100100U
> +#define AARCH64_UNOP_ENDC 0b11100101U
> +#define AARCH64_UNOP_SAVENEXT 0b11100110U
> +#define AARCH64_UNOP_PACSIGNLR 0b11111100U
> +
> +#endif /* OBJ_COFF_SEH_AARCH64_H. */
> diff --git a/gas/config/obj-coff-seh-shared.c b/gas/config/obj-coff-seh-shared.c
> index f3afd981aca..132470c70e6 100644
> --- a/gas/config/obj-coff-seh-shared.c
> +++ b/gas/config/obj-coff-seh-shared.c
> @@ -19,8 +19,13 @@
> Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
> 02110-1301, USA. */
>
> +#if defined (COFFAARCH64)
> +#include "obj-coff-seh-aarch64.h"
> +typedef struct seh_aarch64_context seh_context_t;
> +#else
> #include "obj-coff-seh.h"
> typedef struct seh_context seh_context_t;
> +#endif
>
> /* Private segment collection list. */
> struct seh_seg_list {
> diff --git a/gas/config/obj-coff.c b/gas/config/obj-coff.c
> index 7732c0af911..b08295019d8 100644
> --- a/gas/config/obj-coff.c
> +++ b/gas/config/obj-coff.c
> @@ -55,7 +55,11 @@ static const char weak_altprefix[] = ".weak.";
> #endif /* TE_PE */
>
> #include "obj-coff-seh-shared.c"
> +#if defined (COFFAARCH64)
> +#include "obj-coff-seh-aarch64.c"
> +#else
> #include "obj-coff-seh.c"
> +#endif
>
> typedef struct
> {
> diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
> index cd76163488c..d9d3296a458 100644
> --- a/gas/config/tc-aarch64.c
> +++ b/gas/config/tc-aarch64.c
> @@ -10375,6 +10375,16 @@ aarch64_cleanup (void)
> }
> }
>
> +#if defined (OBJ_COFF)
> +/* Called after all assembly has been done. */
> +
> +void
> +aarch64_md_finish (void)
> +{
> + seh_aarch64_write_data();
> +}
> +#endif /* OBJ_COFF. */
> +
> #ifdef OBJ_ELF
> /* Remove any excess mapping symbols generated for alignment frags in
> SEC. We may have created a mapping symbol before a zero byte
> diff --git a/gas/config/tc-aarch64.h b/gas/config/tc-aarch64.h
> index d1fb4c9058b..67f476a3dcc 100644
> --- a/gas/config/tc-aarch64.h
> +++ b/gas/config/tc-aarch64.h
> @@ -82,6 +82,11 @@ struct aarch64_fix
>
> #define tc_frob_section(S) aarch64_frob_section (S)
>
> +#if defined (OBJ_COFF)
> +#define md_finish aarch64_md_finish
> +extern void aarch64_md_finish (void);
> +#endif
> +
> /* The key used to sign a function's return address. */
> enum pointer_auth_key {
> AARCH64_PAUTH_KEY_A,
> @@ -363,6 +368,7 @@ extern void aarch64_handle_align (struct frag *);
> extern int tc_aarch64_regname_to_dw2regnum (char *regname);
> extern void tc_aarch64_frame_initial_instructions (void);
> extern bool aarch64_fix_adjustable (struct fix *);
> +extern void seh_aarch64_write_data (void);
>
> #ifdef TE_PE
>
> diff --git a/gas/write.c b/gas/write.c
> index 9d0777051dd..903f858b4dc 100644
> --- a/gas/write.c
> +++ b/gas/write.c
> @@ -1831,7 +1831,7 @@ set_symtab (void)
> #endif
> #endif
>
> -static void
> +void
> subsegs_finish_section (asection *s)
> {
> struct frchain *frchainP;
> diff --git a/gas/write.h b/gas/write.h
> index 7281930c2d6..0638e5958d6 100644
> --- a/gas/write.h
> +++ b/gas/write.h
> @@ -188,5 +188,6 @@ extern fixS *fix_new_exp (fragS *, unsigned long, unsigned long,
> const expressionS *, int, bfd_reloc_code_real_type);
> extern void write_print_statistics (FILE *);
> extern void as_bad_subtract (fixS *);
> +extern void subsegs_finish_section (asection *);
>
> #endif /* __write_h__ */
More information about the Binutils
mailing list