@@ -28,6 +28,20 @@ struct seh_seg_list {
char *seg_name;
};
+struct unwind_code_pack_info {
+ const char* directive;
+ int offset_bits;
+ int reg_bits;
+ int code_bits;
+ int code;
+ int offset_right_shift;
+ int offset;
+ int reg_right_shift;
+ int reg_offset;
+ int type;
+ int size;
+};
+
/* Local data. */
static seh_context *seh_ctx_cur = NULL;
@@ -36,6 +50,51 @@ static htab_t seh_hash;
static struct seh_seg_list *x_segcur = NULL;
static struct seh_seg_list *p_segcur = NULL;
+const struct unwind_code_pack_info unwind_code_pack_infos[] = {
+ {NULL, 5, 0, 3, ARM64_UNOP_ALLOCS, 4, 0, 0, 0,
+ alloc_s, 1},
+ {NULL, 11, 0, 5, ARM64_UNOP_ALLOCM, 4, 0, 0, 0,
+ alloc_m, 2},
+ {NULL, 24, 0, 8, ARM64_UNOP_ALLOCL, 4, 0, 0, 0,
+ alloc_l, 4},
+ {".seh_save_reg", 6, 4, 6, ARM64_UNOP_SAVEREG, 3, 0, 0, 19,
+ save_reg, 2},
+ {".seh_save_reg_x", 5, 4, 7, ARM64_UNOP_SAVEREGX, 3, 1, 0, 19,
+ save_reg_x, 2},
+ {".seh_save_regp", 6, 4, 6, ARM64_UNOP_SAVEREGP, 3, 0, 0, 19,
+ save_regp, 2},
+ {".seh_save_regp_x", 6, 4, 6, ARM64_UNOP_SAVEREGPX, 3, 1, 0, 19,
+ save_regp_x, 2},
+ {".seh_save_lrpair", 6, 3, 7, ARM64_UNOP_SAVELRPAIR, 3, 0, 1, 19,
+ save_lrpair, 2},
+ {".seh_save_fregp", 6, 3, 7, ARM64_UNOP_SAVEFREGP, 3, 0, 0, 8,
+ save_fregp, 2},
+ {".seh_save_fregp_x", 6, 3, 7, ARM64_UNOP_SAVEFREGPX, 3, 1, 0, 8,
+ save_fregp_x, 2},
+ {".seh_save_freg", 6, 3, 7, ARM64_UNOP_SAVEFREG, 3, 0, 0, 8,
+ save_freg, 2},
+ {".seh_save_freg_x", 5, 3, 8, ARM64_UNOP_SAVEFREGX, 3, 1, 0, 8,
+ save_freg_x, 2},
+ {".seh_save_fplr", 6, 0, 2, ARM64_UNOP_SAVEFPLR, 3, 0, 0, 0,
+ save_fplr, 1},
+ {".seh_save_fplr_x", 6, 0, 6, ARM64_UNOP_SAVEFPLRX, 3, 1, 0, 0,
+ save_fplr_x, 1},
+ {".seh_save_r19r20_x", 5, 0, 3, ARM64_UNOP_SAVER19R20X, 3, 0, 0, 0,
+ save_r19r20_x, 1},
+ {".seh_add_fp", 8, 0, 8, ARM64_UNOP_ADDFP, 0, 0, 0, 0,
+ add_fp, 2},
+ {".seh_set_fp", 0, 0, 8, ARM64_UNOP_SETFP, 0, 0, 0, 0,
+ set_fp, 1},
+ {".seh_save_next", 0, 0, 8, ARM64_UNOP_SAVENEXT, 0, 0, 0, 0,
+ save_next, 1},
+ {".seh_nop", 0, 0, 8, ARM64_UNOP_NOP, 0, 0, 0, 0,
+ nop, 1},
+ {".seh_pac_sign_lr", 0, 0, 8, ARM64_UNOP_PACSIGNLR, 0, 0, 0, 0,
+ pac_sign_lr, 1},
+ {NULL, 0, 0, 8, ARM64_UNOP_END, 0, 0, 0, 0,
+ end, 1},
+};
+
static void write_function_xdata (seh_context *);
static void write_function_pdata (seh_context *);
@@ -200,6 +259,8 @@ seh_get_target_kind (void)
switch (bfd_get_arch (stdoutput))
{
case bfd_arch_aarch64:
+ return seh_kind_arm64;
+
case bfd_arch_arm:
case bfd_arch_powerpc:
case bfd_arch_sh:
@@ -270,6 +331,32 @@ verify_context_and_target (const char *directive, seh_kind target)
return verify_context (directive);
}
+/* Verify mulitple targets. */
+
+static int
+verify_context_and_targets (const char *directive, const seh_kind targets[],
+ int count_targets)
+{
+ bool match = false;
+ for (int i = 0; i < count_targets; ++i)
+ {
+ if (seh_get_target_kind () == targets[i])
+ {
+ match = true;
+ break;
+ }
+ }
+
+ if (!match)
+ {
+ as_warn (_("%s ignored for this target"), directive);
+ ignore_rest_of_line ();
+ return 0;
+ }
+
+ return verify_context (directive);
+}
+
/* Skip whitespace and a comma. Error if the comma is not seen. */
static int
@@ -362,6 +449,10 @@ obj_coff_seh_handler (int what ATTRIBUTE_UNUSED)
else
expression (&seh_ctx_cur->handler);
+ const seh_kind target_kind = seh_get_target_kind ();
+ if (target_kind == seh_kind_arm64)
+ seh_ctx_cur->arm64_ctx.xdata_header.x = 1;
+
seh_ctx_cur->handler_data.X_op = O_constant;
seh_ctx_cur->handler_data.X_add_number = 0;
seh_ctx_cur->handler_flags = 0;
@@ -369,7 +460,7 @@ obj_coff_seh_handler (int what ATTRIBUTE_UNUSED)
if (!skip_whitespace_and_comma (0))
return;
- if (seh_get_target_kind () == seh_kind_x64)
+ if (target_kind == seh_kind_x64 || target_kind == seh_kind_arm64)
{
do
{
@@ -401,13 +492,65 @@ 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))
+ const seh_kind targets[] = { seh_kind_x64, seh_kind_arm64 };
+ if (!verify_context_and_targets (".seh_handlerdata", targets,
+ sizeof (targets) / sizeof (seh_kind)))
return;
demand_empty_rest_of_line ();
switch_xdata (seh_ctx_cur->subsection + 1, seh_ctx_cur->code_seg);
}
+/* Obtain available unwind element. */
+
+static void
+seh_arm64_add_unwind_element (seh_arm64_unwind_types unwind_type, int offset,
+ int reg)
+{
+ if (seh_ctx_cur == NULL
+ || seh_ctx_cur->arm64_ctx.unwind_codes_count >= ARM64_MAX_UNWIND_CODES)
+ {
+ as_warn (_("no unwind element available."));
+ return;
+ }
+
+ seh_arm64_unwind_code *arm64_element = seh_ctx_cur->arm64_ctx.unwind_codes
+ + seh_ctx_cur->arm64_ctx.unwind_codes_count++;
+ const struct unwind_code_pack_info *unwind_code_pack_info;
+ unwind_code_pack_info = unwind_code_pack_infos + unwind_type;
+ arm64_element->value = 0;
+ int value_offset_bits = 0;
+
+ 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;
+ arm64_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;
+ arm64_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;
+ code &= (1 << unwind_code_pack_info->code_bits) - 1;
+ arm64_element->value |= code << value_offset_bits;
+ }
+
+ arm64_element->type = unwind_code_pack_info->type;
+ seh_ctx_cur->arm64_ctx.unwind_codes_byte_count += unwind_code_pack_info->size;
+}
+
+
/* Mark end of current context. */
static void
@@ -465,11 +608,18 @@ obj_coff_seh_proc (int what ATTRIBUTE_UNUSED)
seh_ctx_cur->code_seg = now_seg;
- if (seh_get_target_kind () == seh_kind_x64)
+ seh_kind kind = seh_get_target_kind ();
+ if (kind == seh_kind_x64 || kind == seh_kind_arm64)
{
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 (kind == seh_kind_arm64)
+ {
+ seh_ctx_cur->arm64_ctx.unwind_codes_count = 0;
+ seh_ctx_cur->arm64_ctx.epilogue_scopes_count = 0;
+ }
}
SKIP_WHITESPACE ();
@@ -498,6 +648,23 @@ 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 (seh_get_target_kind () == seh_kind_arm64)
+ {
+ const int n = seh_ctx_cur->arm64_ctx.unwind_codes_count;
+
+ /* Unwind codes need to be reversed. */
+ for (int i = 0; i < n / 2; ++i)
+ {
+ seh_arm64_unwind_code *unwind_codes;
+ unwind_codes = seh_ctx_cur->arm64_ctx.unwind_codes;
+ seh_arm64_unwind_code temp = unwind_codes[i];
+ unwind_codes[i] = unwind_codes[n-i-1];
+ unwind_codes[n-i-1] = temp;
+ }
+
+ seh_arm64_add_unwind_element (end, 0, 0);
+ }
}
/* End-of-file hook. */
@@ -691,34 +858,66 @@ obj_coff_seh_stackalloc (int what ATTRIBUTE_UNUSED)
offsetT off;
int code, info;
- if (!verify_context_and_target (".seh_stackalloc", seh_kind_x64)
+ const seh_kind targets[] = { seh_kind_x64, seh_kind_arm64 };
+ if (!verify_context_and_targets (".seh_stackalloc", targets,
+ sizeof (targets) / sizeof (seh_kind))
|| !seh_validate_seg (".seh_stackalloc"))
return;
off = get_absolute_expression ();
demand_empty_rest_of_line ();
- if (off == 0)
- return;
- if (off < 0)
- {
- as_bad (_(".seh_stackalloc offset is negative"));
- return;
- }
+ switch (seh_get_target_kind ())
+ {
+ case seh_kind_x64:
+ if (off == 0)
+ return;
+ if (off < 0)
+ {
+ as_bad (_(".seh_stackalloc offset is negative"));
+ return;
+ }
- if ((off & 7) == 0 && off <= 128)
- code = UWOP_ALLOC_SMALL, info = (off - 8) >> 3, off = 0;
- else if ((off & 7) == 0 && off <= (offsetT) (0xffff * 8))
- code = UWOP_ALLOC_LARGE, info = 0, off >>= 3;
- else if (off <= (offsetT) 0xffffffff)
- code = UWOP_ALLOC_LARGE, info = 1;
- else
- {
- as_bad (_(".seh_stackalloc offset out of range"));
- return;
- }
+ if ((off & 7) == 0 && off <= 128)
+ code = UWOP_ALLOC_SMALL, info = (off - 8) >> 3, off = 0;
+ else if ((off & 7) == 0 && off <= (offsetT) (0xffff * 8))
+ code = UWOP_ALLOC_LARGE, info = 0, off >>= 3;
+ else if (off <= (offsetT) 0xffffffff)
+ code = UWOP_ALLOC_LARGE, info = 1;
+ else
+ {
+ as_bad (_(".seh_stackalloc offset out of range"));
+ return;
+ }
- seh_x64_make_prologue_element (code, info, off);
+ seh_x64_make_prologue_element (code, info, off);
+ break;
+
+ case seh_kind_arm64:
+ /* arm64 offset should be encoded in multiples of sixteen. */
+ if ((off & 0xf) != 0)
+ {
+ as_bad (_(".seh_stackalloc offset < 16-byte stack alignment"));
+ return;
+ }
+
+ if (off < 0x200)
+ seh_arm64_add_unwind_element (alloc_s, off, 0);
+ else if (off < 0x8000)
+ seh_arm64_add_unwind_element (alloc_m, off, 0);
+ else if (off < 0x10000000)
+ seh_arm64_add_unwind_element (alloc_l, off, 0);
+ else
+ {
+ as_bad (_(".seh_stackalloc offset out of range"));
+ return;
+ }
+ break;
+
+ default:
+ as_bad (_(".seh_stackalloc invalid target"));
+ return;
+ }
}
/* Add a frame-pointer token to current context. */