[PATCH v2 2/2] Enable arch-specific CFI directives and DWARF instructions only when required by the target (part 1)
Matthieu Longo
matthieu.longo@arm.com
Fri Apr 25 11:04:09 GMT 2025
DWARF 5 standard provides a space for vendor-specific DWARF directives.
Binutils currently defines GNU-specific DWARF directives and vendor-specific
ones for AArch64, SGI/MIPS, and Sparc architectures.
The architecture specific code is not guarded. This has caused confusion in
the past, and .cfi_window_save (a Sparc-specific directive) was used on
AArch64 in place of .cfi_negate_ra_state, the defined directive by the "DWARF
extensions on AArch64" document (see [1]).
Gas only supports one target architecture (the one selected with --target),
whereas others tools like readelf, objdump, etc. deduce the target
architecture from the input object at runtime.
In bfd, the architecture of the input object is detected at runtime using
bfd_get_arch, and only the architecture-specific instructions matching the
target of the object are considered during the processing.
In gas, the architecture-specific code is surrounded by TC_<arch> defines
to enable/disable DWARF directives at compilation time.
This patch deliberately excluded from the scope binutils/dwarf.c that also
requires a check of the architecture at runtime. This part will be addressed
in a follow-up patch.
[1]: https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst#id2
---
bfd/elf-eh-frame.c | 93 ++++++++++++++++++++++++++++++++++++++++------
gas/dw2gencfi.c | 33 +++++++++++++---
gas/gen-sframe.c | 35 ++++-------------
include/dwarf2.def | 17 ++++++---
4 files changed, 128 insertions(+), 50 deletions(-)
diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c
index 78b3ecb5a7d..7efc9b1c80e 100644
--- a/bfd/elf-eh-frame.c
+++ b/bfd/elf-eh-frame.c
@@ -338,12 +338,82 @@ next_cie_fde_offset (const struct eh_cie_fde *ent,
return sec->size;
}
+static bool
+skip_cfa_op_aarch64 (bfd_byte op,
+ bfd_byte **iter ATTRIBUTE_UNUSED,
+ bfd_byte *end ATTRIBUTE_UNUSED)
+{
+ switch (op)
+ {
+ case DW_CFA_AARCH64_negate_ra_state:
+ case DW_CFA_AARCH64_negate_ra_state_with_pc:
+ /* No arguments. */
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool
+skip_cfa_op_mips (bfd_byte op,
+ bfd_byte **iter,
+ bfd_byte *end)
+{
+ switch (op)
+ {
+ case DW_CFA_MIPS_advance_loc8:
+ return skip_bytes (iter, end, 8);
+
+ default:
+ return false;
+ }
+}
+
+static bool
+skip_cfa_op_sparc (bfd_byte op,
+ bfd_byte **iter ATTRIBUTE_UNUSED,
+ bfd_byte *end ATTRIBUTE_UNUSED)
+{
+ switch (op)
+ {
+ case DW_CFA_GNU_window_save:
+ /* No arguments. */
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool
+skip_cfa_op_arch_specific (enum bfd_architecture arch,
+ bfd_byte **iter, bfd_byte *end,
+ bfd_byte op)
+{
+ switch (arch)
+ {
+ case bfd_arch_aarch64:
+ return skip_cfa_op_aarch64 (op, iter, end);
+
+ case bfd_arch_mips:
+ return skip_cfa_op_mips (op, iter, end);
+
+ case bfd_arch_sparc:
+ return skip_cfa_op_sparc (op, iter, end);
+
+ default:
+ return false;
+ }
+}
+
/* Assume that the bytes between *ITER and END are CFA instructions.
Try to move *ITER past the first instruction and return true on
success. ENCODED_PTR_WIDTH gives the width of pointer entries. */
static bool
-skip_cfa_op (bfd_byte **iter, bfd_byte *end, unsigned int encoded_ptr_width)
+skip_cfa_op (enum bfd_architecture arch, bfd_byte **iter, bfd_byte *end,
+ unsigned int encoded_ptr_width)
{
bfd_byte op = 0;
bfd_vma length;
@@ -351,15 +421,14 @@ skip_cfa_op (bfd_byte **iter, bfd_byte *end, unsigned int encoded_ptr_width)
if (!read_byte (iter, end, &op))
return false;
- switch (op & 0xc0 ? op & 0xc0 : op)
+ op = op & 0xc0 ? op & 0xc0 : op;
+ switch (op)
{
case DW_CFA_nop:
case DW_CFA_advance_loc:
case DW_CFA_restore:
case DW_CFA_remember_state:
case DW_CFA_restore_state:
- case DW_CFA_GNU_window_save:
- case DW_CFA_AARCH64_negate_ra_state_with_pc:
/* No arguments. */
return true;
@@ -410,10 +479,9 @@ skip_cfa_op (bfd_byte **iter, bfd_byte *end, unsigned int encoded_ptr_width)
case DW_CFA_advance_loc4:
return skip_bytes (iter, end, 4);
- case DW_CFA_MIPS_advance_loc8:
- return skip_bytes (iter, end, 8);
-
default:
+ if (op >= DW_CFA_lo_user && op <= DW_CFA_hi_user)
+ return skip_cfa_op_arch_specific (arch, iter, end, op);
return false;
}
}
@@ -424,8 +492,8 @@ skip_cfa_op (bfd_byte **iter, bfd_byte *end, unsigned int encoded_ptr_width)
ENCODED_PTR_WIDTH is as for skip_cfa_op. */
static bfd_byte *
-skip_non_nops (bfd_byte *buf, bfd_byte *end, unsigned int encoded_ptr_width,
- unsigned int *set_loc_count)
+skip_non_nops (enum bfd_architecture arch, bfd_byte *buf, bfd_byte *end,
+ unsigned int encoded_ptr_width, unsigned int *set_loc_count)
{
bfd_byte *last;
@@ -437,7 +505,7 @@ skip_non_nops (bfd_byte *buf, bfd_byte *end, unsigned int encoded_ptr_width,
{
if (*buf == DW_CFA_set_loc)
++*set_loc_count;
- if (!skip_cfa_op (&buf, end, encoded_ptr_width))
+ if (!skip_cfa_op (arch, &buf, end, encoded_ptr_width))
return 0;
last = buf;
}
@@ -994,7 +1062,8 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
include the padding. */
length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size);
set_loc_count = 0;
- insns_end = skip_non_nops (insns, end, length, &set_loc_count);
+ insns_end = skip_non_nops (bfd_get_arch (abfd), insns, end, length,
+ &set_loc_count);
/* If we don't understand the CFA instructions, we can't know
what needs to be adjusted there. */
if (insns_end == NULL
@@ -1025,7 +1094,7 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
{
if (*p == DW_CFA_set_loc)
this_inf->set_loc[++cnt] = p + 1 - start;
- REQUIRE (skip_cfa_op (&p, end, length));
+ REQUIRE (skip_cfa_op (bfd_get_arch (abfd), &p, end, length));
}
}
diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index a21ae322488..d025da35676 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -720,9 +720,16 @@ const pseudo_typeS cfi_pseudo_table[] =
{ "cfi_same_value", dot_cfi, DW_CFA_same_value },
{ "cfi_remember_state", dot_cfi, DW_CFA_remember_state },
{ "cfi_restore_state", dot_cfi, DW_CFA_restore_state },
- { "cfi_window_save", dot_cfi, DW_CFA_GNU_window_save },
+#if TC_AARCH64
+ /* cfi_window_save is an alias of cfi_negate_ra_state which is kept for
+ backward-compatibility concerns. */
+ { "cfi_window_save", dot_cfi, DW_CFA_AARCH64_negate_ra_state },
{ "cfi_negate_ra_state", dot_cfi, DW_CFA_AARCH64_negate_ra_state },
{ "cfi_negate_ra_state_with_pc", dot_cfi, DW_CFA_AARCH64_negate_ra_state_with_pc },
+#endif /* TC_AARCH64 */
+#if TC_SPARC
+ { "cfi_window_save", dot_cfi, DW_CFA_GNU_window_save },
+#endif /* TC_SPARC */
{ "cfi_escape", dot_cfi_escape, 0 },
{ "cfi_signal_frame", dot_cfi, CFI_signal_frame },
{ "cfi_personality", dot_cfi_personality, 0 },
@@ -919,13 +926,21 @@ dot_cfi (int arg)
cfi_add_CFA_restore_state ();
break;
- case DW_CFA_GNU_window_save:
- cfi_add_CFA_insn (DW_CFA_GNU_window_save);
+#if TC_AARCH64
+ case DW_CFA_AARCH64_negate_ra_state:
+ cfi_add_CFA_insn (DW_CFA_AARCH64_negate_ra_state);
break;
case DW_CFA_AARCH64_negate_ra_state_with_pc:
cfi_add_CFA_insn (DW_CFA_AARCH64_negate_ra_state_with_pc);
break;
+#endif /* TC_AARCH64 */
+
+#if TC_SPARC
+ case DW_CFA_GNU_window_save:
+ cfi_add_CFA_insn (DW_CFA_GNU_window_save);
+ break;
+#endif /* TC_SPARC */
case CFI_signal_frame:
frchain_now->frch_cfi_data->cur_fde_data->signal_frame = 1;
@@ -1824,13 +1839,21 @@ output_cfi_insn (struct cfi_insn_data *insn)
out_one (insn->insn);
break;
- case DW_CFA_GNU_window_save:
- out_one (DW_CFA_GNU_window_save);
+#if TC_AARCH64
+ case DW_CFA_AARCH64_negate_ra_state:
+ out_one (DW_CFA_AARCH64_negate_ra_state);
break;
case DW_CFA_AARCH64_negate_ra_state_with_pc:
out_one (DW_CFA_AARCH64_negate_ra_state_with_pc);
break;
+#endif /* TC_AARCH64 */
+
+#if TC_SPARC
+ case DW_CFA_GNU_window_save:
+ out_one (DW_CFA_GNU_window_save);
+ break;
+#endif /* TC_SPARC */
case CFI_escape:
{
diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
index 5b5e7b14b3a..3a5ecda47de 100644
--- a/gas/gen-sframe.c
+++ b/gas/gen-sframe.c
@@ -1257,6 +1257,8 @@ sframe_xlate_do_restore (struct sframe_xlate_ctx *xlate_ctx,
return SFRAME_XLATE_OK;
}
+#if TC_AARCH64
+
/* Translate DW_CFA_AARCH64_negate_ra_state into SFrame context.
Return SFRAME_XLATE_OK if success. */
@@ -1287,30 +1289,7 @@ sframe_xlate_do_aarch64_negate_ra_state_with_pc (struct sframe_xlate_ctx *xlate_
return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */
}
-/* Translate DW_CFA_GNU_window_save into SFrame context.
- DW_CFA_GNU_window_save is a DWARF Sparc extension, but is multiplexed with a
- directive of DWARF AArch64 extension: DW_CFA_AARCH64_negate_ra_state.
- The AArch64 backend of GCC 14 and older versions was emitting mistakenly the
- Sparc CFI directive (.cfi_window_save). From GCC 15, the AArch64 backend
- only emits .cfi_negate_ra_state. For backward compatibility, the handler for
- .cfi_window_save needs to check whether the directive was used in a AArch64
- ABI context or not.
- Return SFRAME_XLATE_OK if success. */
-
-static int
-sframe_xlate_do_gnu_window_save (struct sframe_xlate_ctx *xlate_ctx,
- struct cfi_insn_data *cfi_insn)
-{
- unsigned char abi_arch = sframe_get_abi_arch ();
-
- /* Translate DW_CFA_AARCH64_negate_ra_state into SFrame context. */
- if (abi_arch == SFRAME_ABI_AARCH64_ENDIAN_BIG
- || abi_arch == SFRAME_ABI_AARCH64_ENDIAN_LITTLE)
- return sframe_xlate_do_aarch64_negate_ra_state (xlate_ctx, cfi_insn);
-
- as_warn (_("skipping SFrame FDE; .cfi_window_save"));
- return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */
-}
+#endif /* TC_AARCH64 */
/* Handle DW_CFA_expression in .cfi_escape.
@@ -1622,14 +1601,14 @@ sframe_do_cfi_insn (struct sframe_xlate_ctx *xlate_ctx,
case DW_CFA_restore:
err = sframe_xlate_do_restore (xlate_ctx, cfi_insn);
break;
- /* DW_CFA_AARCH64_negate_ra_state is multiplexed with
- DW_CFA_GNU_window_save. */
- case DW_CFA_GNU_window_save:
- err = sframe_xlate_do_gnu_window_save (xlate_ctx, cfi_insn);
+#if TC_AARCH64
+ case DW_CFA_AARCH64_negate_ra_state:
+ err = sframe_xlate_do_aarch64_negate_ra_state (xlate_ctx, cfi_insn);
break;
case DW_CFA_AARCH64_negate_ra_state_with_pc:
err = sframe_xlate_do_aarch64_negate_ra_state_with_pc (xlate_ctx, cfi_insn);
break;
+#endif /* TC_AARCH64 */
case DW_CFA_register:
err = sframe_xlate_do_register (xlate_ctx, cfi_insn);
break;
diff --git a/include/dwarf2.def b/include/dwarf2.def
index 1a50e19d8c9..f2c464d09b0 100644
--- a/include/dwarf2.def
+++ b/include/dwarf2.def
@@ -780,17 +780,24 @@ DW_CFA (DW_CFA_val_offset, 0x14)
DW_CFA (DW_CFA_val_offset_sf, 0x15)
DW_CFA (DW_CFA_val_expression, 0x16)
+/* Users extensions.
+ Note: DW_CFA_lo_user and DW_CFA_hi_user can be aliased to used as extension.
+ This is a closed interval. */
DW_CFA (DW_CFA_lo_user, 0x1c)
DW_CFA (DW_CFA_hi_user, 0x3f)
-/* SGI/MIPS specific. */
+/* SGI/MIPS specific extensions. */
DW_CFA (DW_CFA_MIPS_advance_loc8, 0x1d)
-/* AArch64 extensions. */
-DW_CFA (DW_CFA_AARCH64_negate_ra_state_with_pc, 0x2c)
-/* GNU extensions.
- NOTE: DW_CFA_GNU_window_save is multiplexed on Sparc and AArch64. */
+
+/* Sparc specific extensions. */
+/* NOTE: DW_CFA_GNU_window_save is multiplexed on Sparc and AArch64. */
DW_CFA (DW_CFA_GNU_window_save, 0x2d)
+
+/* AArch64 specific extensions. */
+DW_CFA (DW_CFA_AARCH64_negate_ra_state_with_pc, 0x2c)
DW_CFA_DUP (DW_CFA_AARCH64_negate_ra_state, 0x2d)
+
+/* GNU extensions. */
DW_CFA (DW_CFA_GNU_args_size, 0x2e)
DW_CFA (DW_CFA_GNU_negative_offset_extended, 0x2f)
--
2.49.0
More information about the Binutils
mailing list