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

Evgeny Karpov evgeny.karpov@arm.com
Tue Jun 30 16:57:02 GMT 2026


On Fri, 26 Jun 2026, Alice Carlotti wrote:
> > Changes in v10:
> > - Rename offset_shift/reg_shift to offset_multiplier/reg_multiplier.
> I think you misinterpreted my review - I wanted you to rename the local
> variables in seh_aarch64_add_unwind_element:
> divided_by -> offset_multipler
> regn_divided_by -> reg_multiplier
> 
> The offset_shift and reg_shift field names were fine, and the new names are
> wrong (because the values are shift amounts, not multipliers).  Please change
> the field names back to offset_shift and reg_shift.

Ok, it was not clear in the previous comment. 
 
> > Yes, something should be wrong if func_size < 0, no need to check if func_size >= 0.
> 
> Can you explain why there's no need to check when func_size >= 0?  It looks to
> me like we could end up in that case without the function size being fully
> evaluated, but I don't really understand what this is checking.
 
func_size had mostly been calculated correctly in the past,
except in some cases when the alignment was not handled correctly.
In that case, func_size was calculated as a negative value.
The issue with the code alignment has been fixed, however 
that condition still checks that case.

> > 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. 
> 
> I think deliberately committing code that is broken for a big endian host, and
> then testing and fixing it later, is the wrong approach when doing it correctly
> is no more complex (and, in my opinion, might be a bit clearer).

It does not look like a common case for aarch64-w64-mingw32 target,
however it will be validated, and required changes will be done if needed.
 
> > +      reg = atoi (symbol_name + 1);
> > +      (void) restore_line_pointer (name_end);
> > +
> > +      if (!skip_whitespace_and_comma (1))
> > +	return;
> > +
> > +      /* Check that reference registers are not higher than x30.  */
> 
> Nit: s/reference/referenced/

Ok.

> > +
> > +  /* Calculate how many unwind bytes will be emitted in .xdata record.  */
> > +  unsigned unwind_bytes = prologue_size;
> > +
> > +  /* Check if current fragment has a phantom prologue.  If yes, then
> > +      the unwinding size should be adjusted.  */
> > +  const bool has_phantom_prologue = is_fragmented_function && is_last_frag;
> This condition is still wrong.
  
Thanks for noticing this. There was an issue with phantom encoding. 

> > +      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_prologue)
> > +	    header->ext_epilogue_count = 0;
> This looks wrong - surely the start index is 1 in this case (with unwind code
> index 0 being end_c, to indicate the phantom prologue).

That was fixed for the phantom prolog. The start index was correct, however 
it should be encoded in epilogue_count.
header->epilogue_count = 0;
 
> > +	      header->ext_epilogue_count = scope->epilogue_start_index;
> > +	    }
> > +	}
> > +    }
> > +
> > +  md_number_to_chars (frag_more (header_size), seh_ctx->xdata_header_value,
> > +		      header_size);
> 
> I think we should just build up the headers manually - this avoids endianess or
> other platform-specific issues, and simplifies the code a bit.  So something
> like:
> 
> +  unsigned code_words = ((unwind_bytes + 3) >> 2);
> +
> +  /* Compute and emit the xdata record header.  */
> +  uint32_t header = frag_size >> 2;
> +  if (seh_ctx->has_exception_data)
> +    header |= 1 << 20;
> +  if (epilogue_count <= 31 && code_words <= 31)
> +    {
> +      header |= epilogue_count << 22;
> +      header |= code_words << 27;
> +      md_number_to_chars (frag_more (4), header, 4));
> +    }
> +  else
> +    {
> +      md_number_to_chars (frag_more (4), header, 4));
> +      uint32_t header2 = epilogue_count | (code_words << 16)
> +      md_number_to_chars (frag_more (4), header2, 4));
> +    }
> 
> This doesn't use the E=1 format (which only saves 4 bytes) - if we wanted to
> include that then we'd need to add an initial check for:
> +  if (epilogue_count == 1 && code_words <= 31
> +      && /* Check epilogue start index <= 31.  */
> +      && /* Check epilogue is at the end of the function - this doesn't seem
> to be documented, but it's the only sensible way I can see to specify the
> offset in this case.  */)
> 
> However, I can't see clear documentation for how this works, so it might be
> better to ignore the E=1 format for now, and seek clarification before
> implementing it.

epilogue_count <= 31 && code_words <=31
This condition does not cover a case when epilogue_count == 0 && code_words == 0.
It should use the extended header in that case.
It looks like if E=1 and both code words and epilogue count are 0, the extended 
header should be used anyway.
 
> > +
> > +      /* Calculate current fragment size.  */
> > +      uintptr_t frag_size = func_size - fragment_offset;
> > +      if (frag_size > max_frag_size)
> > +	frag_size = max_frag_size;
> > +
> > +      const bool is_last_frag = (fragment_offset + frag_size) == func_size;
> This value shouldn't be needed in the function call, so we can just inline this
> condition into the loop termination check.

The condition will be inlined.

> > +
> > +  subsegT subsection;
> > +
> > +  union {
> > +    seh_aarch64_xdata_header xdata_header;
> > +    valueT xdata_header_value;
> > +  };
> The only header field that is now used outside of seh_aarch64_emit_xdata_record
> is xdata_header->x.  Please replace this union with a single boolean value
> seh_ctx_cur->has_exception_data (or some similar name).  We can keep details
> of the header layout contained to the single function that writes those other
> fields.
 
Ok, it will be changed.

Regards,
Evgeny



More information about the Binutils mailing list