[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