[RFC,V2,4/5] ld/aarch64elf: add support for DT_AARCH64_MEMTAG_MODE dynamic tag

Message ID 20250411041635.1984719-5-indu.bhagat@oracle.com
State New
Headers
Series Add support for MTE stack tagging |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm warning Skipped upon request
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 warning Skipped upon request

Commit Message

Indu Bhagat April 11, 2025, 4:16 a.m. UTC
  Add new command line option -z memtag-mode=<mode> to aarch64 elf,
where <mode> can be one of none, sync, or async.  For mode of sync or
async, a DT_AARCH64_MEMTAG_MODE dynamic tag with a value of 0 or 1
respectively is emitted.

readelf displays the dynamic tag when present:

$ readelf -d <exectutable>
Dynamic section at offset 0xfdd8 contains XX entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x400520
 0x000000000000000d (FINI)               0x400b64
 0x0000000000000019 (INIT_ARRAY)         0x41fdc8
 ...                 ...                 ...
 0x0000000070000009 (AARCH64_MEMTAG_MODE) 0x1
 ...                 ...                 ...

[Note/FIXME: Asymm is an Armv8.7 extension to MTE; it is a mode of
operation that provides synchronous checking on memory reads, and
asynchronous checking of memory writes.  Its not clear if and how the
toolchain (likely the compiler?) can do some sanity checking of whether
asymm is allowed.  E.g., asymm should be disallowed with Armv8.5a ?

Further the MemtagABI does not yet define a constant for the asymm mode
value for DT_AARCH64_MEMTAG_MODE.]

TBD:
  1. test cases and testing
  2. Why does the ABI doc not define a constant for asymm ?  The patch
     skips adding support for asymm altogether ATM.

ChangeLog:

        * bfd/elfnn-aarch64.c (struct elf_aarch64_link_hash_table): Add
	new member for memtag properties.
        (bfd_elfNN_aarch64_set_options): New argument to pass memtag
	properties.
	(elfNN_aarch64_late_size_sections): Emit DT_AARCH64_MEMTAG_MODE
	dynamic tag.
        * bfd/elfxx-aarch64.h: New definition for the various memtag
	properties.
        * binutils/readelf.c (get_aarch64_dynamic_type): Handle
	DT_AARCH64_MEMTAG_MODE.
        * ld/emultempl/aarch64elf.em: Likewise.
        * ld/ld.texi: Add documentation for the new option
	-z memtag-mode.
        * ld/testsuite/ld-aarch64/aarch64-elf.exp: New test.
        * ld/testsuite/ld-aarch64/dt-memtag.d: New test.
        * ld/testsuite/ld-aarch64/dt-memtag-mode.s: New test.

include/ChangeLog:

        * elf/aarch64.h (DT_AARCH64_MEMTAG_MODE): New definition.
---
 bfd/elfnn-aarch64.c                      | 15 ++++++++-
 bfd/elfxx-aarch64.h                      | 24 ++++++++++++--
 binutils/readelf.c                       |  2 ++
 include/elf/aarch64.h                    |  1 +
 ld/emultempl/aarch64elf.em               | 42 +++++++++++++++++++++++-
 ld/ld.texi                               |  9 +++++
 ld/testsuite/ld-aarch64/aarch64-elf.exp  |  1 +
 ld/testsuite/ld-aarch64/dt-memtag-mode.d |  7 ++++
 ld/testsuite/ld-aarch64/dt-memtag.s      |  7 ++++
 9 files changed, 104 insertions(+), 4 deletions(-)
 create mode 100644 ld/testsuite/ld-aarch64/dt-memtag-mode.d
 create mode 100644 ld/testsuite/ld-aarch64/dt-memtag.s
  

Patch

diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
index 548da1f8b30..9a4d15b4ce3 100644
--- a/bfd/elfnn-aarch64.c
+++ b/bfd/elfnn-aarch64.c
@@ -2619,6 +2619,9 @@  struct elf_aarch64_link_hash_table
   /* Don't apply link-time values for dynamic relocations.  */
   int no_apply_dynamic_relocs;
 
