[RFC, V3 5/5] ld/aarch64elf: add support for DT_AARCH64_MEMTAG_STACK dynamic tag

Indu Bhagat indu.bhagat@oracle.com
Wed Jun 4 23:25:55 GMT 2025


Add new command line option -z memtag-stack for aarch64 elf.  This
option instructs the linker to generate the necessary dynamic tag
DT_AARCH64_MEMTAG_STACK,  which the dynamic loader can then use to
protect the stack memory with PROT_MTE.  Linker issues an 'unrecognized
option' error when -z memtag-stack is specified for non-aarch64 based
emulations.

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
...                 ...                 ...
0x000000007000000c (AARCH64_MEMTAG_STACK) 0x1
...                 ...                 ...

TBD:
1. Error/Warn if user says -z memtag-stack but does not select a
   mode ?
   If selecting a mode by default, is sync a good default?
   Note that in GCC, the proposal (WIP patches upstream) is to do the following:

+   %{%:sanitize(memtag-stack):%{!fsanitize-memtag-mode:-z memtag-stack -z memtag-mode=sync}} \
+   %{%:sanitize(memtag-stack):%{fsanitize-memtag-mode=*:-z memtag-stack -z memtag-mode=%}}"

   This means memtag-mode of sync is selected by the driver, if user
   does not specify one explicitly via GCC option
   -fsanitize-memtag-mode=<none|sync|async>.  The behaviour in GCC is
   similar to the same option in clang.

   Wasnt sure if there is value (or not) in keeping ld behavior
   different.  Hence the TBD.

2. Should ld check that all input ELF components were compiled with
   -fsanitize=memtag-stack ? If yes, we likely need something like
   GNU_PROPERTY_AARCH64_FEATURE_1_MEMTAG_STACK specified in the ABI.
   Should we then allow the user to select the desirable action like we
   do in other cases, via say memtag-report[=none|warning|error] ?

3. Testing and add more testcases based on the resolutions to above two.

ChangeLog:

        * bfd/elfnn-aarch64.c (elfNN_aarch64_late_size_sections): Emit
	DT_AARCH64_MEMTAG_STACK dynamic tag.
        * bfd/elfxx-aarch64.h (struct aarch64_memtag_opts): Add new
	member for tracking whether stack access uses MTE insns.
        * binutils/readelf.c (get_aarch64_dynamic_type): Handle
	DT_AARCH64_MEMTAG_STACK.
        * ld/emultempl/aarch64elf.em: Add new command line option.
        * ld/ld.texi: Add documentation for -z memtag-stack.
        * ld/testsuite/ld-aarch64/aarch64-elf.exp: Add new test.
        * ld/testsuite/ld-aarch64/dt-memtag-stack.d: New test.

include/ChangeLog:

        * elf/aarch64.h (DT_AARCH64_MEMTAG_STACK): New definition.

---
[Changes in V3]
  - Updated the text of the TBD notes.
[End of changes in V3]
---
 bfd/elfnn-aarch64.c                       | 6 ++++++
 bfd/elfxx-aarch64.h                       | 3 +++
 binutils/readelf.c                        | 1 +
 include/elf/aarch64.h                     | 1 +
 ld/emultempl/aarch64elf.em                | 5 +++++
 ld/ld.texi                                | 5 +++++
 ld/testsuite/ld-aarch64/aarch64-elf.exp   | 1 +
 ld/testsuite/ld-aarch64/dt-memtag-stack.d | 7 +++++++
 8 files changed, 29 insertions(+)
 create mode 100644 ld/testsuite/ld-aarch64/dt-memtag-stack.d

diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
index 89f4e26245e..acec4c951ef 100644
--- a/bfd/elfnn-aarch64.c
+++ b/bfd/elfnn-aarch64.c
@@ -9783,6 +9783,12 @@ elfNN_aarch64_late_size_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 	  && !add_dynamic_entry (DT_AARCH64_MEMTAG_MODE,
 				 htab->memtag_opts.memtag_mode == AARCH64_MEMTAG_MODE_ASYNC))
 	return false;
+
+      if (is_aarch64_elf (output_bfd)
+	  && htab->memtag_opts.memtag_stack == 1
+	  && !add_dynamic_entry (DT_AARCH64_MEMTAG_STACK,
+				 htab->memtag_opts.memtag_stack == 1))
+	return false;
     }
 
 #undef add_dynamic_entry
