[PATCH v9 1/1] aarch64: Implement Structured Exception Handling (SEH) on AArch64

Evgeny Karpov evgeny.karpov@arm.com
Thu Jun 25 11:38:13 GMT 2026


On Mon, 22 Jun 2026, Alice Carlotti wrote:
> > Yes, it makes sense to stop compilation when function size cannot be properly calculated.
> > It means the assembler has an internal issue and should be fixed.
> 
> I think you missed my point - I'm asking whether "the function size has not
> been evaluated" only happens when func_size < 0, or can it happen with
> func_size >= 0 as well?

Yes, something should be wrong if func_size < 0, no need to check if func_size >= 0.

> > Unwind capacity is handled in seh_aarch64_add_unwind_element,
> > and epilogue capacity is handled in obj_coff_seh_startepilogue,
> > by AARCH64_MAX_UNWIND_CODES_SIZE and AARCH64_MAX_EPILOGUE_SCOPES.
> 
> I see - we currently apply the per-fragment limits on number of unwind codes
> and epilogue scopes to the entire function, so we don't need to check those
> limits when we later split the function into fragments.  But the downside is
> that we bail out in some cases where we could have used fragments instead.
> 
> I'm ok with this limitation remaining in the current patch for now, as long as
> it is explicitly called out in both:
> - any lists of currently unsupported functionality, and
> - a comment in this function explaining why we don't currently consider those
>   limits during fragmentation (the comment above the "while (true)" would be a
>   good place to mention this).

Ok, the description with the limitations will be extended.

> > Ok, it makes sense.
> > "Reuse of identical unwind code sequences" is not applicable for 
> > aarch64-w64-mingw32.
> 
> Really?  I see no reason why it wouldn't be applicable, and the documentation
> itself implicitly mentions reusing unwind codes:
> 
>     These assumptions are made in the exception handling description:
> 
>     - Prologs and epilogs tend to mirror each other. By taking advantage of
>       this common trait, the size of the metadata needed to describe unwinding
>       can be greatly reduced. Within the body of the function, it doesn't
>       matter whether the prolog's operations are undone, or the epilog's
>       operations are done in a forward manner. Both should produce identical
>       results.
> 
> (https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#assumptions)

It can be done by supporting the packaged .pdata or reusing the same codes for
epilog scopes, however both are not supported yet.

> > The big endian host should be supported properly by md_number_to_chars that is used
> > for emitting pdata/xdata records.
> 
> That only ensures that the conversion from valueT to bytes-on-disk is handled
> correctly.  The issue is with the bit-field to valueT conversion - on a
> big-endian host I think this would pack the bitfields in the wrong order. 

The description will be extended with text that big endian host has not been validated.
It will be validated, and a separate patch will be submitted if needed.

> > +  unsigned char code;
> > +  unsigned char offset_shift;
> > +  unsigned char offset_addend;
> > +  unsigned char reg_shift;
> > +  unsigned char reg_addend;
> > +  unsigned char size;
> > +};
> Could you reorder the fields in the struct definition as well, to match the
> improved instantiation order?

It will be reordered.

> > +    as_bad (_("no unwind element available."));
> > +
> > +  unsigned value = 0;
> > +
> > +  if (info->offset_bits)
> > +    {
> > +      const unsigned divided_by = 1u << info->offset_shift;
> offset_multiplier would be a better name

It will be renamed.

> > +      if (offset & (divided_by - 1))
> > +	as_bad (_("offset should be divided by %u"), divided_by);
> "offset should be a multiple of %u"

It will be changed.
 
> > +      value |= offset << value_offset_bits;
> > +      value_offset_bits += info->offset_bits;
> > +    }
> > +
> > +  if (info->reg_bits)
> > +    {
> > +      const unsigned regn_divided_by = 1u << info->reg_shift;
> similiarly reg_multiplier

It will be renamed.
 
> > +      if (!skip_whitespace_and_comma (1))
> > +	return;
> > +
> > +      const unsigned reg_max = info->reg_max ? info->reg_max: 30;
> > +      if (reg > reg_max)
> > +	as_bad (_("unexpected register number"));
> > +    }
> This feels awkward, because the maximum isn't actually 30 in some cases.  I
> think it would be clearer to have a reg_pair field (instead of the reg_max)
> field, and replace this check with:
> 
> 
> +      /* Most range checks are applied during encoding.  Check here that we
> +	 don't reference registers higher than x30.  */
> +      if (info->reg_type == 'x'
> +	  && reg + (info->reg_pair ? 1 : 0) > 30)
> +	as_bad (_("unexpected register number"));
> 
> Although now I look at it some more, I think we should put all of the range
> checking in the same place (checking it during parsing might give better error
> locations, but I haven't verified this).

Ok, reg_pair will be used.

> > +	.seh_handler _ZN9exception6handleEPvS0_S0_S0_, @except
> > +	.seh_handlerdata
> > +	.long 1
> > +	.seh_code
> > +	.seh_startepilogue
> > +	.seh_endepilogue
> > +	.seh_endproc
> 
> Shouldn't seh directives be interleaved with the matching assembly
> instructions?  We don't check the offsets of individual seh unwind codes, but
> this test file looks like it would have a prologue that overlaps the epilogue
> and extends beyond the end of the function (which is something that we should
> probably check for).

It is not necessary now. The test will be extended with the instructions once
the packed .pdata is supported. 
Offsets for unwind codes are validated by tests.
Regarding "prologue that overlaps the epilogue", could it be clarified what
should be checked?
 
> Additionally, why is there a mismatch between .seh_set_fp near the end of the
> prologue in the .s file, and .seh_save_fp near the start of the reversed
> prologue in the .d file?

It is a typo in the comments. It will be changed to .seh_set_fp.

Regards,
Evgeny



More information about the Binutils mailing list