+  /* Memtag Extension mode of operation.  */
+  aarch64_memtag_opts memtag_opts;
+
   /* The number of bytes in the initial entry in the PLT.  */
   bfd_size_type plt_header_size;
 
@@ -5006,13 +5009,15 @@  bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
 			       int fix_erratum_835769,
 			       erratum_84319_opts fix_erratum_843419,
 			       int no_apply_dynamic_relocs,
-			       const aarch64_protection_opts *sw_protections)
+			       const aarch64_protection_opts *sw_protections,
+			       const aarch64_memtag_opts *memtag_opts)
 {
   struct elf_aarch64_link_hash_table *globals;
 
   globals = elf_aarch64_hash_table (link_info);
   globals->pic_veneer = pic_veneer;
   globals->fix_erratum_835769 = fix_erratum_835769;
+  globals->memtag_opts = *memtag_opts;
   /* If the default options are used, then ERRAT_ADR will be set by default
      which will enable the ADRP->ADR workaround for the erratum 843419
      workaround.  */
@@ -9772,7 +9777,15 @@  elfNN_aarch64_late_size_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 		   && !add_dynamic_entry (DT_AARCH64_PAC_PLT, 0))
 	    return false;
 	}
+
+      /* FIXME - what about asymm ? */
+      if (is_aarch64_elf (output_bfd)
+	  && htab->memtag_opts.memtag_mode != AARCH64_MEMTAG_MODE_NONE
+	  && !add_dynamic_entry (DT_AARCH64_MEMTAG_MODE,
+				 htab->memtag_opts.memtag_mode == AARCH64_MEMTAG_MODE_ASYNC))
+	return false;
     }
+
 #undef add_dynamic_entry
 
   return true;
diff --git a/bfd/elfxx-aarch64.h b/bfd/elfxx-aarch64.h
index 506f4a93c9d..64ab39edb71 100644
--- a/bfd/elfxx-aarch64.h
+++ b/bfd/elfxx-aarch64.h
@@ -129,13 +129,33 @@  typedef enum
   ERRAT_ADRP  = (1 << 2),  /* Erratum workarounds using ADRP are allowed.  */
 } erratum_84319_opts;
 
+/* An enum to define the various modes of MTE operation.
+   The enumerator constants must be the same as specified in the Memtag ABI
+   Extension to ELF for the Arm® 64-bit Architecture (AArch64) document.  */
+typedef enum
+{
+  AARCH64_MEMTAG_MODE_SYNC    = 0,
+  AARCH64_MEMTAG_MODE_ASYNC   = 1,
+  AARCH64_MEMTAG_MODE_NONE    = 2,
+} aarch64_memtag_mode_type;
+
+/* A structure to encompass all information about memtag feature related
+   command line options.  */
+struct aarch64_memtag_opts
+{
+  /* Mode of MTE operation.  */
+  aarch64_memtag_mode_type memtag_mode;
+};
+
+typedef struct aarch64_memtag_opts aarch64_memtag_opts;
+
 extern void bfd_elf64_aarch64_set_options
   (bfd *, struct bfd_link_info *, int, int, int, int, erratum_84319_opts, int,
-   const aarch64_protection_opts *);
+   const aarch64_protection_opts *, const aarch64_memtag_opts *);
 
 extern void bfd_elf32_aarch64_set_options
   (bfd *, struct bfd_link_info *, int, int, int, int, erratum_84319_opts, int,
-   const aarch64_protection_opts *);
+   const aarch64_protection_opts *, const aarch64_memtag_opts *);
 
 /* AArch64 stub generation support for ELF64.  Called from the linker.  */
 extern int elf64_aarch64_setup_section_lists
