[PATCH V4 7/8] ld/aarch64elf: add support for DT_AARCH64_MEMTAG_MODE dynamic tag

Richard Earnshaw Richard.Earnshaw@arm.com
Fri Jul 11 13:37:11 GMT 2025


On 09/07/2025 22:44, Jose E. Marchesi via Binutils wrote:
> 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 that this patch doesn't add support for the "asymm" MTE mode,
> which is an Armv8.7 extension.
> 
> 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                      | 14 +++++++-
>   bfd/elfxx-aarch64.h                      | 26 ++++++++++++--
>   binutils/readelf.c                       |  6 ++--
>   include/elf/aarch64.h                    |  1 +
>   ld/emultempl/aarch64elf.em               | 43 +++++++++++++++++++++++-
>   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, 108 insertions(+), 6 deletions(-)
>   create mode 100644 ld/testsuite/ld-aarch64/dt-memtag-mode.d
>   create mode 100644 ld/testsuite/ld-aarch64/dt-memtag.s
> 
> diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
> index 8f399204f04..0e9e22e210b 100644
> --- a/bfd/elfnn-aarch64.c
> +++ b/bfd/elfnn-aarch64.c
> @@ -2622,6 +2622,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;
>   
> @@ -5009,13 +5012,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.  */
> @@ -9775,7 +9780,14 @@ elfNN_aarch64_late_size_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
>   		   && !add_dynamic_entry (DT_AARCH64_PAC_PLT, 0))
>   	    return false;
>   	}
> +
> +      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..66c1f48d450 100644
> --- a/bfd/elfxx-aarch64.h
> +++ b/bfd/elfxx-aarch64.h
> @@ -129,13 +129,35 @@ 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.
> +   At this time, except AARCH64_MEMTAG_MODE_NONE, the enumerator constants are
> +   the same as specified in the Memtag ABI Extension to ELF for the Arm 64-bit
> +   Architecture (AArch64) document (the intent being that this keeps the
> +   emission of the associated dynamic tag simple).*/
> +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 bcaac571e73..2ae6eea810f 100644
> --- a/binutils/readelf.c
> +++ b/binutils/readelf.c
> @@ -2584,9 +2584,11 @@ get_aarch64_dynamic_type (unsigned long type)
>   {
>     switch (type)
>       {
> -    case DT_AARCH64_BTI_PLT:  return "AARCH64_BTI_PLT";
> -    case DT_AARCH64_PAC_PLT:  return "AARCH64_PAC_PLT";
> +    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)

Use a hard tab here to match the other instances.

>   
>   /* 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..d7e4eb78059 100644
> --- a/ld/emultempl/aarch64elf.em
> +++ b/ld/emultempl/aarch64elf.em
> @@ -42,6 +42,10 @@ static aarch64_protection_opts sw_protections = {
>     .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 +339,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 +445,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 +548,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 +572,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 afcc3fea94f..f196f1e5870 100644
> --- a/ld/ld.texi
> +++ b/ld/ld.texi
> @@ -8374,6 +8374,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

There should be separate @samp entries for none and sync; the comma and 
the space should not be inside a @samp.

> +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

OK with the nits mentioned above fixed.

R.


More information about the Binutils mailing list