[PATCH] gas: Add AArch64 Windows SEH (pdata/xdata) support
trcrsired <[email protected]> Wed, 5 Aug 2026 02:02:13 +0800
| Newsgroups | gmane.comp.gnu.binutils |
|---|---|
| Message-ID | <[email protected]> |
Implement Windows exception-handling (SEH) unwind info generation for
the aarch64-w64-mingw32 target in GAS.
The existing obj-coff-seh.c only handled x64. This adds full AArch64
support:
* .seh_proc/.seh_endprologue and all AArch64 unwind directives
(.seh_alloc_stack, .seh_save_fplr/x, .seh_save_reg/p/p_x,
.seh_save_freg/p/p_x, .seh_save_lrpair, .seh_set_fp, .seh_add_fp,
.seh_handler, etc.)
* Correct xdata header layout (FunctionLength / CodeWords / EpilogCount /
E / X bits) matching the MS ARM64 format
* Emit the xdata extension word when CodeWords==0 and EpilogCount==0 so
Windows exception parsers do not misread the next entry
* Correct opcode encodings for register save pairs (save_regp etc.)
* Function length computed from fragment positions instead of
resolve_expression, which failed across fragments
---
gas/config/obj-coff-seh.c | 1042 ++++++++++++++++++++++++++++++-------
gas/config/obj-coff-seh.h | 80 ++-
libiberty/cp-demangle.c | 276 +++++-----
3 files changed, 1083 insertions(+), 315 deletions(-)
diff --git a/gas/config/obj-coff-seh.c b/gas/config/obj-coff-seh.c
index 1ac64f8f9ba..b7a63acc2bb 100644
--- a/gas/config/obj-coff-seh.c
+++ b/gas/config/obj-coff-seh.c
@@ -43,6 +43,8 @@ seh_get_target_kind (void)
switch (bfd_get_arch (stdoutput))
{
case bfd_arch_aarch64:
+ return seh_kind_aarch64;
+
case bfd_arch_arm:
case bfd_arch_powerpc:
case bfd_arch_sh:
@@ -176,7 +178,8 @@ obj_coff_seh_handler (int what ATTRIBUTE_UNUSED)
if (!skip_whitespace_and_comma (0))
return;
- if (seh_get_target_kind () == seh_kind_x64)
+ if (seh_get_target_kind () == seh_kind_x64
+ || seh_get_target_kind () == seh_kind_aarch64)
{
do
{
@@ -208,7 +211,7 @@ obj_coff_seh_handler (int what ATTRIBUTE_UNUSED)
static void
obj_coff_seh_handlerdata (int what ATTRIBUTE_UNUSED)
{
- if (!verify_context_and_target (".seh_handlerdata", seh_kind_x64))
+ if (!verify_context_and_target (".seh_handlerdata", seh_get_target_kind ()))
return;
demand_empty_rest_of_line ();
@@ -217,6 +220,186 @@ obj_coff_seh_handlerdata (int what ATTRIBUTE_UNUSED)
/* Mark end of current context. */
+static void
+out_one (int byte)
+{
+ char *p = frag_more (1);
+ md_number_to_chars (p, byte, 1);
+}
+
+static void
+out_two (int data)
+{
+ char *p = frag_more (2);
+ md_number_to_chars (p, data, 2);
+}
+
+static void
+out_four (int data)
+{
+ char *p = frag_more (4);
+ md_number_to_chars (p, data, 4);
+}
+
+/* Count the number of slots (shorts) in the x64 unwind codes array. */
+
+static int
+seh_x64_size_prologue_data (const seh_context *c)
+{
+ int i, ret = 0;
+
+ for (i = c->elems_count - 1; i >= 0; --i)
+ switch (c->elems[i].code)
+ {
+ case UWOP_PUSH_NONVOL:
+ case UWOP_ALLOC_SMALL:
+ case UWOP_SET_FPREG:
+ case UWOP_PUSH_MACHFRAME:
+ ret += 1;
+ break;
+
+ case UWOP_SAVE_NONVOL:
+ case UWOP_SAVE_XMM128:
+ ret += 2;
+ break;
+
+ case UWOP_SAVE_NONVOL_FAR:
+ case UWOP_SAVE_XMM128_FAR:
+ ret += 3;
+ break;
+
+ case UWOP_ALLOC_LARGE:
+ ret += (c->elems[i].info ? 3 : 2);
+ break;
+
+ default:
+ abort ();
+ }
+
+ return ret;
+}
+
+/* Write out the x64 unwind codes array. */
+
+static void
+seh_x64_write_prologue_data (const seh_context *c)
+{
+ int i;
+
+ /* We have to store in reverse order. */
+ for (i = c->elems_count - 1; i >= 0; --i)
+ {
+ const seh_prologue_element *e = c->elems + i;
+ expressionS exp;
+
+ /* First comes byte offset in code. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = e->pc_addr;
+ exp.X_op_symbol = c->start_addr;
+ exp.X_add_number = 0;
+ emit_expr (&exp, 1);
+
+ /* Second comes code+info packed into a byte. */
+ out_one ((e->info << 4) | e->code);
+
+ switch (e->code)
+ {
+ case UWOP_PUSH_NONVOL:
+ case UWOP_ALLOC_SMALL:
+ case UWOP_SET_FPREG:
+ case UWOP_PUSH_MACHFRAME:
+ /* These have no extra data. */
+ break;
+
+ case UWOP_ALLOC_LARGE:
+ if (e->info)
+ {
+ case UWOP_SAVE_NONVOL_FAR:
+ case UWOP_SAVE_XMM128_FAR:
+ /* An unscaled 4 byte offset. */
+ out_four (e->off);
+ break;
+ }
+ /* FALLTHRU */
+
+ case UWOP_SAVE_NONVOL:
+ case UWOP_SAVE_XMM128:
+ /* A scaled 2 byte offset. */
+ out_two (e->off);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+}
+
+/* Write out the xdata information for one function (x64). */
+
+static void
+seh_x64_write_function_xdata (seh_context *c)
+{
+ int flags, count_unwind_codes;
+ expressionS exp;
+
+ /* Set 4-byte alignment. */
+ frag_align (2, 0, 0);
+
+ c->xdata_addr = symbol_temp_new_now ();
+ flags = c->handler_flags;
+ count_unwind_codes = seh_x64_size_prologue_data (c);
+
+ /* ubyte:3 version, ubyte:5 flags. */
+ out_one ((flags << 3) | 1);
+
+ /* Size of prologue. */
+ if (c->endprologue_addr)
+ {
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = c->endprologue_addr;
+ exp.X_op_symbol = c->start_addr;
+ exp.X_add_number = 0;
+ emit_expr (&exp, 1);
+ }
+ else
+ out_one (0);
+
+ /* Number of slots (i.e. shorts) in the unwind codes array. */
+ if (count_unwind_codes > 255)
+ as_fatal (_("too much unwind data in this .seh_proc"));
+ out_one (count_unwind_codes);
+
+ /* ubyte:4 frame-reg, ubyte:4 frame-reg-offset. */
+ /* Note that frameoff is already a multiple of 16, and therefore
+ the offset is already both scaled and shifted into place. */
+ out_one (c->frameoff | c->framereg);
+
+ seh_x64_write_prologue_data (c);
+
+ /* We need to align prologue data. */
+ if (count_unwind_codes & 1)
+ out_two (0);
+
+ if (flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
+ {
+ /* Force the use of segment-relative relocations instead of absolute
+ valued expressions. Don't adjust for constants (e.g. NULL). */
+ if (c->handler.X_op == O_symbol)
+ c->handler.X_op = O_symbol_rva;
+ emit_expr (&c->handler, 4);
+ }
+
+ /* Handler data will be tacked in here by subsections. */
+}
+
+/* Write pdata for an ARM (WinCE-style) function (no-op stub). */
+
+static void
+seh_arm_write_function_pdata (seh_context *c ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
static void
do_seh_endproc (void)
{
@@ -272,7 +455,8 @@ obj_coff_seh_proc (int what ATTRIBUTE_UNUSED)
seh_ctx_cur->code_seg = now_seg;
- if (seh_get_target_kind () == seh_kind_x64)
+ if (seh_get_target_kind () == seh_kind_x64
+ || seh_get_target_kind () == seh_kind_aarch64)
{
x_segcur = seh_hash_find_or_make (seh_ctx_cur->code_seg, ".xdata");
seh_ctx_cur->subsection = x_segcur->subseg;
@@ -565,248 +749,737 @@ obj_coff_seh_setframe (int what ATTRIBUTE_UNUSED)
seh_x64_make_prologue_element (UWOP_SET_FPREG, 0, 0);
}
}
-
-/* Data writing routines. */
-
-/* Output raw integers in 1, 2, or 4 bytes. */
-
-static inline void
-out_one (int byte)
-{
- FRAG_APPEND_1_CHAR (byte);
-}
-static inline void
-out_two (int data)
+/* AArch64 support. */
+
+/* AArch64 register name tables. */
+static const char * const aarch64_int_regs[31] = {
+ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+ "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+ "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
+ "x24", "x25", "x26", "x27", "x28", "x29", "x30"
+};
+
+static const char * const aarch64_fp_regs[32] = {
+ "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
+ "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
+ "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
+ "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31"
+};
+
+/* Read an AArch64 integer register from input stream.
+ Returns the register number (0-30) or -1 on error.
+ Also accepts "fp" (x29) and "lr" (x30). */
+static int
+seh_aarch64_read_int_reg (const char *directive, int min_reg, int max_reg)
{
- md_number_to_chars (frag_more (2), data, 2);
-}
+ char name_end;
+ char *symbol_name;
+ int i;
-static inline void
-out_four (int data)
-{
- md_number_to_chars (frag_more (4), data, 4);
-}
+ SKIP_WHITESPACE ();
+ name_end = get_symbol_name (&symbol_name);
-/* Write out prologue data for x64. */
+ /* Check for special names. */
+ if (strcasecmp (symbol_name, "fp") == 0)
+ i = 29;
+ else if (strcasecmp (symbol_name, "lr") == 0)
+ i = 30;
+ else
+ {
+ for (i = 0; i < 31; i++)
+ if (!strcasecmp (aarch64_int_regs[i], symbol_name))
+ break;
+ }
-static void
-seh_x64_write_prologue_data (const seh_context *c)
-{
- int i;
+ (void) restore_line_pointer (name_end);
- /* We have to store in reverse order. */
- for (i = c->elems_count - 1; i >= 0; --i)
+ if (i > max_reg || i < min_reg)
{
- const seh_prologue_element *e = c->elems + i;
- expressionS exp;
+ as_bad (_("invalid register for %s"), directive);
+ return -1;
+ }
- /* First comes byte offset in code. */
- exp.X_op = O_subtract;
- exp.X_add_symbol = e->pc_addr;
- exp.X_op_symbol = c->start_addr;
- exp.X_add_number = 0;
- emit_expr (&exp, 1);
+ return i;
+}
- /* Second comes code+info packed into a byte. */
- out_one ((e->info << 4) | e->code);
+/* Read an AArch64 FP/SIMD register from input stream.
+ Returns the register number (0-31) or -1 on error. */
+static int
+seh_aarch64_read_fp_reg (const char *directive)
+{
+ char name_end;
+ char *symbol_name;
+ int i;
- switch (e->code)
- {
- case UWOP_PUSH_NONVOL:
- case UWOP_ALLOC_SMALL:
- case UWOP_SET_FPREG:
- case UWOP_PUSH_MACHFRAME:
- /* These have no extra data. */
- break;
+ SKIP_WHITESPACE ();
+ name_end = get_symbol_name (&symbol_name);
- case UWOP_ALLOC_LARGE:
- if (e->info)
- {
- case UWOP_SAVE_NONVOL_FAR:
- case UWOP_SAVE_XMM128_FAR:
- /* An unscaled 4 byte offset. */
- out_four (e->off);
- break;
- }
- /* FALLTHRU */
+ for (i = 0; i < 32; i++)
+ if (!strcasecmp (aarch64_fp_regs[i], symbol_name))
+ break;
- case UWOP_SAVE_NONVOL:
- case UWOP_SAVE_XMM128:
- /* A scaled 2 byte offset. */
- out_two (e->off);
- break;
+ (void) restore_line_pointer (name_end);
- default:
- abort ();
- }
+ if (i == 32)
+ {
+ as_bad (_("invalid floating-point register for %s"), directive);
+ return -1;
}
+
+ return i;
}
-static int
-seh_x64_size_prologue_data (const seh_context *c)
+/* Add a prologue element to the AArch64 SEH context. */
+static void
+seh_aarch64_make_prologue_element (int code, int info, offsetT off)
{
- int i, ret = 0;
+ seh_prologue_element *n;
- for (i = c->elems_count - 1; i >= 0; --i)
- switch (c->elems[i].code)
- {
- case UWOP_PUSH_NONVOL:
- case UWOP_ALLOC_SMALL:
- case UWOP_SET_FPREG:
- case UWOP_PUSH_MACHFRAME:
- ret += 1;
- break;
+ if (seh_ctx_cur == NULL)
+ return;
+ if (seh_ctx_cur->elems_count == seh_ctx_cur->elems_max)
+ {
+ seh_ctx_cur->elems_max += 8;
+ seh_ctx_cur->elems = XRESIZEVEC (seh_prologue_element,
+ seh_ctx_cur->elems,
+ seh_ctx_cur->elems_max);
+ }
- case UWOP_SAVE_NONVOL:
- case UWOP_SAVE_XMM128:
- ret += 2;
- break;
+ n = &seh_ctx_cur->elems[seh_ctx_cur->elems_count++];
+ n->code = code;
+ n->info = info;
+ n->off = off;
+ n->pc_addr = symbol_temp_new_now ();
+}
- case UWOP_SAVE_NONVOL_FAR:
- case UWOP_SAVE_XMM128_FAR:
- ret += 3;
- break;
+/* .seh_save_regp <reg1>, <reg2>, <offset> (aarch64)
+ Save register pair at offset from SP. */
+static void
+obj_coff_seh_aarch64_save_regp (int what ATTRIBUTE_UNUSED)
+{
+ int reg1, reg2;
+ offsetT off;
- case UWOP_ALLOC_LARGE:
- ret += (c->elems[i].info ? 3 : 2);
- break;
+ if (!verify_context_and_target (".seh_save_regp", seh_kind_aarch64)
+ || !seh_validate_seg (".seh_save_regp"))
+ return;
- default:
- abort ();
- }
+ reg1 = seh_aarch64_read_int_reg (".seh_save_regp", 0, 30);
+ if (!skip_whitespace_and_comma (1))
+ return;
+ reg2 = seh_aarch64_read_int_reg (".seh_save_regp", 0, 30);
+ if (!skip_whitespace_and_comma (1))
+ return;
+ off = get_absolute_expression ();
+ demand_empty_rest_of_line ();
- return ret;
+ if (reg1 < 0 || reg2 < 0)
+ return;
+ if (off < 0 || (off & 7))
+ {
+ as_bad (_(".seh_save_regp offset must be non-negative and 8-byte aligned"));
+ return;
+ }
+
+ /* Check for special case: saving x29,x30 (FPLR pair). */
+ if (reg1 == 29 && reg2 == 30)
+ {
+ if (off <= 0x3F * 8)
+ {
+ seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_FPLR, off >> 3, 0);
+ return;
+ }
+ }
+
+ seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_REG_P, reg1, off);
}
-/* Write out the xdata information for one function (x64). */
+/* .seh_save_fregp <reg1>, <reg2>, <offset> (aarch64)
+ Save FP/SIMD register pair. */
+static void
+obj_coff_seh_aarch64_save_fregp (int what ATTRIBUTE_UNUSED)
+{
+ int reg1, reg2;
+ offsetT off;
+
+ if (!verify_context_and_target (".seh_save_fregp", seh_kind_aarch64)
+ || !seh_validate_seg (".seh_save_fregp"))
+ return;
+
+ reg1 = seh_aarch64_read_fp_reg (".seh_save_fregp");
+ if (!skip_whitespace_and_comma (1))
+ return;
+ reg2 = seh_aarch64_read_fp_reg (".seh_save_fregp");
+ if (!skip_whitespace_and_comma (1))
+ return;
+ off = get_absolute_expression ();
+ demand_empty_rest_of_line ();
+
+ if (reg1 < 0 || reg2 < 0)
+ return;
+ if (off < 0 || (off & 7))
+ {
+ as_bad (_(".seh_save_fregp offset must be non-negative and 8-byte aligned"));
+ return;
+ }
+
+ seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_FREG_P, reg1, off);
+}
+/* .seh_save_reg <reg>, <offset> (aarch64)
+ Save a single integer register. */
static void
-seh_x64_write_function_xdata (seh_context *c)
+obj_coff_seh_aarch64_save_reg (int what ATTRIBUTE_UNUSED)
{
- int flags, count_unwind_codes;
- expressionS exp;
+ int reg;
+ offsetT off;
- /* Set 4-byte alignment. */
- frag_align (2, 0, 0);
+ if (!verify_context_and_target (".seh_save_reg", seh_kind_aarch64)
+ || !seh_validate_seg (".seh_save_reg"))
+ return;
- c->xdata_addr = symbol_temp_new_now ();
- flags = c->handler_flags;
- count_unwind_codes = seh_x64_size_prologue_data (c);
+ reg = seh_aarch64_read_int_reg (".seh_save_reg", 0, 30);
+ if (!skip_whitespace_and_comma (1))
+ return;
+ off = get_absolute_expression ();
+ demand_empty_rest_of_line ();
- /* ubyte:3 version, ubyte:5 flags. */
- out_one ((flags << 3) | 1);
+ if (reg < 0)
+ return;
+ if (off < 0 || (off & 7))
+ {
+ as_bad (_(".seh_save_reg offset must be non-negative and 8-byte aligned"));
+ return;
+ }
- /* Size of prologue. */
- if (c->endprologue_addr)
+ seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_REG, reg, off);
+}
+
+/* .seh_save_freg <reg>, <offset> (aarch64)
+ Save a single FP/SIMD register. */
+static void
+obj_coff_seh_aarch64_save_freg (int what ATTRIBUTE_UNUSED)
+{
+ int reg;
+ offsetT off;
+
+ if (!verify_context_and_target (".seh_save_freg", seh_kind_aarch64)
+ || !seh_validate_seg (".seh_save_freg"))
+ return;
+
+ reg = seh_aarch64_read_fp_reg (".seh_save_freg");
+ if (!skip_whitespace_and_comma (1))
+ return;
+ off = get_absolute_expression ();
+ demand_empty_rest_of_line ();
+
+ if (reg < 0)
+ return;
+ if (off < 0 || (off & 7))
{
- exp.X_op = O_subtract;
- exp.X_add_symbol = c->endprologue_addr;
- exp.X_op_symbol = c->start_addr;
- exp.X_add_number = 0;
- emit_expr (&exp, 1);
+ as_bad (_(".seh_save_freg offset must be non-negative and 8-byte aligned"));
+ return;
}
- else
- out_one (0);
- /* Number of slots (i.e. shorts) in the unwind codes array. */
- if (count_unwind_codes > 255)
- as_fatal (_("too much unwind data in this .seh_proc"));
- out_one (count_unwind_codes);
+ seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_FREG, reg, off);
+}
- /* ubyte:4 frame-reg, ubyte:4 frame-reg-offset. */
- /* Note that frameoff is already a multiple of 16, and therefore
- the offset is already both scaled and shifted into place. */
- out_one (c->frameoff | c->framereg);
+/* .seh_save_fplr <offset> (aarch64)
+ Save x29 (FP) and x30 (LR) at a positive offset from SP. */
+static void
+obj_coff_seh_aarch64_save_fplr (int what ATTRIBUTE_UNUSED)
+{
+ offsetT off;
- seh_x64_write_prologue_data (c);
+ if (!verify_context_and_target (".seh_save_fplr", seh_kind_aarch64)
+ || !seh_validate_seg (".seh_save_fplr"))
+ return;
- /* We need to align prologue data. */
- if (count_unwind_codes & 1)
- out_two (0);
+ off = get_absolute_expression ();
+ demand_empty_rest_of_line ();
- if (flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
+ if (off < 0 || (off & 7))
{
- /* Force the use of segment-relative relocations instead of absolute
- valued expressions. Don't adjust for constants (e.g. NULL). */
- if (c->handler.X_op == O_symbol)
- c->handler.X_op = O_symbol_rva;
- emit_expr (&c->handler, 4);
+ as_bad (_(".seh_save_fplr offset must be non-negative and 8-byte aligned"));
+ return;
}
- /* Handler data will be tacked in here by subsections. */
+ seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_FPLR, off >> 3, 0);
}
-/* Write out xdata for one function. */
+/* .seh_save_fplr_x <offset> (aarch64)
+ Save x29 (FP) and x30 (LR) with predecrement (stp x29, x30, [sp, #-N]!). */
+static void
+obj_coff_seh_aarch64_save_fplr_x (int what ATTRIBUTE_UNUSED)
+{
+ offsetT off;
+
+ if (!verify_context_and_target (".seh_save_fplr_x", seh_kind_aarch64)
+ || !seh_validate_seg (".seh_save_fplr_x"))
+ return;
+
+ off = get_absolute_expression ();
+ demand_empty_rest_of_line ();
+
+ if (off < 0 || (off & 15))
+ {
+ as_bad (_(".seh_save_fplr_x offset must be negative and 16-byte aligned"));
+ return;
+ }
+ if (off > 0x3F * 8)
+ {
+ as_bad (_(".seh_save_fplr_x offset out of range (max 504)"));
+ return;
+ }
+ seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_FPLRX, (off >> 3) - 1, 0);
+}
+
+/* .seh_save_lrpair <reg>, <offset> (aarch64)
+ Save x30 (LR) and another register at offset from SP. */
static void
-write_function_xdata (seh_context *c)
+obj_coff_seh_aarch64_save_lrpair (int what ATTRIBUTE_UNUSED)
{
- segT save_seg = now_seg;
- int save_subseg = now_subseg;
+ int reg;
+ offsetT off;
+
+ if (!verify_context_and_target (".seh_save_lrpair", seh_kind_aarch64)
+ || !seh_validate_seg (".seh_save_lrpair"))
+ return;
+
+ reg = seh_aarch64_read_int_reg (".seh_save_lrpair", 0, 28);
+ if (!skip_whitespace_and_comma (1))
+ return;
+ off = get_absolute_expression ();
+ demand_empty_rest_of_line ();
- /* MIPS, SH, ARM don't have xdata. */
- if (seh_get_target_kind () != seh_kind_x64)
+ if (reg < 0)
return;
+ if (off < 0 || (off & 7))
+ {
+ as_bad (_(".seh_save_lrpair offset must be non-negative and 8-byte aligned"));
+ return;
+ }
- switch_xdata (c->subsection, c->code_seg);
+ seh_aarch64_make_prologue_element (AARCH64_UOP_SAVE_LRPAIR, (reg - 19) >> 1, off >> 3);
+}
- seh_x64_write_function_xdata (c);
+/* .seh_set_fp (aarch64)
+ Set frame pointer (mov x29, sp). */
+static void
+obj_coff_seh_aarch64_set_fp (int what ATTRIBUTE_UNUSED)
+{
+ if (!verify_context_and_target (".seh_set_fp", seh_kind_aarch64)
+ || !seh_validate_seg (".seh_set_fp"))
+ return;
+ demand_empty_rest_of_line ();
- subseg_set (save_seg, save_subseg);
+ seh_aarch64_make_prologue_element (AARCH64_UOP_SET_FP, 0, 0);
}
-/* Write pdata section data for one function (arm). */
+/* .seh_add_fp <offset> (aarch64)
+ Add offset to frame pointer (add x29, sp, #N). */
+static void
+obj_coff_seh_aarch64_add_fp (int what ATTRIBUTE_UNUSED)
+{
+ offsetT off;
+
+ if (!verify_context_and_target (".seh_add_fp", seh_kind_aarch64)
+ || !seh_validate_seg (".seh_add_fp"))
+ return;
+
+ off = get_absolute_expression ();
+ demand_empty_rest_of_line ();
+
+ seh_aarch64_make_prologue_element (AARCH64_UOP_ADD_FP, off >> 3, 0);
+}
+/* .seh_nop (aarch64)
+ No-op padding in the unwind code. */
static void
-seh_arm_write_function_pdata (seh_context *c)
+obj_coff_seh_aarch64_nop (int what ATTRIBUTE_UNUSED)
{
- expressionS exp;
- unsigned int prol_len = 0, func_len = 0;
- unsigned int val;
-
- /* Start address of the function. */
- exp.X_op = O_symbol;
- exp.X_add_symbol = c->start_addr;
- exp.X_add_number = 0;
- emit_expr (&exp, 4);
-
- exp.X_op = O_subtract;
- exp.X_add_symbol = c->end_addr;
- exp.X_op_symbol = c->start_addr;
- exp.X_add_number = 0;
- if (resolve_expression (&exp) && exp.X_op == O_constant)
- func_len = exp.X_add_number;
- else
- as_bad (_(".seh_endproc in a different section from .seh_proc"));
+ if (!verify_context_and_target (".seh_nop", seh_kind_aarch64)
+ || !seh_validate_seg (".seh_nop"))
+ return;
+ demand_empty_rest_of_line ();
- if (c->endprologue_addr)
+ seh_aarch64_make_prologue_element (AARCH64_UOP_NOP, 0, 0);
+}
+
+/* .seh_alloc_stack <size> (aarch64)
+ Allocate stack space. */
+static void
+obj_coff_seh_aarch64_alloc_stack (int what ATTRIBUTE_UNUSED)
+{
+ offsetT off;
+ int code, info;
+
+ if (!verify_context_and_target (".seh_alloc_stack", seh_kind_aarch64)
+ || !seh_validate_seg (".seh_alloc_stack"))
+ return;
+
+ off = get_absolute_expression ();
+ demand_empty_rest_of_line ();
+
+ if (off == 0)
+ return;
+ if (off < 0)
{
- exp.X_op = O_subtract;
- exp.X_add_symbol = c->endprologue_addr;
- exp.X_op_symbol = c->start_addr;
- exp.X_add_number = 0;
+ as_bad (_(".seh_alloc_stack offset is negative"));
+ return;
+ }
+
+ if ((off & 15) == 0 && off <= 0x1F * 16)
+ {
+ code = AARCH64_UOP_ALLOC_SMALL;
+ info = (off / 16) - 1;
+ }
+ else if ((off & 15) == 0 && off <= 0x7FF * 16)
+ {
+ code = AARCH64_UOP_ALLOC_MEDIUM;
+ info = off / 16;
+ }
+ else if ((off & 15) == 0 && off <= (offsetT) 0xFFFFFF * 16)
+ {
+ code = AARCH64_UOP_ALLOC_LARGE;
+ info = 0;
+ off = off / 16;
+ }
+ else
+ {
+ as_bad (_(".seh_alloc_stack offset out of range"));
+ return;
+ }
- if (resolve_expression (&exp) && exp.X_op == O_constant)
- prol_len = exp.X_add_number;
+ seh_aarch64_make_prologue_element (code, info, off);
+}
+
+/* AArch64 xdata writing. */
+
+/* Count the size of AArch64 unwind code data in bytes. */
+static int
+seh_aarch64_size_prologue_data (const seh_context *c)
+{
+ int i, ret = 0;
+
+ for (i = c->elems_count - 1; i >= 0; --i)
+ {
+ int code = c->elems[i].code;
+ if (code == AARCH64_UOP_ALLOC_SMALL
+ || code == AARCH64_UOP_SAVE_R19R20X
+ || code == AARCH64_UOP_SAVE_FPLRX
+ || code == AARCH64_UOP_SAVE_FPLR
+ || code == AARCH64_UOP_SET_FP
+ || code == AARCH64_UOP_NOP
+ || code == AARCH64_UOP_END
+ || code == AARCH64_UOP_SAVE_NEXT
+ || code == AARCH64_UOP_TRAP_FRAME
+ || code == AARCH64_UOP_PUSH_MACH
+ || code == AARCH64_UOP_CONTEXT
+ || code == AARCH64_UOP_EC_CONTEXT
+ || code == AARCH64_UOP_CLEAR_UNWOUND_TO_CALL
+ || code == AARCH64_UOP_PAC_SIGN_LR)
+ ret += 1;
+ else if (code == AARCH64_UOP_ALLOC_MEDIUM
+ || code == AARCH64_UOP_SAVE_REG
+ || code == AARCH64_UOP_SAVE_REG_X
+ || code == AARCH64_UOP_SAVE_REG_P
+ || code == AARCH64_UOP_SAVE_REG_PX
+ || code == AARCH64_UOP_SAVE_LRPAIR
+ || code == AARCH64_UOP_SAVE_FREG
+ || code == AARCH64_UOP_SAVE_FREG_X
+ || code == AARCH64_UOP_SAVE_FREG_P
+ || code == AARCH64_UOP_SAVE_FREG_PX
+ || code == AARCH64_UOP_ADD_FP)
+ ret += 2;
+ else if (code == AARCH64_UOP_ALLOC_LARGE
+ || code == AARCH64_UOP_SAVE_ANY_REG_I
+ || code == AARCH64_UOP_SAVE_ANY_REG_IP
+ || code == AARCH64_UOP_SAVE_ANY_REG_D
+ || code == AARCH64_UOP_SAVE_ANY_REG_DP
+ || code == AARCH64_UOP_SAVE_ANY_REG_Q
+ || code == AARCH64_UOP_SAVE_ANY_REG_QP)
+ ret += 3;
else
- as_bad (_(".seh_endprologue in a different section from .seh_proc"));
+ abort ();
}
- /* Both function and prologue are in units of instructions. */
- func_len >>= (c->use_instruction_32 ? 2 : 1);
- prol_len >>= (c->use_instruction_32 ? 2 : 1);
+ return ret;
+}
+
+/* Write out AArch64 prologue unwind codes. */
+static void
+seh_aarch64_write_prologue_data (const seh_context *c)
+{
+ int i;
- /* Assemble the second word of the pdata. */
- val = prol_len & 0xff;
- val |= (func_len & 0x3fffff) << 8;
- if (c->use_instruction_32)
- val |= 0x40000000U;
- if (c->handler_written)
- val |= 0x80000000U;
- out_four (val);
+ /* We have to store in reverse order. */
+ for (i = c->elems_count - 1; i >= 0; --i)
+ {
+ const seh_prologue_element *e = c->elems + i;
+
+ switch (e->code)
+ {
+ case AARCH64_UOP_ALLOC_SMALL:
+ out_one (AARCH64_UOP_ALLOC_SMALL | (e->info & 0x1f));
+ break;
+
+ case AARCH64_UOP_ALLOC_MEDIUM:
+ out_one (AARCH64_UOP_ALLOC_MEDIUM | ((e->info >> 8) & 3));
+ out_one (e->info & 0xff);
+ break;
+
+ case AARCH64_UOP_ALLOC_LARGE:
+ out_one (AARCH64_UOP_ALLOC_LARGE);
+ out_two (e->off);
+ break;
+
+ case AARCH64_UOP_SAVE_R19R20X:
+ out_one (AARCH64_UOP_SAVE_R19R20X | (e->info & 0x1f));
+ break;
+
+ case AARCH64_UOP_SAVE_FPLRX:
+ out_one (AARCH64_UOP_SAVE_FPLRX | (e->info & 0x3f));
+ break;
+
+ case AARCH64_UOP_SAVE_FPLR:
+ out_one (AARCH64_UOP_SAVE_FPLR | (e->info & 0x3f));
+ break;
+
+ case AARCH64_UOP_SAVE_REG:
+ {
+ int r = e->info - 19;
+ out_one (AARCH64_UOP_SAVE_REG | ((r & 0xC) >> 2));
+ out_one (((r & 0x3) << 6) | ((e->off >> 3) & 0x3f));
+ }
+ break;
+
+ case AARCH64_UOP_SAVE_REG_X:
+ {
+ int r = e->info - 19;
+ out_one (AARCH64_UOP_SAVE_REG_X | ((r & 0x8) >> 3));
+ out_one (((r & 0x7) << 5) | (((e->off >> 3) - 1) & 0x1f));
+ }
+ break;
+
+ case AARCH64_UOP_SAVE_REG_P:
+ {
+ int r = e->info - 19;
+ out_one (AARCH64_UOP_SAVE_REG_P | ((r & 0xC) >> 2));
+ out_one (((r & 0x3) << 6) | ((e->off >> 3) & 0x3f));
+ }
+ break;
+
+ case AARCH64_UOP_SAVE_REG_PX:
+ {
+ int r = e->info - 19;
+ out_one (AARCH64_UOP_SAVE_REG_PX | ((r & 0xC) >> 2));
+ out_one (((r & 0x3) << 6) | (((e->off >> 3) - 1) & 0x3f));
+ }
+ break;
+
+ case AARCH64_UOP_SAVE_LRPAIR:
+ {
+ int r = e->info - 19;
+ out_one (AARCH64_UOP_SAVE_LRPAIR | ((r & 0xC) >> 2));
+ out_one (((r & 0x3) << 6) | ((e->off >> 3) & 0x3f));
+ }
+ break;
+
+ case AARCH64_UOP_SAVE_FREG:
+ {
+ int r = e->info - 8;
+ out_one (AARCH64_UOP_SAVE_FREG | ((r & 0x4) >> 2));
+ out_one (((r & 0x3) << 6) | ((e->off >> 3) & 0x3f));
+ }
+ break;
+
+ case AARCH64_UOP_SAVE_FREG_X:
+ {
+ int r = e->info - 8;
+ out_one (AARCH64_UOP_SAVE_FREG_X);
+ out_one (((r & 0x7) << 5) | (((e->off >> 3) - 1) & 0x1f));
+ }
+ break;
+
+ case AARCH64_UOP_SAVE_FREG_P:
+ {
+ int r = e->info - 8;
+ out_one (AARCH64_UOP_SAVE_FREG_P | ((r & 0x4) >> 2));
+ out_one (((r & 0x3) << 6) | ((e->off >> 3) & 0x3f));
+ }
+ break;
+
+ case AARCH64_UOP_SAVE_FREG_PX:
+ {
+ int r = e->info - 8;
+ out_one (AARCH64_UOP_SAVE_FREG_PX | ((r & 0x4) >> 2));
+ out_one (((r & 0x3) << 6) | (((e->off >> 3) - 1) & 0x3f));
+ }
+ break;
+
+ case AARCH64_UOP_SET_FP:
+ case AARCH64_UOP_NOP:
+ case AARCH64_UOP_END:
+ case AARCH64_UOP_SAVE_NEXT:
+ case AARCH64_UOP_TRAP_FRAME:
+ case AARCH64_UOP_PUSH_MACH:
+ case AARCH64_UOP_CONTEXT:
+ case AARCH64_UOP_EC_CONTEXT:
+ case AARCH64_UOP_CLEAR_UNWOUND_TO_CALL:
+ case AARCH64_UOP_PAC_SIGN_LR:
+ out_one (e->code);
+ break;
+
+ case AARCH64_UOP_ADD_FP:
+ out_one (AARCH64_UOP_ADD_FP);
+ out_one (e->info & 0xff);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ /* Terminate with END opcode. */
+ out_one (AARCH64_UOP_END);
+}
+
+/* Write the xdata for one AArch64 function. */
+static void
+seh_aarch64_write_function_xdata (seh_context *c)
+{
+ int code_words, epilog_count;
+ unsigned int func_length;
+
+ /* 4-byte alignment. */
+ frag_align (2, 0, 0);
+
+ c->xdata_addr = symbol_temp_new_now ();
+
+ /* Calculate function length in 4-byte units. Compute it from the
+ symbols' fragment positions rather than resolve_expression, which
+ can fail when the symbols span multiple fragments. */
+ {
+ addressT off1, off2;
+ fragS *f1 = symbol_get_frag_and_value (c->start_addr, &off1);
+ fragS *f2 = symbol_get_frag_and_value (c->end_addr, &off2);
+ long bytes = 0;
+
+ if (f1 == f2)
+ bytes = off2 - off1;
+ else
+ {
+ /* Sum fragment sizes from f1 to f2. */
+ bytes = off2;
+ for (fragS *f = f1; f && f != f2; f = f->fr_next)
+ {
+ if (!f->fr_next)
+ break;
+ bytes += f->fr_fix;
+ if (f->fr_var > 0)
+ bytes += f->fr_var * f->fr_subtype;
+ }
+ bytes -= off1;
+ }
+ func_length = bytes < 0 ? 0 : bytes >> 2;
+ }
+
+ /* Count unwind code bytes, including the terminating END. */
+ int code_bytes = seh_aarch64_size_prologue_data (c) + 1;
+ code_words = (code_bytes + 3) / 4;
+
+ /* A function with no prologue codes only needs the END marker.
+ In that case, code_words is 0 and no unwind code bytes are emitted. */
+ bool no_unwind_codes = (code_bytes == 1);
+ if (no_unwind_codes)
+ code_words = 0;
+
+ /* Header word (Microsoft ARM64 SEH xdata format, matching LLVM MCWin64EH):
+ bits [0:17] = Function Length / 4 (18 bits)
+ bit [20] = X (Exception Handler Present)
+ bit [21] = E (Packed Epilog Present)
+ bits [26:22] = Epilog Count (5 bits)
+ bits [31:27] = Code Words (5 bits) */
+ epilog_count = 0;
+ unsigned int header = func_length & 0x3ffff;
+ if (code_words > 0x1f)
+ {
+ header |= (0 << 27);
+ header |= ((epilog_count & 0x1f) << 22);
+ }
+ else
+ {
+ header |= ((code_words & 0x1f) << 27);
+ header |= ((epilog_count & 0x1f) << 22);
+ }
+ if (c->handler_flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
+ header |= (1 << 20);
+
+ out_four (header);
+
+ /* If extended code words needed, emit extension word.
+ bits [15:0] = Epilog Count, bits [23:16] = Code Words.
+ Also emit it when both the code words and epilog count are zero:
+ in that case Windows (and Wine) treat the packed fields as zero and
+ read the actual values from the extension word. */
+ if (code_words > 0x1f || no_unwind_codes)
+ {
+ unsigned int ext = ((code_words & 0xff) << 16)
+ | (epilog_count & 0xffff);
+ out_four (ext);
+ }
+
+ /* Write prologue unwind codes (skipped when no_unwind_codes). */
+ if (!no_unwind_codes)
+ seh_aarch64_write_prologue_data (c);
+
+ /* Pad to 4-byte alignment. Only needed when unwind codes are present. */
+ if (!no_unwind_codes)
+ {
+ int remainder = (code_bytes) & 3;
+ if (remainder)
+ for (int i = 0; i < 4 - remainder; i++)
+ out_one (AARCH64_UOP_NOP);
+ }
+
+ /* If exception handler present, emit it. */
+ if (c->handler_flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
+ {
+ if (c->handler.X_op == O_symbol)
+ c->handler.X_op = O_symbol_rva;
+ emit_expr (&c->handler, 4);
+ }
+
+ /* Handler data follows in subsections. */
+}
+
+/* Write out xdata for one function. */
+
+static void
+write_function_xdata (seh_context *c)
+{
+ segT save_seg = now_seg;
+ int save_subseg = now_subseg;
+
+ if (seh_get_target_kind () == seh_kind_x64)
+ {
+ switch_xdata (c->subsection, c->code_seg);
+ seh_x64_write_function_xdata (c);
+ }
+ else if (seh_get_target_kind () == seh_kind_aarch64)
+ {
+ switch_xdata (c->subsection, c->code_seg);
+ seh_aarch64_write_function_xdata (c);
+ }
+
+ subseg_set (save_seg, save_subseg);
}
+
+
/* Write out pdata for one function. */
static void
@@ -858,6 +1531,17 @@ write_function_pdata (seh_context *c)
seh_arm_write_function_pdata (c);
break;
+ case seh_kind_aarch64:
+ exp.X_op = O_symbol_rva;
+ exp.X_add_number = 0;
+ exp.X_add_symbol = c->start_addr;
+ emit_expr (&exp, 4);
+ exp.X_op = O_symbol_rva;
+ exp.X_add_number = 0;
+ exp.X_add_symbol = c->xdata_addr;
+ emit_expr (&exp, 4);
+ break;
+
default:
abort ();
}
diff --git a/gas/config/obj-coff-seh.h b/gas/config/obj-coff-seh.h
index 8a3e1d602aa..f1b3535d138 100644
--- a/gas/config/obj-coff-seh.h
+++ b/gas/config/obj-coff-seh.h
@@ -25,6 +25,8 @@
The third is the IA64 and x64 version. Note, the IA64 isn't implemented yet,
but to find information about it, please see specification about IA64 on
http://download.intel.com/design/Itanium/Downloads/245358.pdf file.
+ The fourth is for AArch64 (ARM64) Windows, which uses the same pdata/xdata
+ model as x64 but with different unwind codes and a 2-word pdata entry.
The first version has just entries in the pdata section: BeginAddress,
EndAddress, ExceptionHandler, HandlerData, and PrologueEndAddress. Each
@@ -41,10 +43,14 @@
prologue, exception-handler, and additional SEH data is stored
within the UNWIND_DATA field in the xdata section.
+ The fourth (AArch64/ARM64) version has a 2-word pdata entry:
+ BeginAddress (RVA) and UnwindData (RVA), with xdata using ARM64-specific
+ unwind codes.
+
The pseudos:
.seh_proc <fct_name>
.seh_endprologue
- .seh_handler <handler>[,@unwind][,@except] (x64)
+ .seh_handler <handler>[,@unwind][,@except] (x64, aarch64)
.seh_handler <handler>[,<handler_data>] (others)
.seh_handlerdata
.seh_eh
@@ -57,6 +63,17 @@
.seh_savexmm
.seh_pushframe
.seh_code
+ .seh_save_regp <reg1>,<reg2>,<offset> (aarch64)
+ .seh_save_fregp <reg1>,<reg2>,<offset> (aarch64)
+ .seh_save_reg <reg>,<offset> (aarch64)
+ .seh_save_freg <reg>,<offset> (aarch64)
+ .seh_save_fplr <offset> (aarch64)
+ .seh_save_fplr_x <offset> (aarch64)
+ .seh_save_lrpair <reg>,<offset> (aarch64)
+ .seh_set_fp (aarch64)
+ .seh_add_fp <offset> (aarch64)
+ .seh_nop (aarch64)
+ .seh_alloc_stack <size> (aarch64)
*/
#ifndef OBJ_COFF_SEH_H
@@ -78,7 +95,18 @@
{"seh_no32", obj_coff_seh_32, 0}, \
{"seh_handler", obj_coff_seh_handler, 0}, \
{"seh_code", obj_coff_seh_code, 0}, \
- {"seh_handlerdata", obj_coff_seh_handlerdata, 0},
+ {"seh_handlerdata", obj_coff_seh_handlerdata, 0}, \
+ {"seh_save_regp", obj_coff_seh_aarch64_save_regp, 0}, \
+ {"seh_save_fregp", obj_coff_seh_aarch64_save_fregp, 0}, \
+ {"seh_save_reg", obj_coff_seh_aarch64_save_reg, 0}, \
+ {"seh_save_freg", obj_coff_seh_aarch64_save_freg, 0}, \
+ {"seh_save_fplr", obj_coff_seh_aarch64_save_fplr, 0}, \
+ {"seh_save_fplr_x", obj_coff_seh_aarch64_save_fplr_x, 0}, \
+ {"seh_save_lrpair", obj_coff_seh_aarch64_save_lrpair, 0}, \
+ {"seh_set_fp", obj_coff_seh_aarch64_set_fp, 0}, \
+ {"seh_add_fp", obj_coff_seh_aarch64_add_fp, 0}, \
+ {"seh_nop", obj_coff_seh_aarch64_nop, 0}, \
+ {"seh_alloc_stack", obj_coff_seh_aarch64_alloc_stack, 0},
/* Type definitions. */
@@ -135,9 +163,44 @@ typedef enum seh_kind {
seh_kind_unknown = 0,
seh_kind_mips = 1, /* Used for MIPS and x86 pdata generation. */
seh_kind_arm = 2, /* Used for ARM, PPC, SH3, and SH4 pdata (PDATA_EH) generation. */
- seh_kind_x64 = 3 /* Used for IA64 and x64 pdata/xdata generation. */
+ seh_kind_x64 = 3, /* Used for IA64 and x64 pdata/xdata generation. */
+ seh_kind_aarch64 = 4 /* Used for AArch64 (ARM64) Windows pdata/xdata generation. */
} seh_kind;
+/* AArch64 unwind opcodes. */
+#define AARCH64_UOP_ALLOC_SMALL 0x00
+#define AARCH64_UOP_ALLOC_MEDIUM 0xC0
+#define AARCH64_UOP_ALLOC_LARGE 0xE0
+#define AARCH64_UOP_SAVE_R19R20X 0x20
+#define AARCH64_UOP_SAVE_FPLRX 0x80
+#define AARCH64_UOP_SAVE_FPLR 0x40
+#define AARCH64_UOP_SAVE_REG 0xD0
+#define AARCH64_UOP_SAVE_REG_X 0xD4
+#define AARCH64_UOP_SAVE_REG_P 0xC8
+#define AARCH64_UOP_SAVE_REG_PX 0xCC
+#define AARCH64_UOP_SAVE_LRPAIR 0xD6
+#define AARCH64_UOP_SAVE_FREG 0xDC
+#define AARCH64_UOP_SAVE_FREG_X 0xDE
+#define AARCH64_UOP_SAVE_FREG_P 0xD8
+#define AARCH64_UOP_SAVE_FREG_PX 0xDA
+#define AARCH64_UOP_SET_FP 0xE1
+#define AARCH64_UOP_ADD_FP 0xE2
+#define AARCH64_UOP_NOP 0xE3
+#define AARCH64_UOP_END 0xE4
+#define AARCH64_UOP_SAVE_NEXT 0xE6
+#define AARCH64_UOP_TRAP_FRAME 0xE8
+#define AARCH64_UOP_PUSH_MACH 0xE9
+#define AARCH64_UOP_CONTEXT 0xEA
+#define AARCH64_UOP_EC_CONTEXT 0xEB
+#define AARCH64_UOP_CLEAR_UNWOUND_TO_CALL 0xEC
+#define AARCH64_UOP_PAC_SIGN_LR 0xFC
+#define AARCH64_UOP_SAVE_ANY_REG_I 0xE7
+#define AARCH64_UOP_SAVE_ANY_REG_IP 0xE7
+#define AARCH64_UOP_SAVE_ANY_REG_D 0xE7
+#define AARCH64_UOP_SAVE_ANY_REG_DP 0xE7
+#define AARCH64_UOP_SAVE_ANY_REG_Q 0xE7
+#define AARCH64_UOP_SAVE_ANY_REG_QP 0xE7
+
/* Forward declarations. */
static void obj_coff_seh_stackalloc (int);
static void obj_coff_seh_setframe (int);
@@ -152,6 +215,17 @@ 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);
+static void obj_coff_seh_aarch64_save_regp (int);
+static void obj_coff_seh_aarch64_save_fregp (int);
+static void obj_coff_seh_aarch64_save_reg (int);
+static void obj_coff_seh_aarch64_save_freg (int);
+static void obj_coff_seh_aarch64_save_fplr (int);
+static void obj_coff_seh_aarch64_save_fplr_x (int);
+static void obj_coff_seh_aarch64_save_lrpair (int);
+static void obj_coff_seh_aarch64_set_fp (int);
+static void obj_coff_seh_aarch64_add_fp (int);
+static void obj_coff_seh_aarch64_nop (int);
+static void obj_coff_seh_aarch64_alloc_stack (int);
#define UNDSEC bfd_und_section_ptr
diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index ab122fc388d..7e25f86513d 100644
--- a/libiberty/cp-demangle.c
+++ b/libiberty/cp-demangle.c
@@ -199,6 +199,20 @@ static void d_init_info (const char *, int, size_t, struct d_info *);
#endif /* defined (__STDC__) */
#endif /* ! defined (__GNUC__) */
+/* Allocate SIZE bytes of memory, aborting on failure, like libiberty's
+ xmalloc. libiberty's xmalloc cannot be used here because this file is
+ also compiled into libstdc++ (-DIN_GLIBCPP_V3), which does not link
+ against libiberty. */
+
+static void *
+d_malloc (size_t size)
+{
+ void *p = malloc (size);
+ if (p == NULL)
+ abort ();
+ return p;
+}
+
/* We avoid pulling in the ctype tables, to prevent pulling in
additional unresolved symbols when this code is used in a library.
FIXME: Is this really a valid reason? This comes from the original
@@ -4687,26 +4701,22 @@ cplus_demangle_print_callback (int options,
d_print_init (&dpi, callback, opaque, dc);
- {
-#ifdef CP_DYNAMIC_ARRAYS
- /* Avoid zero-length VLAs, which are prohibited by the C99 standard
- and flagged as errors by Address Sanitizer. */
- __extension__ struct d_saved_scope scopes[(dpi.num_saved_scopes > 0)
- ? dpi.num_saved_scopes : 1];
- __extension__ struct d_print_template temps[(dpi.num_copy_templates > 0)
- ? dpi.num_copy_templates : 1];
-
- dpi.saved_scopes = scopes;
- dpi.copy_templates = temps;
-#else
- dpi.saved_scopes = alloca (dpi.num_saved_scopes
- * sizeof (*dpi.saved_scopes));
- dpi.copy_templates = alloca (dpi.num_copy_templates
- * sizeof (*dpi.copy_templates));
-#endif
+ /* Allocate these on the heap rather than with alloca/VLAs: they can be
+ large for deeply-nested templates, and alloca overflows the stack
+ when the process has a small committed stack (e.g. Windows threads). */
+ if (dpi.num_saved_scopes > 0)
+ dpi.saved_scopes = (struct d_saved_scope *)
+ d_malloc (dpi.num_saved_scopes * sizeof (*dpi.saved_scopes));
+ if (dpi.num_copy_templates > 0)
+ dpi.copy_templates = (struct d_print_template *)
+ d_malloc (dpi.num_copy_templates * sizeof (*dpi.copy_templates));
+
+ d_print_comp (&dpi, options, dc);
- d_print_comp (&dpi, options, dc);
- }
+ if (dpi.saved_scopes != NULL)
+ free (dpi.saved_scopes);
+ if (dpi.copy_templates != NULL)
+ free (dpi.copy_templates);
d_print_flush (&dpi);
@@ -6853,6 +6863,9 @@ d_demangle_callback (const char *mangled, int options,
type;
struct d_info di;
struct demangle_component *dc;
+ struct demangle_component *comps;
+ struct demangle_component **subs;
+ size_t len;
int status;
if (mangled[0] == '_' && mangled[1] == 'Z')
@@ -6869,82 +6882,74 @@ d_demangle_callback (const char *mangled, int options,
type = DCT_TYPE;
}
+ len = strlen (mangled);
+
+ /* We cannot need more components than twice the number of chars in
+ the mangled string, nor more substitutions than chars in it. These
+ arrays used to be stack-allocated with alloca/VLAs, which overflowed
+ the stack on deeply-templated symbols when the process had a small
+ committed stack (e.g. Windows threads). Allocate them on the heap
+ instead, so demangling does not depend on the stack size. */
+ comps = (struct demangle_component *)
+ d_malloc (2 * len * sizeof (*comps));
+ subs = (struct demangle_component **)
+ d_malloc (len * sizeof (*subs));
+
di.unresolved_name_state = 1;
again:
- cplus_demangle_init_info (mangled, options, strlen (mangled), &di);
-
- /* PR 87675 - Check for a mangled string that is so long
- that we do not have enough stack space to demangle it. */
- if (((options & DMGL_NO_RECURSE_LIMIT) == 0)
- /* This check is a bit arbitrary, since what we really want to do is to
- compare the sizes of the di.comps and di.subs arrays against the
- amount of stack space remaining. But there is no portable way to do
- this, so instead we use the recursion limit as a guide to the maximum
- size of the arrays. */
- && (unsigned long) di.num_comps > DEMANGLE_RECURSION_LIMIT)
- {
- /* FIXME: We need a way to indicate that a stack limit has been reached. */
- return 0;
- }
-
- {
-#ifdef CP_DYNAMIC_ARRAYS
- __extension__ struct demangle_component comps[di.num_comps];
- __extension__ struct demangle_component *subs[di.num_subs];
+ cplus_demangle_init_info (mangled, options, len, &di);
- di.comps = comps;
- di.subs = subs;
-#else
- di.comps = alloca (di.num_comps * sizeof (*di.comps));
- di.subs = alloca (di.num_subs * sizeof (*di.subs));
-#endif
+ di.comps = comps;
+ di.subs = subs;
- switch (type)
- {
- case DCT_TYPE:
- dc = cplus_demangle_type (&di);
- break;
- case DCT_MANGLED:
- dc = cplus_demangle_mangled_name (&di, 1);
- break;
- case DCT_GLOBAL_CTORS:
- case DCT_GLOBAL_DTORS:
- d_advance (&di, 11);
- dc = d_make_comp (&di,
- (type == DCT_GLOBAL_CTORS
- ? DEMANGLE_COMPONENT_GLOBAL_CONSTRUCTORS
- : DEMANGLE_COMPONENT_GLOBAL_DESTRUCTORS),
- d_make_demangle_mangled_name (&di, d_str (&di)),
- NULL);
- d_advance (&di, strlen (d_str (&di)));
- break;
- default:
- abort (); /* We have listed all the cases. */
- }
+ switch (type)
+ {
+ case DCT_TYPE:
+ dc = cplus_demangle_type (&di);
+ break;
+ case DCT_MANGLED:
+ dc = cplus_demangle_mangled_name (&di, 1);
+ break;
+ case DCT_GLOBAL_CTORS:
+ case DCT_GLOBAL_DTORS:
+ d_advance (&di, 11);
+ dc = d_make_comp (&di,
+ (type == DCT_GLOBAL_CTORS
+ ? DEMANGLE_COMPONENT_GLOBAL_CONSTRUCTORS
+ : DEMANGLE_COMPONENT_GLOBAL_DESTRUCTORS),
+ d_make_demangle_mangled_name (&di, d_str (&di)),
+ NULL);
+ d_advance (&di, strlen (d_str (&di)));
+ break;
+ default:
+ abort (); /* We have listed all the cases. */
+ }
- /* If DMGL_PARAMS is set, then if we didn't consume the entire
- mangled string, then we didn't successfully demangle it. If
- DMGL_PARAMS is not set, we didn't look at the trailing
- parameters. */
- if (((options & DMGL_PARAMS) != 0) && d_peek_char (&di) != '\0')
- dc = NULL;
+ /* If DMGL_PARAMS is set, then if we didn't consume the entire
+ mangled string, then we didn't successfully demangle it. If
+ DMGL_PARAMS is not set, we didn't look at the trailing
+ parameters. */
+ if (((options & DMGL_PARAMS) != 0) && d_peek_char (&di) != '\0')
+ dc = NULL;
- /* See discussion in d_unresolved_name. */
- if (dc == NULL && di.unresolved_name_state == -1)
- {
- di.unresolved_name_state = 0;
- goto again;
- }
+ /* See discussion in d_unresolved_name. */
+ if (dc == NULL && di.unresolved_name_state == -1)
+ {
+ di.unresolved_name_state = 0;
+ goto again;
+ }
#ifdef CP_DEMANGLE_DEBUG
- d_dump (dc, 0);
+ d_dump (dc, 0);
#endif
- status = (dc != NULL)
- ? cplus_demangle_print_callback (options, dc, callback, opaque)
- : 0;
- }
+ status = (dc != NULL)
+ ? cplus_demangle_print_callback (options, dc, callback, opaque)
+ : 0;
+
+ free (comps);
+ free (subs);
return status;
}
@@ -7169,65 +7174,70 @@ is_ctor_or_dtor (const char *mangled,
{
struct d_info di;
struct demangle_component *dc;
+ struct demangle_component *comps;
+ struct demangle_component **subs;
+ size_t len;
int ret;
*ctor_kind = (enum gnu_v3_ctor_kinds) 0;
*dtor_kind = (enum gnu_v3_dtor_kinds) 0;
- cplus_demangle_init_info (mangled, DMGL_GNU_V3, strlen (mangled), &di);
+ len = strlen (mangled);
- {
-#ifdef CP_DYNAMIC_ARRAYS
- __extension__ struct demangle_component comps[di.num_comps];
- __extension__ struct demangle_component *subs[di.num_subs];
+ /* Allocate on the heap rather than with alloca/VLAs (see comment in
+ d_demangle_callback). */
+ comps = (struct demangle_component *)
+ d_malloc (2 * len * sizeof (*comps));
+ subs = (struct demangle_component **)
+ d_malloc (len * sizeof (*subs));
- di.comps = comps;
- di.subs = subs;
-#else
- di.comps = alloca (di.num_comps * sizeof (*di.comps));
- di.subs = alloca (di.num_subs * sizeof (*di.subs));
-#endif
+ cplus_demangle_init_info (mangled, DMGL_GNU_V3, len, &di);
- dc = cplus_demangle_mangled_name (&di, 1);
+ di.comps = comps;
+ di.subs = subs;
- /* Note that because we did not pass DMGL_PARAMS, we don't expect
- to demangle the entire string. */
+ dc = cplus_demangle_mangled_name (&di, 1);
- ret = 0;
- while (dc != NULL)
- {
- switch (dc->type)
- {
- /* These cannot appear on a constructor or destructor. */
- case DEMANGLE_COMPONENT_RESTRICT_THIS:
- case DEMANGLE_COMPONENT_VOLATILE_THIS:
- case DEMANGLE_COMPONENT_CONST_THIS:
- case DEMANGLE_COMPONENT_REFERENCE_THIS:
- case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
- default:
- dc = NULL;
- break;
- case DEMANGLE_COMPONENT_TYPED_NAME:
- case DEMANGLE_COMPONENT_TEMPLATE:
- dc = d_left (dc);
- break;
- case DEMANGLE_COMPONENT_QUAL_NAME:
- case DEMANGLE_COMPONENT_LOCAL_NAME:
- dc = d_right (dc);
- break;
- case DEMANGLE_COMPONENT_CTOR:
- *ctor_kind = dc->u.s_ctor.kind;
- ret = 1;
- dc = NULL;
- break;
- case DEMANGLE_COMPONENT_DTOR:
- *dtor_kind = dc->u.s_dtor.kind;
- ret = 1;
- dc = NULL;
- break;
- }
- }
- }
+ /* Note that because we did not pass DMGL_PARAMS, we don't expect
+ to demangle the entire string. */
+
+ ret = 0;
+ while (dc != NULL)
+ {
+ switch (dc->type)
+ {
+ /* These cannot appear on a constructor or destructor. */
+ case DEMANGLE_COMPONENT_RESTRICT_THIS:
+ case DEMANGLE_COMPONENT_VOLATILE_THIS:
+ case DEMANGLE_COMPONENT_CONST_THIS:
+ case DEMANGLE_COMPONENT_REFERENCE_THIS:
+ case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
+ default:
+ dc = NULL;
+ break;
+ case DEMANGLE_COMPONENT_TYPED_NAME:
+ case DEMANGLE_COMPONENT_TEMPLATE:
+ dc = d_left (dc);
+ break;
+ case DEMANGLE_COMPONENT_QUAL_NAME:
+ case DEMANGLE_COMPONENT_LOCAL_NAME:
+ dc = d_right (dc);
+ break;
+ case DEMANGLE_COMPONENT_CTOR:
+ *ctor_kind = dc->u.s_ctor.kind;
+ ret = 1;
+ dc = NULL;
+ break;
+ case DEMANGLE_COMPONENT_DTOR:
+ *dtor_kind = dc->u.s_dtor.kind;
+ ret = 1;
+ dc = NULL;
+ break;
+ }
+ }
+
+ free (comps);
+ free (subs);
return ret;
}
--
2.55.0