[PATCH v4 3/7] Adjust x64 SEH implementation for AArch64
Jan Beulich
jbeulich@suse.com
Fri Oct 10 13:55:39 GMT 2025
More information about the Binutils mailing list
Fri Oct 10 13:55:39 GMT 2025
- Previous message (by thread): [PATCH v4 2/7] Define unwinding and SEH data structures for aarch64
- Next message (by thread): [PATCH v4 4/7] Add aarch64-specific SEH commands
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
On 15.08.2025 00:47, Evgeny Karpov wrote:
> This patch defines the required unwind codes for AArch64 and updates handlers
> for SEH commands that are used by both x64 and AArch64.
> It implements encoding for unwind elements that will be emitted
> to pdata/xdata sections.
> The x64 SEH implementation emits .pdata/.xdata records at every seh_endproc.
> This has been adjusted to keep the x64 SEH implementation unchanged and to
> allow the aarch64 SEH to emit .pdata/.xdata records at the end of the assembly.
Which needs to be this way because of ...?
> gas/ChangeLog:
>
> * config/obj-coff-seh.c (struct aarch64_unwind_code_pack_info): New.
> (defined): Add aarch64_unwind_code_pack_data.
> (obj_coff_seh_code): Add COFFAARCH64 guard.
> (seh_get_target_kind): Update.
> (verify_context): Update.
> (verify_context_and_target): Update.
> (obj_coff_seh_eh): Add COFFAARCH64 guard.
> (seh_aarch64_add_unwind_element): New.
> (do_seh_endproc): Remove.
Resulting in more open-coding / duplicate code at the two prior call sites.
Tolerable, but not very nice.
> --- a/gas/config/obj-coff-seh.c
> +++ b/gas/config/obj-coff-seh.c
> @@ -28,14 +28,86 @@ struct seh_seg_list {
> char *seg_name;
> };
>
> +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 type;
> + unsigned size;
> +};
> +
> /* Local data. */
> +#if defined (COFFAARCH64)
> +static seh_context *seh_ctx_root = NULL;
> +#endif
> static seh_context *seh_ctx_cur = NULL;
> +static bool in_seh_proc = false;
>
> static htab_t seh_hash;
>
> static struct seh_seg_list *x_segcur = NULL;
> static struct seh_seg_list *p_segcur = NULL;
>
> +#if defined (COFFAARCH64)
> +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, offset_bits, reg_bits, code_bits, code, offset_right_shift,
> + offset, reg_right_shift, reg_offset, type, size. */
> + {NULL, 5, 0, 3, AARCH64_UNOP_ALLOCS, 4, 0, 0,
> + 0, alloc_s, 1},
Now that we've been using C99 for a while, can't such initializers become
dedicated ones? That'll be bigger code size, yes, but far easier to follow
(and, if need be, edit).
> @@ -227,18 +301,24 @@ seh_get_target_kind (void)
> }
> return seh_kind_unknown;
> }
> +#endif
>
> /* Verify that seh directives are supported. */
>
> static bool
> verify_target (const char *directive)
> {
> +#if defined (COFFAARCH64)
> + (void) directive; /* Unused. */
Please use ATTRIBUTE_UNUSED instead.
> @@ -247,7 +327,7 @@ verify_target (const char *directive)
> static int
> verify_context (const char *directive)
> {
> - if (seh_ctx_cur == NULL)
> + if (!in_seh_proc)
> {
> as_bad (_("%s used outside of .seh_proc block"), directive);
> ignore_rest_of_line ();
I fear I don't understand why this change would be needed. (Can it
be broken out as a separate change, with a proper description?)
> @@ -366,10 +450,21 @@ obj_coff_seh_handler (int what ATTRIBUTE_UNUSED)
> seh_ctx_cur->handler_data.X_add_number = 0;
> seh_ctx_cur->handler_flags = 0;
>
> +#if defined (COFFAARCH64)
> + seh_ctx_cur->aarch64_ctx.xdata_header.x = 1;
Why unconditionally 1?
> +#endif
> +
> if (!skip_whitespace_and_comma (0))
> return;
>
> - if (seh_get_target_kind () == seh_kind_x64)
> + bool seh_handler_supported;
> +#if defined (COFFAARCH64)
> + seh_handler_supported = true;
> +#else
> + seh_handler_supported = seh_get_target_kind () == seh_kind_x64;
> +#endif
> +
> + if (seh_handler_supported)
> {
Easier (less churn and less code volume) as
#if !defined (COFFAARCH64)
if (seh_handler_supported)
#endif
{
(Such an #if may want to have a brief comment attached, though.)
> @@ -401,26 +496,97 @@ obj_coff_seh_handler (int what ATTRIBUTE_UNUSED)
> static void
> obj_coff_seh_handlerdata (int what ATTRIBUTE_UNUSED)
> {
> +#if !defined (COFFAARCH64)
> if (!verify_context_and_target (".seh_handlerdata", seh_kind_x64))
> return;
> +#endif
> demand_empty_rest_of_line ();
>
> switch_xdata (seh_ctx_cur->subsection + 1, seh_ctx_cur->code_seg);
> }
>
> +#if defined (COFFAARCH64)
> +/* Obtain available unwind element. */
> +
> +static void
> +seh_aarch64_add_unwind_element (seh_aarch64_unwind_types unwind_type,
> + int offset, int reg)
> +{
> + gas_assert (in_seh_proc);
> +
> + seh_aarch64_context* aarch64_ctx = &seh_ctx_cur->aarch64_ctx;
Nit: Misplaced *.
> + seh_aarch64_unwind_code *aarch64_element;
> + aarch64_element = aarch64_ctx->unwind_codes
> + + aarch64_ctx->unwind_codes_count++;
Is it wise to update the count before any error checking?
> + const struct aarch64_unwind_code_pack_info *unwind_code_pack_info;
> + unwind_code_pack_info = aarch64_unwind_code_pack_data + unwind_type;
> + aarch64_element->value = 0;
> + int value_offset_bits = 0;
> +
> + const unsigned max_unwind_size = AARCH64_MAX_UNWIND_CODES_SIZE;
> + if ((aarch64_ctx->unwind_codes_byte_count
> + + unwind_code_pack_info->size) > max_unwind_size)
> + {
> + as_bad (_ ("no unwind element available."));
Nit: No full stop on diagnostics please.
> + return;
> + }
> +
> + 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;
Stray / odd "<<" - a problem with your email UI?
> + 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)
> + {
> + int code = unwind_code_pack_info->code;
Likely unsigned int, seeing ...
> + code &= (1 << unwind_code_pack_info->code_bits) - 1;
> + aarch64_element->value |= code << value_offset_bits;
... it's used?
> @@ -429,13 +595,21 @@ obj_coff_seh_endproc (int what ATTRIBUTE_UNUSED)
> if (!verify_target (".seh_endproc"))
> return;
> demand_empty_rest_of_line ();
> - if (seh_ctx_cur == NULL)
> + if (!in_seh_proc)
> {
> as_bad (_(".seh_endproc used without .seh_proc"));
> return;
> }
> seh_validate_seg (".seh_endproc");
> - do_seh_endproc ();
> +
> + seh_ctx_cur->end_addr = symbol_temp_new_now ();
> +#if !defined (COFFAARCH64)
> + emit_pdata_xdata_records (seh_ctx_cur);
> + free_seh_ctx (seh_ctx_cur);
> + seh_ctx_cur = NULL;
Related to an earlier remark: If this was moved ...
> +#endif
.. below here, wouldn't ...
> +
> + in_seh_proc = false;
... this new variable become unnecessary?
> @@ -461,15 +640,45 @@ obj_coff_seh_proc (int what ATTRIBUTE_UNUSED)
> return;
> }
>
> +
Nit: Please don't add double blank lines.
> +#if defined (COFFAARCH64)
> + 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;
> +#else
> seh_ctx_cur = XCNEW (seh_context);
> +#endif
>
> seh_ctx_cur->code_seg = now_seg;
>
> - if (seh_get_target_kind () == seh_kind_x64)
> + bool use_xdata;
> +#if defined (COFFAARCH64)
> + use_xdata = true;
> +#else
> + use_xdata = seh_get_target_kind () == seh_kind_x64;
> +#endif
> +
> + if (use_xdata)
See an earlier remark on how to simplify this.
> {
> x_segcur = seh_hash_find_or_make (seh_ctx_cur->code_seg, ".xdata");
> seh_ctx_cur->subsection = x_segcur->subseg;
> x_segcur->subseg += 2;
> +
> +#if defined (COFFAARCH64)
> + seh_ctx_cur->aarch64_ctx.unwind_codes_count = 0;
> + seh_ctx_cur->aarch64_ctx.epilogue_scopes_count = 0;
> + seh_ctx_cur->aarch64_ctx.epilogue_scopes_capacity = 0;
> + seh_ctx_cur->aarch64_ctx.epilogue_scopes = NULL;
> +#endif
This also could do with a brief comment.
> @@ -481,6 +690,7 @@ obj_coff_seh_proc (int what ATTRIBUTE_UNUSED)
> 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. */
> @@ -498,6 +708,22 @@ obj_coff_seh_endprologue (int what ATTRIBUTE_UNUSED)
> as_warn (_("duplicate .seh_endprologue in .seh_proc block"));
> else
> seh_ctx_cur->endprologue_addr = symbol_temp_new_now ();
> +
> +#if defined (COFFAARCH64)
> + const int n = seh_ctx_cur->aarch64_ctx.unwind_codes_count;
I question the use of plain int in cases like this and ...
> + /* Unwind codes need to be reversed. */
> + for (int i = 0; i < n / 2; ++i)
... this one. Apart from this - n isn't ...
> + {
> + seh_aarch64_unwind_code *unwind_codes;
> + unwind_codes = seh_ctx_cur->aarch64_ctx.unwind_codes;
> + 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 (end, 0, 0);
> +#endif
> }
... needed outside the loop either, so why is it not restricted to the
loop just like i is?
> @@ -505,10 +731,27 @@ obj_coff_seh_endprologue (int what ATTRIBUTE_UNUSED)
> void
> obj_coff_seh_do_final (void)
> {
> - if (seh_ctx_cur != NULL)
> + if (in_seh_proc)
> as_bad (_("open SEH entry at end of file (missing .seh_endproc)"));
> +
> +#if defined (COFFAARCH64)
> +
> + if (!seh_ctx_root)
> + return;
> +
> + struct seh_context *seh_ctx = seh_ctx_root;
> + while (seh_ctx)
> + {
> + emit_pdata_xdata_records (seh_ctx);
> + struct seh_context *next = seh_ctx->next;
> + free_seh_ctx (seh_ctx);
> + seh_ctx = next;
> + }
> + seh_ctx_root = NULL;
Move this up to prevent the value becoming a dangling pointer during the 1st
loop iteration?
> @@ -938,17 +1209,23 @@ write_function_xdata (seh_context *c)
> segT save_seg = now_seg;
> int save_subseg = now_subseg;
>
> +#if !defined (COFFAARCH64)
You use this here and elsewhere; why ...
> /* MIPS, SH, ARM don't have xdata. */
> if (seh_get_target_kind () != seh_kind_x64)
> return;
> +#endif
>
> switch_xdata (c->subsection, c->code_seg);
>
> +#if defined (COFFAARCH64)
> +#else
... not also here?
> @@ -1011,6 +1289,8 @@ write_function_pdata (seh_context *c)
> memset (&exp, 0, sizeof (expressionS));
> switch_pdata (c->code_seg);
>
> +#if defined (COFFAARCH64)
> +#else
Same question here.
> --- a/gas/config/obj-coff-seh.h
> +++ b/gas/config/obj-coff-seh.h
> @@ -112,6 +112,17 @@ typedef enum seh_aarch64_unwind_types
> {"seh_code", obj_coff_seh_code, 0}, \
> {"seh_handlerdata", obj_coff_seh_handlerdata, 0},
>
> +#if defined (COFFAARCH64)
> +#undef SEH_CMDS
Better avoid the need to use #undef by using #if / #else?
> +#define SEH_CMDS \
> + {"seh_proc", obj_coff_seh_proc, 0}, \
> + {"seh_endproc", obj_coff_seh_endproc, 0}, \
> + {"seh_endprologue", obj_coff_seh_endprologue, 0}, \
> + {"seh_stackalloc", obj_coff_seh_stackalloc, 0}, \
> + {"seh_handler", obj_coff_seh_handler, 0}, \
> + {"seh_handlerdata", obj_coff_seh_handlerdata, 0},
> +#endif
While I realize pre-existing .seh_* directives lack documentation, I
think we better wouldn't continue this with this new addition. (This isn't
a request to document what was there before, just one to document what is
being added.)
> @@ -271,18 +282,20 @@ typedef enum seh_kind {
>
> /* Forward declarations. */
> static void obj_coff_seh_stackalloc (int);
> -static void obj_coff_seh_setframe (int);
> static void obj_coff_seh_endprologue (int);
> +static void obj_coff_seh_endproc (int);
> +static void obj_coff_seh_proc (int);
> +static void obj_coff_seh_handler (int);
> +static void obj_coff_seh_handlerdata (int);
> +#if !defined (COFFAARCH64)
> +static void obj_coff_seh_setframe (int);
> static void obj_coff_seh_save (int);
> static void obj_coff_seh_pushreg (int);
> static void obj_coff_seh_pushframe (int);
> -static void obj_coff_seh_endproc (int);
> static void obj_coff_seh_eh (int);
> static void obj_coff_seh_32 (int);
> -static void obj_coff_seh_proc (int);
> -static void obj_coff_seh_handler (int);
> -static void obj_coff_seh_handlerdata (int);
> static void obj_coff_seh_code (int);
> +#endif
Can't all of these be deleted (in a separate, pre-req patch)? obj-coff.c
includes obj-coff-seh.c well ahead of expanding SEH_CMDS.
Jan
- Previous message (by thread): [PATCH v4 2/7] Define unwinding and SEH data structures for aarch64
- Next message (by thread): [PATCH v4 4/7] Add aarch64-specific SEH commands
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the Binutils mailing list