diff --git a/binutils/readelf.c b/binutils/readelf.c
index dd1871d8c75..1a74588087c 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -2587,6 +2587,8 @@  get_aarch64_dynamic_type (unsigned long type)
     case DT_AARCH64_BTI_PLT:  return "AARCH64_BTI_PLT";
     case DT_AARCH64_PAC_PLT:  return "AARCH64_PAC_PLT";
     case DT_AARCH64_VARIANT_PCS:  return "AARCH64_VARIANT_PCS";
+    case DT_AARCH64_MEMTAG_MODE: return "AARCH64_MEMTAG_MODE";
+
     default:
       return NULL;
     }
diff --git a/include/elf/aarch64.h b/include/elf/aarch64.h
index e218e07fa73..129aef9a524 100644
--- a/include/elf/aarch64.h
+++ b/include/elf/aarch64.h
@@ -52,6 +52,7 @@ 
 #define DT_AARCH64_BTI_PLT	(DT_LOPROC + 1)
 #define DT_AARCH64_PAC_PLT	(DT_LOPROC + 3)
 #define DT_AARCH64_VARIANT_PCS	(DT_LOPROC + 5)
+#define DT_AARCH64_MEMTAG_MODE  (DT_LOPROC + 9)
 
 /* AArch64-specific values for st_other.  */
 #define STO_AARCH64_VARIANT_PCS	0x80  /* Symbol may follow different call
diff --git a/ld/emultempl/aarch64elf.em b/ld/emultempl/aarch64elf.em
index afa91afb608..dcf314c1a21 100644
--- a/ld/emultempl/aarch64elf.em
+++ b/ld/emultempl/aarch64elf.em
@@ -41,6 +41,9 @@  static aarch64_protection_opts sw_protections = {
   .gcs_report = MARKING_WARN,
   .gcs_report_dynamic = MARKING_UNSET,
 };
+static aarch64_memtag_opts memtag_opts = {
+  .memtag_mode = AARCH64_MEMTAG_MODE_NONE,
+};
 
 #define COMPILE_TIME_STRLEN(s) \
   (sizeof(s) - 1)
@@ -335,7 +338,8 @@  aarch64_elf_create_output_section_statements (void)
 				 pic_veneer,
 				 fix_erratum_835769, fix_erratum_843419,
 				 no_apply_dynamic_relocs,
-				 &sw_protections);
+				 &sw_protections,
+                                 &memtag_opts);
 
   stub_file = lang_add_input_file ("linker stubs",
 				   lang_input_file_is_fake_enum,
@@ -440,6 +444,31 @@  aarch64_parse_gcs_option (const char *_optarg)
   #undef GCS
   #undef GCS_LEN
 }
+
+static bool
+aarch64_parse_memtag_mode_option (const char *optarg)
+{
+  #define MEMTAG_MODE      "memtag-mode"
+  #define MEMTAG_MODE_LEN  COMPILE_TIME_STRLEN (MEMTAG_MODE)
+
+  if (strncmp (optarg, MEMTAG_MODE, MEMTAG_MODE_LEN) != 0)
+    return false;
+
+  if (strcmp (optarg + MEMTAG_MODE_LEN, "=none") == 0)
+    memtag_opts.memtag_mode = AARCH64_MEMTAG_MODE_NONE;
+  else if (strcmp (optarg + MEMTAG_MODE_LEN, "=sync") == 0)
+    memtag_opts.memtag_mode = AARCH64_MEMTAG_MODE_SYNC;
+  else if (strcmp (optarg + MEMTAG_MODE_LEN, "=async") == 0)
+    memtag_opts.memtag_mode = AARCH64_MEMTAG_MODE_ASYNC;
+  else
+    einfo (_("%X%P: error: unrecognized value '-z %s'\n"), optarg);
+
+  return true;
+
+  #undef MEMTAG_MODE
+  #undef MEMTAG_MODE_LEN
+}
+
 EOF
 
 # Define some shell vars to insert bits of code into the standard elf
@@ -518,6 +547,15 @@  PARSE_AND_LIST_OPTIONS='
                                                  and output have GCS marking.\n\
                                                error: Emit error when the input objects are missing GCS markings\n\
                                                  and output have GCS marking.\n"));