diff --git a/bfd/elfxx-aarch64.h b/bfd/elfxx-aarch64.h
index 66c1f48d450..bd3642cbefe 100644
--- a/bfd/elfxx-aarch64.h
+++ b/bfd/elfxx-aarch64.h
@@ -147,6 +147,9 @@ struct aarch64_memtag_opts
 {
   /* Mode of MTE operation.  */
   aarch64_memtag_mode_type memtag_mode;
+
+  /* Whether stack accesses use MTE insns.  */
+  unsigned int memtag_stack;
 };
 
 typedef struct aarch64_memtag_opts aarch64_memtag_opts;
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 1a74588087c..3a1fbffd135 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -2588,6 +2588,7 @@ get_aarch64_dynamic_type (unsigned long type)
     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";
+    case DT_AARCH64_MEMTAG_STACK: return "AARCH64_MEMTAG_STACK";
 
     default:
       return NULL;
diff --git a/include/elf/aarch64.h b/include/elf/aarch64.h
index 129aef9a524..a4ef1118402 100644
--- a/include/elf/aarch64.h
+++ b/include/elf/aarch64.h
@@ -53,6 +53,7 @@
 #define DT_AARCH64_PAC_PLT	(DT_LOPROC + 3)
 #define DT_AARCH64_VARIANT_PCS	(DT_LOPROC + 5)
 #define DT_AARCH64_MEMTAG_MODE  (DT_LOPROC + 9)
+#define DT_AARCH64_MEMTAG_STACK (DT_LOPROC + 12)
 
 /* 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 dcf314c1a21..a2c20682e6f 100644
--- a/ld/emultempl/aarch64elf.em
+++ b/ld/emultempl/aarch64elf.em
@@ -43,6 +43,7 @@ static aarch64_protection_opts sw_protections = {
 };
 static aarch64_memtag_opts memtag_opts = {
   .memtag_mode = AARCH64_MEMTAG_MODE_NONE,
+  .memtag_stack = 0,
 };
 
 #define COMPILE_TIME_STRLEN(s) \
@@ -556,6 +557,8 @@ PARSE_AND_LIST_OPTIONS='
                                          sync: Enable precise exceptions when mismatched address and\n\
                                                allocation tags detected on load/store operations.\n\
                                          async: Enable imprecise exceptions.\n"));
+  fprintf (file, _("\
+  -z memtag-stack               Mark program stack with MTE protection.\n"));
 '
 
 PARSE_AND_LIST_ARGS_CASE_Z_AARCH64='
@@ -573,6 +576,8 @@ PARSE_AND_LIST_ARGS_CASE_Z_AARCH64='
 	{}
      else if (aarch64_parse_memtag_mode_option (optarg))
         {}
+     else if (strcmp (optarg, "memtag-stack") == 0)
+        memtag_opts.memtag_stack = 1;
 '
 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 f196f1e5870..4460a084404 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -8383,6 +8383,11 @@ 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.
 
+@kindex -z memtag-stack
+@cindex Mark program stack for MTE protection
+The @samp{-z memtag-stack} specifies that output object uses MTE
+instructions for stack memory usage.
+
 @ifclear GENERIC
 @lowersections
 @end ifclear
diff --git a/ld/testsuite/ld-aarch64/aarch64-elf.exp b/ld/testsuite/ld-aarch64/aarch64-elf.exp
index 56e68c584fb..8d56beb64c7 100644
--- a/ld/testsuite/ld-aarch64/aarch64-elf.exp
+++ b/ld/testsuite/ld-aarch64/aarch64-elf.exp
@@ -378,6 +378,7 @@ run_dump_test_lp64 "variant_pcs-now"
 
 run_dump_test_lp64 "mte-tagged-frame"
 run_dump_test_lp64 "dt-memtag-mode"
+run_dump_test_lp64 "dt-memtag-stack"
 
 set aarch64elflinktests {
   {"ld-aarch64/so with global symbol" "-shared" "" "" {copy-reloc-so.s}
diff --git a/ld/testsuite/ld-aarch64/dt-memtag-stack.d b/ld/testsuite/ld-aarch64/dt-memtag-stack.d
new file mode 100644
index 00000000000..242f61fa172
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/dt-memtag-stack.d
@@ -0,0 +1,7 @@
+#source: dt-memtag.s
+#ld: -shared -z memtag-stack
+#readelf: -d
+
+#...
+ 0x000000007000000c \(AARCH64_MEMTAG_STACK\) 0x1
+#...
-- 
2.43.0



More information about the Binutils mailing list