https://github.com/EliaGeretto updated 
https://github.com/llvm/llvm-project/pull/183275

>From b4acafd0e40ce263948ec5e1df32e410b34c5e38 Mon Sep 17 00:00:00 2001
From: Elia Geretto <[email protected]>
Date: Fri, 20 Feb 2026 12:19:38 +0100
Subject: [PATCH 1/3] [ELF][MTE] Add generic --memtag-* options

This commits duplicates the Android-specific --android-memtag-* flags,
adding a generic --memtag-* variant. The only difference is that, when
the generic options are used, the linker will emit only the dynamic
array tags specified in the "Memtag ABI Extension to ELF", but no
Android-specific memtag note.
---
 lld/ELF/Config.h                              | 15 ++---
 lld/ELF/Driver.cpp                            | 61 +++++++++++--------
 lld/ELF/Options.td                            | 18 +++---
 lld/ELF/SyntheticSections.cpp                 | 21 ++++---
 lld/test/ELF/aarch64-memtag-abi.s             | 58 ++++++++++++++++++
 lld/test/ELF/aarch64-memtag-android-abi.s     | 37 +++++------
 lld/test/ELF/aarch64-memtag-globals.s         |  8 +--
 ...arch64-memtag-pauth-globals-out-of-range.s |  2 +-
 lld/test/ELF/aarch64-memtag-pauth-globals.s   |  2 +-
 9 files changed, 147 insertions(+), 75 deletions(-)
 create mode 100644 lld/test/ELF/aarch64-memtag-abi.s

diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 237df52194210..fb8eca34ddd50 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -511,17 +511,18 @@ struct Config {
   // 4 for ELF32, 8 for ELF64.
   int wordsize;
 
-  // Mode of MTE to write to the ELF note. Should be one of NT_MEMTAG_ASYNC 
(for
-  // async), NT_MEMTAG_SYNC (for sync), or NT_MEMTAG_LEVEL_NONE (for none). If
-  // async or sync is enabled, write the ELF note specifying the default MTE
-  // mode.
-  int androidMemtagMode;
+  // Mode of MTE to write to the dynamic array. Should be one of 
NT_MEMTAG_ASYNC
+  // (for async), NT_MEMTAG_SYNC (for sync), or NT_MEMTAG_LEVEL_NONE (for 
none).
+  // If async or sync is enabled, write the tag specifying the default MTE 
mode.
+  int memtagMode;
   // Signal to the dynamic loader to enable heap MTE.
-  bool androidMemtagHeap;
+  bool memtagHeap;
   // Signal to the dynamic loader that this binary expects stack MTE. 
Generally,
   // this means to map the primary and thread stacks as PROT_MTE. Note: This is
   // not supported on Android 11 & 12.
-  bool androidMemtagStack;
+  bool memtagStack;
+  // Whether to emit the Android-specific legacy memtag note.
+  bool memtagAndroidNote;
 
   // When using a unified pre-link LTO pipeline, specify the backend LTO mode.
   LtoKind ltoKind = LtoKind::Default;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index d7bfa7357d4ed..d5c7f7e9885aa 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -630,6 +630,27 @@ static ZicfissPolicy getZZicfiss(Ctx &ctx, 
opt::InputArgList &args) {
   return ret;
 }
 
+static int getZMemtagMode(Ctx &ctx, opt::InputArgList &args) {
+  auto ret = ELF::NT_MEMTAG_LEVEL_NONE;
+
+  for (auto *arg : args.filtered(OPT_z)) {
+    std::pair<StringRef, StringRef> kv = StringRef(arg->getValue()).split('=');
+    if (kv.first == "memtag-mode") {
+      arg->claim();
+      if (kv.second == "none")
+        ret = ELF::NT_MEMTAG_LEVEL_NONE;
+      else if (kv.second == "sync")
+        ret = ELF::NT_MEMTAG_LEVEL_SYNC;
+      else if (kv.second == "async")
+        ret = ELF::NT_MEMTAG_LEVEL_ASYNC;
+      else
+        ErrAlways(ctx) << "unknown -z memtag-mode= value: " << kv.second;
+    }
+  }
+
+  return ret;
+}
+
 // Report a warning for an unknown -z option.
 static void checkZOptions(Ctx &ctx, opt::InputArgList &args) {
   // This function is called before getTarget(), when certain options are not
@@ -846,27 +867,20 @@ static StringRef getDynamicLinker(Ctx &ctx, 
opt::InputArgList &args) {
 }
 
 static int getMemtagMode(Ctx &ctx, opt::InputArgList &args) {
-  StringRef memtagModeArg = args.getLastArgValue(OPT_android_memtag_mode);
-  if (memtagModeArg.empty()) {
-    if (ctx.arg.androidMemtagStack)
-      Warn(ctx) << "--android-memtag-mode is unspecified, leaving "
-                   "--android-memtag-stack a no-op";
-    else if (ctx.arg.androidMemtagHeap)
-      Warn(ctx) << "--android-memtag-mode is unspecified, leaving "
-                   "--android-memtag-heap a no-op";
-    return ELF::NT_MEMTAG_LEVEL_NONE;
+  auto memtagMode = getZMemtagMode(ctx, args);
+  if (memtagMode == ELF::NT_MEMTAG_LEVEL_NONE) {
+    if (ctx.arg.memtagStack)
+      Warn(ctx) << "-z memtag-mode is none, leaving "
+                   "-z memtag-stack a no-op";
+    if (ctx.arg.memtagHeap)
+      Warn(ctx) << "-z memtag-mode is none, leaving "
+                   "-z memtag-heap a no-op";
+    if (ctx.arg.memtagAndroidNote)
+      Warn(ctx) << "-z memtag-mode is none, leaving "
+                   "--android-memtag-note a no-op";
   }
 
-  if (memtagModeArg == "sync")
-    return ELF::NT_MEMTAG_LEVEL_SYNC;
-  if (memtagModeArg == "async")
-    return ELF::NT_MEMTAG_LEVEL_ASYNC;
-  if (memtagModeArg == "none")
-    return ELF::NT_MEMTAG_LEVEL_NONE;
-
-  ErrAlways(ctx) << "unknown --android-memtag-mode value: \"" << memtagModeArg
-                 << "\", should be one of {async, sync, none}";
-  return ELF::NT_MEMTAG_LEVEL_NONE;
+  return memtagMode;
 }
 
 static ICFLevel getICF(opt::InputArgList &args) {
@@ -1358,13 +1372,12 @@ static void readConfigs(Ctx &ctx, opt::InputArgList 
&args) {
       hasZOption(args, "muldefs") ||
       args.hasFlag(OPT_allow_multiple_definition,
                    OPT_no_allow_multiple_definition, false);
-  ctx.arg.androidMemtagHeap =
-      args.hasFlag(OPT_android_memtag_heap, OPT_no_android_memtag_heap, false);
-  ctx.arg.androidMemtagStack = args.hasFlag(OPT_android_memtag_stack,
-                                            OPT_no_android_memtag_stack, 
false);
+  ctx.arg.memtagHeap = hasZOption(args, "memtag-heap");
+  ctx.arg.memtagStack = hasZOption(args, "memtag-stack");
+  ctx.arg.memtagAndroidNote = args.hasArg(OPT_android_memtag_note);
   ctx.arg.fatLTOObjects =
       args.hasFlag(OPT_fat_lto_objects, OPT_no_fat_lto_objects, false);
-  ctx.arg.androidMemtagMode = getMemtagMode(ctx, args);
+  ctx.arg.memtagMode = getMemtagMode(ctx, args);
   ctx.arg.auxiliaryList = args::getStrings(args, OPT_auxiliary);
   ctx.arg.armBe8 = args.hasArg(OPT_be8);
   if (opt::Arg *arg = args.getLastArg(
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index c2111e58c12b9..d262f34c9d1dd 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -844,14 +844,10 @@ defm check_dynamic_relocations: 
BB<"check-dynamic-relocations",
 
 defm load_pass_plugins: EEq<"load-pass-plugin", "Load passes from plugin 
library">;
 
-// Hidden options, used by clang's -fsanitize=memtag-* options to emit an ELF
-// note to designate what kinds of memory (stack/heap) should be protected 
using
-// ARM's MTE on armv8.5+. A binary's desire for stack MTE can't be obtained
-// implicitly, so we have a specific bit in the note to signal to the loader to
-// remap the stack as PROT_MTE.
-defm android_memtag_stack: BB<"android-memtag-stack",
-    "Instruct the dynamic loader to prepare for MTE stack instrumentation", 
"">;
-defm android_memtag_heap: BB<"android-memtag-heap",
-    "Instruct the dynamic loader to enable MTE protection for the heap", "">;
-defm android_memtag_mode: EEq<"android-memtag-mode",
-    "Instruct the dynamic loader to start under MTE mode {async, sync, none}">;
+// Hidden option, used by clang's -fsanitize=memtag-* option to emit an ELF 
note
+// to designate what kinds of memory (stack/heap) should be protected using
+// ARM's MTE on armv8.5+. This note is Android-specific and it is used only in
+// older SDK versions.
+defm android_memtag_note
+    : BB<"android-memtag-note", "Emit the Android-specific legacy memtag note",
+         "">;
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 2cfc88d8389b0..5ec226f1621f3 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1504,9 +1504,10 @@ DynamicSection<ELFT>::computeContents() {
       addInt(DT_AARCH64_PAC_PLT, 0);
 
     if (hasMemtag(ctx)) {
-      addInt(DT_AARCH64_MEMTAG_MODE, ctx.arg.androidMemtagMode == 
NT_MEMTAG_LEVEL_ASYNC);
-      addInt(DT_AARCH64_MEMTAG_HEAP, ctx.arg.androidMemtagHeap);
-      addInt(DT_AARCH64_MEMTAG_STACK, ctx.arg.androidMemtagStack);
+      addInt(DT_AARCH64_MEMTAG_MODE,
+             ctx.arg.memtagMode == NT_MEMTAG_LEVEL_ASYNC);
+      addInt(DT_AARCH64_MEMTAG_HEAP, ctx.arg.memtagHeap);
+      addInt(DT_AARCH64_MEMTAG_STACK, ctx.arg.memtagStack);
       if (ctx.mainPart->memtagGlobalDescriptors->isNeeded()) {
         addInSec(DT_AARCH64_MEMTAG_GLOBALS,
                  *ctx.mainPart->memtagGlobalDescriptors);
@@ -4500,7 +4501,7 @@ static bool needsInterpSection(Ctx &ctx) {
 
 bool elf::hasMemtag(Ctx &ctx) {
   return ctx.arg.emachine == EM_AARCH64 &&
-         ctx.arg.androidMemtagMode != ELF::NT_MEMTAG_LEVEL_NONE;
+         ctx.arg.memtagMode != ELF::NT_MEMTAG_LEVEL_NONE;
 }
 
 // Fully static executables don't support MTE globals at this point in time, as
@@ -4528,12 +4529,12 @@ void MemtagAndroidNote::writeTo(uint8_t *buf) {
   buf += 12 + alignTo(sizeof(kMemtagAndroidNoteName), 4);
 
   uint32_t value = 0;
-  value |= ctx.arg.androidMemtagMode;
-  if (ctx.arg.androidMemtagHeap)
+  value |= ctx.arg.memtagMode;
+  if (ctx.arg.memtagHeap)
     value |= ELF::NT_MEMTAG_HEAP;
   // Note, MTE stack is an ABI break. Attempting to run an MTE stack-enabled
   // binary on Android 11 or 12 will result in a checkfail in the loader.
-  if (ctx.arg.androidMemtagStack)
+  if (ctx.arg.memtagStack)
     value |= ELF::NT_MEMTAG_STACK;
   write32(ctx, buf, value); // note value
 }
@@ -4740,8 +4741,10 @@ template <class ELFT> void 
elf::createSyntheticSections(Ctx &ctx) {
     part.dynamic = std::make_unique<DynamicSection<ELFT>>(ctx);
 
     if (hasMemtag(ctx)) {
-      part.memtagAndroidNote = std::make_unique<MemtagAndroidNote>(ctx);
-      add(*part.memtagAndroidNote);
+      if (ctx.arg.memtagAndroidNote) {
+        part.memtagAndroidNote = std::make_unique<MemtagAndroidNote>(ctx);
+        add(*part.memtagAndroidNote);
+      }
       if (canHaveMemtagGlobals(ctx)) {
         part.memtagGlobalDescriptors =
             std::make_unique<MemtagGlobalDescriptors>(ctx);
diff --git a/lld/test/ELF/aarch64-memtag-abi.s 
b/lld/test/ELF/aarch64-memtag-abi.s
new file mode 100644
index 0000000000000..86e53edc1fbaf
--- /dev/null
+++ b/lld/test/ELF/aarch64-memtag-abi.s
@@ -0,0 +1,58 @@
+# REQUIRES: aarch64
+
+# RUN: llvm-mc --filetype=obj -triple=aarch64-linux-gnu %s -o %t.o
+
+# RUN: ld.lld -shared -z memtag-mode=async -z memtag-heap %t.o -o %t
+# RUN: llvm-readelf --memtag %t | FileCheck %s 
--check-prefixes=CHECK,HEAP,NOSTACK,ASYNC
+
+# RUN: ld.lld -shared -z memtag-mode=sync -z memtag-heap %t.o -o %t
+# RUN: llvm-readelf --memtag %t | FileCheck %s 
--check-prefixes=CHECK,HEAP,NOSTACK,SYNC
+
+# RUN: ld.lld -shared -z memtag-mode=async -z memtag-stack %t.o -o %t
+# RUN: llvm-readelf --memtag %t | FileCheck %s 
--check-prefixes=CHECK,NOHEAP,STACK,ASYNC
+
+# RUN: ld.lld -shared -z memtag-mode=sync -z memtag-stack %t.o -o %t
+# RUN: llvm-readelf --memtag %t | FileCheck %s 
--check-prefixes=CHECK,NOHEAP,STACK,SYNC
+
+# RUN: ld.lld -shared -z memtag-mode=async -z memtag-heap \
+# RUN:    -z memtag-stack %t.o -o %t
+# RUN: llvm-readelf --memtag %t | FileCheck %s 
--check-prefixes=CHECK,HEAP,STACK,ASYNC
+
+# RUN: ld.lld -shared -z memtag-mode=sync -z memtag-heap \
+# RUN:    -z memtag-stack %t.o -o %t
+# RUN: llvm-readelf --memtag %t | FileCheck %s 
--check-prefixes=CHECK,HEAP,STACK,SYNC
+
+# RUN: ld.lld -shared -z memtag-heap %t.o -o %t 2>&1 | \
+# RUN:    FileCheck %s --check-prefixes=MISSING-MODE
+# RUN: ld.lld -shared -z memtag-stack %t.o -o %t 2>&1 | \
+# RUN:    FileCheck %s --check-prefixes=MISSING-MODE
+# RUN: ld.lld -shared -z memtag-heap -z memtag-stack %t.o -o %t 2>&1 | \
+# RUN:    FileCheck %s --check-prefixes=MISSING-MODE
+# MISSING-MODE: warning: -z memtag-mode is none, leaving
+# MISSING-MODE-SAME: -z memtag-{{(heap|stack)}} a no-op
+
+# CHECK: Memtag Dynamic Entries
+# SYNC:    AARCH64_MEMTAG_MODE: Synchronous (0)
+# ASYNC:   AARCH64_MEMTAG_MODE: Asynchronous (1)
+# HEAP:    AARCH64_MEMTAG_HEAP: Enabled (1)
+# NOHEAP:  AARCH64_MEMTAG_HEAP: Disabled (0)
+# STACK:   AARCH64_MEMTAG_STACK: Enabled (1)
+# NOSTACK: AARCH64_MEMTAG_STACK: Disabled (0)
+
+# CHECK-NOT: Memtag Android Note
+
+# RUN: not ld.lld -shared -z memtag-mode=asymm -z memtag-heap 2>&1 | \
+# RUN:    FileCheck %s --check-prefix=BAD-MODE
+# BAD-MODE: error: unknown -z memtag-mode= value: asymm
+
+# RUN: ld.lld -static -z memtag-mode=sync -z memtag-heap \
+# RUN:    -z memtag-stack %t.o -o %t
+# RUN: llvm-readelf --memtag %t | FileCheck %s --check-prefixes=STATIC
+
+# STATIC:      Memtag Dynamic Entries:
+# STATIC-NEXT: < none found >
+# STATIC-NOT:  Memtag Android Note
+
+.globl _start
+_start:
+  ret
diff --git a/lld/test/ELF/aarch64-memtag-android-abi.s 
b/lld/test/ELF/aarch64-memtag-android-abi.s
index 57c85dcfe81d0..cba84d07ae227 100644
--- a/lld/test/ELF/aarch64-memtag-android-abi.s
+++ b/lld/test/ELF/aarch64-memtag-android-abi.s
@@ -6,34 +6,36 @@
 ## can be run on these versions of Android.
 
 # RUN: llvm-mc --filetype=obj -triple=aarch64-linux-android %s -o %t.o
-# RUN: ld.lld -shared --android-memtag-mode=async --android-memtag-heap %t.o 
-o %t
+# RUN: ld.lld -shared -z memtag-mode=async -z memtag-heap 
--android-memtag-note %t.o -o %t
 # RUN: llvm-readelf --memtag %t | FileCheck %s 
--check-prefixes=CHECK,HEAP,NOSTACK,ASYNC
 
-# RUN: ld.lld -shared --android-memtag-mode=sync --android-memtag-heap %t.o -o 
%t
+# RUN: ld.lld -shared -z memtag-mode=sync -z memtag-heap --android-memtag-note 
%t.o -o %t
 # RUN: llvm-readelf --memtag %t | FileCheck %s 
--check-prefixes=CHECK,HEAP,NOSTACK,SYNC
 
-# RUN: ld.lld -shared --android-memtag-mode=async --android-memtag-stack %t.o 
-o %t
+# RUN: ld.lld -shared -z memtag-mode=async -z memtag-stack 
--android-memtag-note %t.o -o %t
 # RUN: llvm-readelf --memtag %t | FileCheck %s 
--check-prefixes=CHECK,NOHEAP,STACK,ASYNC
 
-# RUN: ld.lld -shared --android-memtag-mode=sync --android-memtag-stack %t.o 
-o %t
+# RUN: ld.lld -shared -z memtag-mode=sync -z memtag-stack 
--android-memtag-note %t.o -o %t
 # RUN: llvm-readelf --memtag %t | FileCheck %s 
--check-prefixes=CHECK,NOHEAP,STACK,SYNC
 
-# RUN: ld.lld -shared --android-memtag-mode=async --android-memtag-heap \
-# RUN:    --android-memtag-stack %t.o -o %t
+# RUN: ld.lld -shared -z memtag-mode=async -z memtag-heap \
+# RUN:    -z memtag-stack --android-memtag-note %t.o -o %t
 # RUN: llvm-readelf --memtag %t | FileCheck %s 
--check-prefixes=CHECK,HEAP,STACK,ASYNC
 
-# RUN: ld.lld -shared --android-memtag-mode=sync --android-memtag-heap \
-# RUN:    --android-memtag-stack %t.o -o %t
+# RUN: ld.lld -shared -z memtag-mode=sync -z memtag-heap \
+# RUN:    -z memtag-stack --android-memtag-note %t.o -o %t
 # RUN: llvm-readelf --memtag %t | FileCheck %s 
--check-prefixes=CHECK,HEAP,STACK,SYNC
 
-# RUN: ld.lld -shared --android-memtag-heap %t.o -o %t 2>&1 | \
+# RUN: ld.lld -shared --android-memtag-note %t.o -o %t 2>&1 | \
 # RUN:    FileCheck %s --check-prefixes=MISSING-MODE
-# RUN: ld.lld -shared --android-memtag-stack %t.o -o %t 2>&1 | \
+# RUN: ld.lld -shared -z memtag-heap --android-memtag-note %t.o -o %t 2>&1 | \
 # RUN:    FileCheck %s --check-prefixes=MISSING-MODE
-# RUN: ld.lld -shared --android-memtag-heap --android-memtag-stack %t.o -o %t 
2>&1 | \
+# RUN: ld.lld -shared -z memtag-stack --android-memtag-note %t.o -o %t 2>&1 | \
 # RUN:    FileCheck %s --check-prefixes=MISSING-MODE
-# MISSING-MODE: warning: --android-memtag-mode is unspecified, leaving
-# MISSING-MODE: --android-memtag-{{(heap|stack)}} a no-op
+# RUN: ld.lld -shared -z memtag-heap -z memtag-stack --android-memtag-note 
%t.o -o %t 2>&1 | \
+# RUN:    FileCheck %s --check-prefixes=MISSING-MODE
+# MISSING-MODE: warning: -z memtag-mode is none, leaving
+# MISSING-MODE-SAME: {{(--android-memtag-note|-z memtag-(heap|stack))}} a no-op
 
 # CHECK: Memtag Dynamic Entries
 # SYNC:    AARCH64_MEMTAG_MODE: Synchronous (0)
@@ -51,13 +53,12 @@
 # STACK-NEXT:   Stack: Enabled
 # NOSTACK-NEXT: Stack: Disabled
 
-# RUN: not ld.lld -shared --android-memtag-mode=asymm --android-memtag-heap 
2>&1 | \
+# RUN: not ld.lld -shared -z memtag-mode=asymm -z memtag-heap 
--android-memtag-note 2>&1 | \
 # RUN:    FileCheck %s --check-prefix=BAD-MODE
-# BAD-MODE: error: unknown --android-memtag-mode value: "asymm", should be one 
of
-# BAD-MODE: {async, sync, none}
+# BAD-MODE: error: unknown -z memtag-mode= value: asymm
 
-# RUN: ld.lld -static --android-memtag-mode=sync --android-memtag-heap \
-# RUN:    --android-memtag-stack %t.o -o %t
+# RUN: ld.lld -static -z memtag-mode=sync -z memtag-heap \
+# RUN:    -z memtag-stack --android-memtag-note %t.o -o %t
 # RUN: llvm-readelf --memtag %t | FileCheck %s --check-prefixes=STATIC
 
 # STATIC:      Memtag Dynamic Entries:
diff --git a/lld/test/ELF/aarch64-memtag-globals.s 
b/lld/test/ELF/aarch64-memtag-globals.s
index b109c1d2c2e9c..c10fbc7013423 100644
--- a/lld/test/ELF/aarch64-memtag-globals.s
+++ b/lld/test/ELF/aarch64-memtag-globals.s
@@ -4,7 +4,7 @@
 
 ## Ensure MTE globals doesn't work with REL (only RELA).
 # RUN: yaml2obj %s -o %t.rel.o
-# RUN: not ld.lld -shared --android-memtag-mode=sync %t.rel.o -o %t1.so 2>&1 | 
FileCheck %s --check-prefix=CHECK-RELA
+# RUN: not ld.lld -shared -z memtag-mode=sync --android-memtag-note %t.rel.o 
-o %t1.so 2>&1 | FileCheck %s --check-prefix=CHECK-RELA
 # CHECK-RELA: non-RELA relocations are not allowed with memtag globals
 --- !ELF
 FileHeader:
@@ -58,7 +58,7 @@ Symbols:
 # RUN:   %t/input_1.s -o %t1.o
 # RUN: llvm-mc --filetype=obj -triple=aarch64-linux-android \
 # RUN:   %t/input_2.s -o %t2.o
-# RUN: ld.lld -shared --android-memtag-mode=sync %t1.o %t2.o -o %t.so
+# RUN: ld.lld -shared -z memtag-mode=sync --android-memtag-note %t1.o %t2.o -o 
%t.so
 
 ## Normally relocations are printed before the symbol tables, so reorder it a
 ## bit to make it easier on matching addresses of relocations up with the
@@ -69,7 +69,7 @@ Symbols:
 # RUN: llvm-objdump -Dz %t.so | FileCheck %s 
--check-prefix=CHECK-SPECIAL-RELOCS
 
 ## And ensure that --apply-dynamic-relocs is banned.
-# RUN: not ld.lld --apply-dynamic-relocs -shared --android-memtag-mode=sync \
+# RUN: not ld.lld --apply-dynamic-relocs -shared -z memtag-mode=sync 
--android-memtag-note \
 # RUN:   %t1.o %t2.o -o %t1.so 2>&1 | FileCheck %s 
--check-prefix=CHECK-DYNRELOC
 # CHECK-DYNRELOC: --apply-dynamic-relocs cannot be used with MTE globals
 
@@ -78,7 +78,7 @@ Symbols:
 ## dynamic entries, etc.
 # RUN: llvm-mc --filetype=obj -triple=aarch64-linux-android \
 # RUN:   %t/input_3.s -o %t3.o
-# RUN: ld.lld -static --android-memtag-mode=sync %t1.o %t2.o %t3.o -o 
%t.static.so
+# RUN: ld.lld -static -z memtag-mode=sync --android-memtag-note %t1.o %t2.o 
%t3.o -o %t.static.so
 # RUN: llvm-readelf -s --section-headers --relocs --memtag %t.static.so | \
 # RUN:   FileCheck %s --check-prefix=CHECK-STATIC
 # CHECK-STATIC-NOT: .memtag.globals.static
diff --git a/lld/test/ELF/aarch64-memtag-pauth-globals-out-of-range.s 
b/lld/test/ELF/aarch64-memtag-pauth-globals-out-of-range.s
index c786d52a7ec1e..c0b5e6f657cec 100644
--- a/lld/test/ELF/aarch64-memtag-pauth-globals-out-of-range.s
+++ b/lld/test/ELF/aarch64-memtag-pauth-globals-out-of-range.s
@@ -1,6 +1,6 @@
 # REQUIRES: aarch64
 # RUN: llvm-mc -filetype=obj -triple=aarch64-linux-android %s -o %t.o
-# RUN: not ld.lld --shared --android-memtag-mode=sync %t.o -o /dev/null 2>&1 | 
\
+# RUN: not ld.lld --shared -z memtag-mode=sync --android-memtag-note %t.o -o 
/dev/null 2>&1 | \
 # RUN:   FileCheck %s --implicit-check-not=error:
 
 ## Verify that, when composing PAuth and Memtag ABIs, we error if trying to
diff --git a/lld/test/ELF/aarch64-memtag-pauth-globals.s 
b/lld/test/ELF/aarch64-memtag-pauth-globals.s
index 2b468cbbb76c6..61e5d36c8d71c 100644
--- a/lld/test/ELF/aarch64-memtag-pauth-globals.s
+++ b/lld/test/ELF/aarch64-memtag-pauth-globals.s
@@ -1,6 +1,6 @@
 # REQUIRES: aarch64
 # RUN: llvm-mc -filetype=obj -triple=aarch64-linux-android %s -o %t.o
-# RUN: ld.lld --shared --android-memtag-mode=sync %t.o -o %t
+# RUN: ld.lld --shared -z memtag-mode=sync --android-memtag-note %t.o -o %t
 # RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELA
 # RUN: llvm-readelf -x.data %t | FileCheck %s --check-prefix=DATA
 

>From f3a620ae6b06013cb5a13188eb3d1cecc6a67ad1 Mon Sep 17 00:00:00 2001
From: Elia Geretto <[email protected]>
Date: Fri, 20 Feb 2026 14:20:18 +0100
Subject: [PATCH 2/3] [Driver][MTE] Use generic lld memtag flags

This commit adds support for generic --memtag-* lld flags which allow to
produce memtag-enabled ELFs for all supported OS-es. Android targets
will still use the Android-specific --android-memtag-* lld flags.
---
 clang/lib/Driver/ToolChains/CommonArgs.cpp | 25 +++++++----
 clang/test/Driver/fsanitize-memtag.c       |  2 +-
 clang/test/Driver/memtag-android-ld.c      | 48 ++++++++++++++++++++++
 clang/test/Driver/memtag-ld.c              | 30 +++++++-------
 4 files changed, 80 insertions(+), 25 deletions(-)
 create mode 100644 clang/test/Driver/memtag-android-ld.c

diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp 
b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 9a17fa2546e68..865193be2e013 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1796,16 +1796,23 @@ bool tools::addSanitizerRuntimes(const ToolChain &TC, 
const ArgList &Args,
     CmdArgs.push_back("--export-dynamic-symbol=__cfi_check");
 
   if (SanArgs.hasMemTag()) {
-    if (!TC.getTriple().isAndroid()) {
-      TC.getDriver().Diag(diag::err_drv_unsupported_opt_for_target)
-          << "-fsanitize=memtag*" << TC.getTriple().str();
-    }
+    CmdArgs.push_back("-z");
     CmdArgs.push_back(
-        Args.MakeArgString("--android-memtag-mode=" + 
SanArgs.getMemtagMode()));
-    if (SanArgs.hasMemtagHeap())
-      CmdArgs.push_back("--android-memtag-heap");
-    if (SanArgs.hasMemtagStack())
-      CmdArgs.push_back("--android-memtag-stack");
+        Args.MakeArgString("memtag-mode=" + SanArgs.getMemtagMode()));
+
+    if (SanArgs.hasMemtagHeap()) {
+      CmdArgs.push_back("-z");
+      CmdArgs.push_back("memtag-heap");
+    }
+
+    if (SanArgs.hasMemtagStack()) {
+      CmdArgs.push_back("-z");
+      CmdArgs.push_back("memtag-stack");
+    }
+
+    if (TC.getTriple().isAndroid()) {
+      CmdArgs.push_back("--android-memtag-note");
+    }
   }
 
   return !StaticRuntimes.empty() || !NonWholeStaticRuntimes.empty() ||
diff --git a/clang/test/Driver/fsanitize-memtag.c 
b/clang/test/Driver/fsanitize-memtag.c
index c842e6de1b62d..cfe9a2b0c1ad9 100644
--- a/clang/test/Driver/fsanitize-memtag.c
+++ b/clang/test/Driver/fsanitize-memtag.c
@@ -12,7 +12,7 @@
 // CHECK-SANMT-MT: "-target-feature" "+mte"
 // CHECK-SANMT-MT-SAME: "-fsanitize=memtag-stack,memtag-heap,memtag-globals"
 
-// RUN: not %clang --target=aarch64-linux -fsanitize=memtag -Xclang 
-target-feature -Xclang +mte %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-SANMT-MT
+// RUN: %clang --target=aarch64-linux -fsanitize=memtag -Xclang 
-target-feature -Xclang +mte %s -### 2>&1 | FileCheck %s 
--check-prefix=CHECK-SANMT-MT
 
 // RUN: not %clang --target=aarch64-linux -fsanitize=memtag %s -### 2>&1 | 
FileCheck %s --check-prefix=CHECK-SANMT-NOMT-0
 // CHECK-SANMT-NOMT-0: '-fsanitize=memtag-stack' requires hardware support 
(+memtag)
diff --git a/clang/test/Driver/memtag-android-ld.c 
b/clang/test/Driver/memtag-android-ld.c
new file mode 100644
index 0000000000000..36601a6e51d94
--- /dev/null
+++ b/clang/test/Driver/memtag-android-ld.c
@@ -0,0 +1,48 @@
+// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN:   -fsanitize=memtag %s 2>&1 | FileCheck %s \
+// RUN:   --check-prefixes=CHECK-SYNC,CHECK-HEAP,CHECK-STACK,CHECK-NOTE
+
+// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN:   -fsanitize=memtag-stack %s 2>&1 | FileCheck %s \
+// RUN:   --check-prefixes=CHECK-SYNC,CHECK-NO-HEAP,CHECK-STACK,CHECK-NOTE
+
+// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN:   -fsanitize=memtag-heap %s 2>&1 | FileCheck %s \
+// RUN:   --check-prefixes=CHECK-SYNC,CHECK-HEAP,CHECK-NO-STACK,CHECK-NOTE
+
+// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN:   -fsanitize=memtag -fsanitize-memtag-mode=async %s 2>&1 | \
+// RUN:   FileCheck %s 
--check-prefixes=CHECK-ASYNC,CHECK-HEAP,CHECK-STACK,CHECK-NOTE
+
+// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN:   -fsanitize=memtag-stack -fsanitize-memtag-mode=async %s 2>&1 \
+// RUN:   | FileCheck %s \
+// RUN:   --check-prefixes=CHECK-ASYNC,CHECK-NO-HEAP,CHECK-STACK,CHECK-NOTE
+
+// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN:   -fsanitize=memtag-heap -fsanitize-memtag-mode=async %s 2>&1 \
+// RUN:   | FileCheck %s \
+// RUN:   --check-prefixes=CHECK-ASYNC,CHECK-HEAP,CHECK-NO-STACK,CHECK-NOTE
+
+// RUN: not %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN:   -fsanitize=memtag-heap -fsanitize-memtag-mode=asymm %s 2>&1 \
+// RUN:   | FileCheck %s --check-prefixes=CHECK-INVALID-MODE
+
+// RUN: not %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN:   -fsanitize=memtag-stack -fsanitize=memtag-heap \
+// RUN:   -fsanitize-memtag-mode=asymm -fno-sanitize=memtag %s 2>&1 \
+// RUN:   | FileCheck %s --check-prefixes=CHECK-NONE
+
+// CHECK-ASYNC:             ld{{.*}} "-z" "memtag-mode=async"
+// CHECK-SYNC:              ld{{.*}} "-z" "memtag-mode=sync"
+// CHECK-HEAP:              "-z" "memtag-heap"
+// CHECK-NO-HEAP-NOT:       "-z" "memtag-heap"
+// CHECK-STACK:             "-z" "memtag-stack"
+// CHECK-NO-STACK-NOT:      "-z" "memtag-stack"
+// CHECK-NOTE:              "--android-memtag-note"
+// CHECK-INVALID-MODE:      invalid value 'asymm' in '-fsanitize-memtag-mode=',
+// CHECK-INVALID-MODE-SAME: expected one of: {async, sync}
+// CHECK-NONE-NOT:          ld{{.*}} "-z" "memtag
+// CHECK-NONE-NOT:          ld{{.*}} "--android-memtag-note"
+
+void f() {}
diff --git a/clang/test/Driver/memtag-ld.c b/clang/test/Driver/memtag-ld.c
index aef08ddc5758a..05a648a6ac7a1 100644
--- a/clang/test/Driver/memtag-ld.c
+++ b/clang/test/Driver/memtag-ld.c
@@ -1,46 +1,46 @@
-// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN: %clang -### --target=aarch64-linux-gnu -march=armv8+memtag \
 // RUN:   -fsanitize=memtag %s 2>&1 | FileCheck %s \
 // RUN:   --check-prefixes=CHECK-SYNC,CHECK-HEAP,CHECK-STACK
 
-// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN: %clang -### --target=aarch64-linux-gnu -march=armv8+memtag \
 // RUN:   -fsanitize=memtag-stack %s 2>&1 | FileCheck %s \
 // RUN:   --check-prefixes=CHECK-SYNC,CHECK-NO-HEAP,CHECK-STACK
 
-// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN: %clang -### --target=aarch64-linux-gnu -march=armv8+memtag \
 // RUN:   -fsanitize=memtag-heap %s 2>&1 | FileCheck %s \
 // RUN:   --check-prefixes=CHECK-SYNC,CHECK-HEAP,CHECK-NO-STACK
 
-// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN: %clang -### --target=aarch64-linux-gnu -march=armv8+memtag \
 // RUN:   -fsanitize=memtag -fsanitize-memtag-mode=async %s 2>&1 | \
 // RUN:   FileCheck %s --check-prefixes=CHECK-ASYNC,CHECK-HEAP,CHECK-STACK
 
-// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN: %clang -### --target=aarch64-linux-gnu -march=armv8+memtag \
 // RUN:   -fsanitize=memtag-stack -fsanitize-memtag-mode=async %s 2>&1 \
 // RUN:   | FileCheck %s \
 // RUN:   --check-prefixes=CHECK-ASYNC,CHECK-NO-HEAP,CHECK-STACK
 
-// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN: %clang -### --target=aarch64-linux-gnu -march=armv8+memtag \
 // RUN:   -fsanitize=memtag-heap -fsanitize-memtag-mode=async %s 2>&1 \
 // RUN:   | FileCheck %s \
 // RUN:   --check-prefixes=CHECK-ASYNC,CHECK-HEAP,CHECK-NO-STACK
 
-// RUN: not %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN: not %clang -### --target=aarch64-linux-gnu -march=armv8+memtag \
 // RUN:   -fsanitize=memtag-heap -fsanitize-memtag-mode=asymm %s 2>&1 \
 // RUN:   | FileCheck %s --check-prefixes=CHECK-INVALID-MODE
 
-// RUN: not %clang -### --target=aarch64-linux-android -march=armv8+memtag \
+// RUN: not %clang -### --target=aarch64-linux-gnu -march=armv8+memtag \
 // RUN:   -fsanitize=memtag-stack -fsanitize=memtag-heap \
 // RUN:   -fsanitize-memtag-mode=asymm -fno-sanitize=memtag %s 2>&1 \
 // RUN:   | FileCheck %s --check-prefixes=CHECK-NONE
 
-// CHECK-ASYNC:             ld{{.*}} "--android-memtag-mode=async"
-// CHECK-SYNC:              ld{{.*}} "--android-memtag-mode=sync"
-// CHECK-HEAP:              "--android-memtag-heap"
-// CHECK-NO-HEAP-NOT:       "--android-memtag-heap"
-// CHECK-STACK:             "--android-memtag-stack"
-// CHECK-NO-STACK-NOT:      "--android-memtag-stack"
+// CHECK-ASYNC:             ld{{.*}} "-z" "memtag-mode=async"
+// CHECK-SYNC:              ld{{.*}} "-z" "memtag-mode=sync"
+// CHECK-HEAP:              "-z" "memtag-heap"
+// CHECK-NO-HEAP-NOT:       "-z" "memtag-heap"
+// CHECK-STACK:             "-z" "memtag-stack"
+// CHECK-NO-STACK-NOT:      "-z" "memtag-stack"
 // CHECK-INVALID-MODE:      invalid value 'asymm' in '-fsanitize-memtag-mode=',
 // CHECK-INVALID-MODE-SAME: expected one of: {async, sync}
-// CHECK-NONE-NOT:          ld{{.*}} "--android-memtag
+// CHECK-NONE-NOT:          ld{{.*}} "-z" "memtag
 
 void f() {}

>From a4346a21b7b006ec58f50dbd7a987b488de738be Mon Sep 17 00:00:00 2001
From: Elia Geretto <[email protected]>
Date: Fri, 20 Feb 2026 14:36:19 +0100
Subject: [PATCH 3/3] [AsmPrinter][MTE] Support memtag-globals for all AArch64
 targets

---
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp |  4 ++--
 llvm/test/MC/AArch64/global-tagging.ll     | 10 +++++++---
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp 
b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 083b83567e47f..0eca99cfed563 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -833,10 +833,10 @@ void AsmPrinter::emitGlobalVariable(const GlobalVariable 
*GV) {
   if (GV->isTagged()) {
     Triple T = TM.getTargetTriple();
 
-    if (T.getArch() != Triple::aarch64 || !T.isAndroid())
+    if (T.getArch() != Triple::aarch64)
       OutContext.reportError(SMLoc(),
                              "tagged symbols (-fsanitize=memtag-globals) are "
-                             "only supported on AArch64 Android");
+                             "only supported on AArch64");
     OutStreamer->emitSymbolAttribute(EmittedSym, MCSA_Memtag);
   }
 
diff --git a/llvm/test/MC/AArch64/global-tagging.ll 
b/llvm/test/MC/AArch64/global-tagging.ll
index c8b3f52401dc1..29463b1b86ff4 100644
--- a/llvm/test/MC/AArch64/global-tagging.ll
+++ b/llvm/test/MC/AArch64/global-tagging.ll
@@ -1,8 +1,12 @@
-;; Tagged symbols are only available on aarch64-linux-android.
-; RUN: not llc -filetype=null %s -mtriple=aarch64-unknown-linux 2>&1 | 
FileCheck %s --check-prefix=ERR
+;; Tagged symbols are only available on AArch64.
 ; RUN: %if x86-registered-target %{ not llc -filetype=null %s 
-mtriple=x86_64-unknown-linux 2>&1 | FileCheck %s --check-prefix=ERR %}
 
-; ERR: error: tagged symbols (-fsanitize=memtag-globals) are only supported on 
AArch64 Android
+; ERR: error: tagged symbols (-fsanitize=memtag-globals) are only supported on 
AArch64
+
+; RUN: llc %s -mtriple=aarch64-unknown-linux -o %t.linux.S
+; RUN: FileCheck %s --input-file=%t.linux.S --check-prefix=CHECK-ASM
+; RUN: llvm-mc -filetype=obj %t.linux.S -triple=aarch64-unknown-linux -o 
%t.linux.o
+; RUN: llvm-readelf -r %t.linux.o | FileCheck %s --check-prefix=CHECK-RELOCS
 
 ; RUN: llc %s -mtriple=aarch64-linux-android31 -o %t.S
 ; RUN: FileCheck %s --input-file=%t.S --check-prefix=CHECK-ASM

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to