+  fprintf (file, _("\
+  -z memtag-mode[=none|sync|async]     Select Memory Tagging Extension mode of operation to use.\n\
+                                       Emits a DT_AARCH64_MEMTAG_MODE dynamic tag for the binary.\n\
+                                       This entry is only valid on the main executable.  It is\n\
+                                       ignored in the dynamically loaded objects by the loader.\n\
+                                         none (default): Disable MTE checking of memory reads and writes.\n\
+                                         sync: Enable precise exceptions when mismatched address and\n\
+                                               allocation tags detected on load/store operations.\n\
+                                         async: Enable imprecise exceptions.\n"));
 '
 
 PARSE_AND_LIST_ARGS_CASE_Z_AARCH64='
@@ -533,6 +571,8 @@  PARSE_AND_LIST_ARGS_CASE_Z_AARCH64='
 	{}
      else if (aarch64_parse_gcs_option (optarg))
 	{}
+     else if (aarch64_parse_memtag_mode_option (optarg))
+        {}
 '
 PARSE_AND_LIST_ARGS_CASE_Z="$PARSE_AND_LIST_ARGS_CASE_Z $PARSE_AND_LIST_ARGS_CASE_Z_AARCH64"
 
diff --git a/ld/ld.texi b/ld/ld.texi
index 29bd0e1bb1e..763dce22af8 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -8371,6 +8371,15 @@  GCS markings.
 If issues are found, a maximum of 20 messages will be emitted, and then a summary
 with the total number of issues will be displayed at the end.
 
+@kindex -z memtag-mode=@var{mode}
+@cindex MTE modes of operation
+The @samp{-z memtag-mode=mode} specifies the MTE mode of operation.  The
+value of @samp{mode} can be one of @samp{none, sync} or @samp{async}.  The
+specified modes determine the value of the @samp{DT_AARCH64_MEMTAG_MODE}
+dynamic tag.  The @samp{sync} mode implies precise exceptions, with the runtime
+providing the exact instruction where the fault occurred, and the exact
+faulting address.  The @samp{async} mode implies imprecise exceptions.
+
 @ifclear GENERIC
 @lowersections
 @end ifclear
diff --git a/ld/testsuite/ld-aarch64/aarch64-elf.exp b/ld/testsuite/ld-aarch64/aarch64-elf.exp
index 2d444e7d1e2..56e68c584fb 100644
--- a/ld/testsuite/ld-aarch64/aarch64-elf.exp
+++ b/ld/testsuite/ld-aarch64/aarch64-elf.exp
@@ -377,6 +377,7 @@  run_dump_test_lp64 "variant_pcs-shared"
 run_dump_test_lp64 "variant_pcs-now"
 
 run_dump_test_lp64 "mte-tagged-frame"
+run_dump_test_lp64 "dt-memtag-mode"
 
 set aarch64elflinktests {
   {"ld-aarch64/so with global symbol" "-shared" "" "" {copy-reloc-so.s}
diff --git a/ld/testsuite/ld-aarch64/dt-memtag-mode.d b/ld/testsuite/ld-aarch64/dt-memtag-mode.d
new file mode 100644
index 00000000000..21670bc20cd
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/dt-memtag-mode.d
@@ -0,0 +1,7 @@ 
+#source: dt-memtag.s
+#ld: -shared -z memtag-mode=async
+#readelf: -d
+
+#...
+ 0x0000000070000009 \(AARCH64_MEMTAG_MODE\) 0x1
+#...
diff --git a/ld/testsuite/ld-aarch64/dt-memtag.s b/ld/testsuite/ld-aarch64/dt-memtag.s
new file mode 100644
index 00000000000..51f3ba5ddd2
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/dt-memtag.s
@@ -0,0 +1,7 @@ 
+// Test DT_AARCH64_MEMTAG_MODE.
+
+.text
+.p2align 3
+.global foo
+foo:
+.xword foo