[PATCH v3 5/7] Write SEH records to pdata/xdata

Evgeny Karpov evgeny@kmaps.co
Thu Aug 14 08:14:22 GMT 2025


The patch includes an implementation for emitting the necessary records to the
.pdata and .xdata sections, which contain unwinding information for SEH.

gas/ChangeLog:

	* config/obj-coff-seh.c (defined): Add COFFAARCH64 guard.
	(seh_aarch64_emit_epilog_scopes): New.
	(seh_aarch64_emit_unwind_codes): New.
	(seh_aarch64_write_function_xdata): New.
---
 gas/config/obj-coff-seh.c | 305 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 305 insertions(+)

diff --git a/gas/config/obj-coff-seh.c b/gas/config/obj-coff-seh.c
index a12f5a3efb5..2c85973aaac 100644
--- a/gas/config/obj-coff-seh.c
+++ b/gas/config/obj-coff-seh.c
@@ -1256,7 +1256,86 @@ seh_x64_write_prologue_data (const seh_context *c)
 	}
     }
 }
+#endif
+
+#if defined (COFFAARCH64)
+static void
+seh_aarch64_emit_epilog_scopes (const seh_context *seh_ctx,
+				uint64_t fragment_offset,
+				uint32_t prolog_size,
+				unsigned int first_fragment_scope,
+				unsigned int last_fragment_scope,
+				bool has_phantom_prolog)
+{
+  int32_t start_index_offset = 0;
+  const
+  seh_aarch64_epilogue_scope *scopes = seh_ctx->aarch64_ctx.epilogue_scopes;
+  if (first_fragment_scope < seh_ctx->aarch64_ctx.epilogue_scopes_count)
+    start_index_offset = scopes[first_fragment_scope].epilogue_start_index
+			 - prolog_size;
+  if (has_phantom_prolog)
+    start_index_offset -= 1;
+  for (unsigned int i = first_fragment_scope; i < last_fragment_scope; ++i)
+    {
+      seh_aarch64_epilogue_scope scope;
+      scope = seh_ctx->aarch64_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));
+      out_four (scope_code);
+    }
+}
+
+static void
+seh_aarch64_emit_unwind_codes (const seh_context *seh_ctx,
+			       uint32_t prolog_size,
+			       uint32_t first_epilog_index,
+			       uint32_t last_epilog_index,
+			       bool has_phantom_prolog)
+{
+  uint32_t total_byte_count = 0;
+
+  if (has_phantom_prolog)
+    {
+      ++total_byte_count;
+      md_number_to_chars (frag_more (1), AARCH64_UNOP_ENDC, 1);
+    }

+  uint32_t unwind_bytes_offset = 0;
+  for (int i = 0; i < (int)seh_ctx->aarch64_ctx.unwind_codes_count; ++i)
+    {
+      const seh_aarch64_unwind_code *code = seh_ctx->aarch64_ctx.unwind_codes
+					    + i;
+      const int 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.  */
+    int required_padding = (4 - total_byte_count % 4) % 4;
+    if (required_padding)
+      {
+	const uint32_t nop_chain = 0xe3e3e3e3;
+	md_number_to_chars (frag_more (required_padding), nop_chain,
+			    required_padding);
+      }
+}
+#endif
+
+#if !defined (COFFAARCH64)
 static int
 seh_x64_size_prologue_data (const seh_context *c)
 {
@@ -1352,6 +1431,213 @@ seh_x64_write_function_xdata (seh_context *c)
 }
 #endif

+#if defined (COFFAARCH64)
+/* Write out the xdata information for one function (aarch64).  */
+static void
+seh_aarch64_write_function_xdata (seh_context *seh_ctx)
+{
+  if (!seh_ctx->aarch64_ctx.unwind_codes_byte_count)
+    return;
+
+  /* Set 4-byte alignment.  */
+  frag_align (2, 0, 0);
+
+  fragS *start_frag, *end_frag;
+  addressT start_value, end_value;
+  start_frag = symbol_get_frag_and_value (seh_ctx->start_addr, &start_value);
+  end_frag = symbol_get_frag_and_value (seh_ctx->end_addr, &end_value);
+  offsetT offset;
+  frag_offset_ignore_align_p (end_frag, start_frag, &offset);
+  start_value += offset / OCTETS_PER_BYTE;
+  offset = end_value - start_value;
+
+  if (offset < 0)
+    {
+      as_bad (_("the function size expression for %s "
+	      "does not evaluate to a non-negative value"),
+	      S_GET_NAME (seh_ctx->start_addr));
+      return;
+    }
+
+  uintptr_t func_size = offset;
+
+  const uint32_t max_frag_size = ((1 << 18) - 1) << 2;
+  uintptr_t fragment_offset = 0;
+  bool is_fragmented_function = func_size > max_frag_size;
+
+  /* [first_fragment_scope, last_fragment_scope).  */
+  unsigned int first_fragment_scope = 0;
+  unsigned int last_fragment_scope = 0;
+  uint32_t prolog_size = 0;
+  uint32_t prolog_insruction_count = 0;
+  for (unsigned int i = 0; i < seh_ctx->aarch64_ctx.unwind_codes_count; ++i)
+    {
+      if (seh_ctx->aarch64_ctx.unwind_codes[i].type == end)
+	{
+	  prolog_insruction_count = i + 1;
+	  break;
+	}
+    }
+
+  if (seh_ctx->aarch64_ctx.epilogue_scopes_count)
+    prolog_size = seh_ctx->aarch64_ctx.epilogue_scopes[0].epilogue_start_index;
+  else
+    prolog_size = seh_ctx->aarch64_ctx.unwind_codes_byte_count;
+
+  seh_aarch64_func_fragment *fragment;
+  fragment = &seh_ctx->aarch64_ctx.func_fragment;
+  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;
+
+      bool is_first_frag = fragment_offset == 0;
+      bool is_last_frag = (fragment_offset + frag_size) == func_size;
+
+      if (!is_fragmented_function)
+	last_fragment_scope = seh_ctx->aarch64_ctx.epilogue_scopes_count;
+      else
+	{
+	  first_fragment_scope = last_fragment_scope;
+	  for (unsigned int i = first_fragment_scope;
+	       i < seh_ctx->aarch64_ctx.epilogue_scopes_count; ++i)
+	    {
+	      const seh_aarch64_epilogue_scope *scope;
+	      scope = seh_ctx->aarch64_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->aarch64_ctx.xdata_header;
+      const
+      seh_aarch64_epilogue_scope *scopes =
seh_ctx->aarch64_ctx.epilogue_scopes;
+
+      header->func_length = frag_size >> 2;
+      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;
+
+      uint32_t first_epilog_index = 0;
+      uint32_t 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->aarch64_ctx.epilogue_scopes_count)
+	    last_epilog_index = seh_ctx->aarch64_ctx.unwind_codes_byte_count;
+	  else
+	    {
+	      scope = scopes + last_fragment_scope;
+	      last_epilog_index = scope->epilogue_start_index;
+	    }
+	}
+
+      uint32_t 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;
+
+      if (is_fragmented_function && is_last_frag && unwind_bytes)
+	{
+	  unwind_bytes += 1;
+	  ++header->ext_epilogue_count;
+	}
+
+      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->aarch64_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 (is_fragmented_function && is_last_frag)
+		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;
+		}
+	    }
+	  out_four (seh_ctx->aarch64_ctx.xdata_header_value);
+	}
+
+      bool has_phantom_prolog = is_fragmented_function && is_last_frag;
+      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 (is_fragmented_function && is_last_frag)
+	    {
+	      uint32_t epilog_start_offset;
+	      epilog_start_offset = frag_size - prolog_insruction_count * 4;
+	      md_number_to_chars (frag_more (4),
+				  (1 << 22) | (epilog_start_offset >> 2), 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;
+    }
+}
+#endif
+
 /* Write out xdata for one function.  */

 static void
@@ -1369,6 +1655,7 @@ write_function_xdata (seh_context *c)
   switch_xdata (c->subsection, c->code_seg);

 #if defined (COFFAARCH64)
+  seh_aarch64_write_function_xdata (c);
 #else
   seh_x64_write_function_xdata (c);
 #endif
@@ -1441,6 +1728,24 @@ write_function_pdata (seh_context *c)
   switch_pdata (c->code_seg);

 #if defined (COFFAARCH64)
+  if (c->aarch64_ctx.unwind_codes_byte_count)
+    {
+      seh_aarch64_func_fragment *fragment = &c->aarch64_ctx.func_fragment;
+      while (fragment)
+	{
+	  exp.X_op = O_symbol_rva;
+	  exp.X_add_number = fragment->offset;
+	  exp.X_add_symbol = c->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;
+	}
+    }
 #else
   switch (seh_get_target_kind ())
     {
-- 
2.39.5


More information about the Binutils mailing list