[clang] [lld] [lld][ELF][clang][MTE] Add generic -z memtag-* options (PR #187055)
Elia Geretto via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 23 02:43:57 PDT 2026
https://github.com/EliaGeretto updated https://github.com/llvm/llvm-project/pull/187055
>From 86a881c3c91f5d99a388403243cfb8e295f79827 Mon Sep 17 00:00:00 2001
From: Elia Geretto <egeretto at qti.qualcomm.com>
Date: Fri, 20 Feb 2026 12:19:38 +0100
Subject: [PATCH] [lld][ELF][MTE] Add generic -z memtag-* options
This change eliminates the Android-specific --android-memtag-* flags
from lld, replacing them with -z memtag-* generic equivalents. With
these generic flags, the linker will emit only the dynamic array tags
specified in the "Memtag ABI Extension to ELF", but no Android-specific
memtag note.
In addition, this change adds an --android-memtag-note flag which should
be used when the Android-specific memtag note should be emitted.
This change also modifies the clang driver to make use of the new flags.
---
clang/lib/Driver/ToolChains/CommonArgs.cpp | 24 +++++---
clang/test/Driver/fsanitize-memtag.c | 2 +-
.../Driver/{memtag-ld.c => memtag-android.c} | 37 +++++++----
clang/test/Driver/memtag.c | 54 ++++++++++++++++
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 +-
13 files changed, 241 insertions(+), 98 deletions(-)
rename clang/test/Driver/{memtag-ld.c => memtag-android.c} (53%)
create mode 100644 clang/test/Driver/memtag.c
create mode 100644 lld/test/ELF/aarch64-memtag-abi.s
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 9a17fa2546e68..bfa2667e566e1 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1796,16 +1796,22 @@ 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-ld.c b/clang/test/Driver/memtag-android.c
similarity index 53%
rename from clang/test/Driver/memtag-ld.c
rename to clang/test/Driver/memtag-android.c
index aef08ddc5758a..06aee761c066f 100644
--- a/clang/test/Driver/memtag-ld.c
+++ b/clang/test/Driver/memtag-android.c
@@ -1,28 +1,28 @@
// 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
+// RUN: --check-prefixes=CHECK-CC1-ALL,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
+// RUN: --check-prefixes=CHECK-CC1-STACK,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
+// RUN: --check-prefixes=CHECK-CC1-HEAP,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
+// RUN: FileCheck %s --check-prefixes=CHECK-CC1-ALL,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
+// RUN: --check-prefixes=CHECK-CC1-STACK,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
+// RUN: --check-prefixes=CHECK-CC1-HEAP,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 \
@@ -33,14 +33,25 @@
// 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-CC1-ALL: "-fsanitize=memtag-stack,memtag-heap,memtag-globals"
+// CHECK-CC1-STACK: "-fsanitize=memtag-stack"
+// CHECK-CC1-HEAP: "-fsanitize=memtag-heap"
+
+// 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{{.*}} "--android-memtag
+
+// CHECK-NONE-NOT: "-fsanitize=memtag-stack"
+// CHECK-NONE-NOT: "-fsanitize=memtag-heap"
+// CHECK-NONE-NOT: ld{{.*}} "-z" "memtag
+// CHECK-NONE-NOT: ld{{.*}} "--android-memtag-note"
void f() {}
diff --git a/clang/test/Driver/memtag.c b/clang/test/Driver/memtag.c
new file mode 100644
index 0000000000000..488acf68aa422
--- /dev/null
+++ b/clang/test/Driver/memtag.c
@@ -0,0 +1,54 @@
+// RUN: %clang -### --target=aarch64-linux-gnu -march=armv8+memtag \
+// RUN: -fsanitize=memtag %s 2>&1 | FileCheck %s \
+// RUN: --check-prefixes=CHECK-CC1-ALL,CHECK-SYNC,CHECK-HEAP,CHECK-STACK
+
+// RUN: %clang -### --target=aarch64-linux-gnu -march=armv8+memtag \
+// RUN: -fsanitize=memtag-stack %s 2>&1 | FileCheck %s \
+// RUN: --check-prefixes=CHECK-CC1-STACK,CHECK-SYNC,CHECK-NO-HEAP,CHECK-STACK
+
+// RUN: %clang -### --target=aarch64-linux-gnu -march=armv8+memtag \
+// RUN: -fsanitize=memtag-heap %s 2>&1 | FileCheck %s \
+// RUN: --check-prefixes=CHECK-CC1-HEAP,CHECK-SYNC,CHECK-HEAP,CHECK-NO-STACK
+
+// 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-CC1-ALL,CHECK-ASYNC,CHECK-HEAP,CHECK-STACK
+
+// 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-CC1-STACK,CHECK-ASYNC,CHECK-NO-HEAP,CHECK-STACK
+
+// 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-CC1-HEAP,CHECK-ASYNC,CHECK-HEAP,CHECK-NO-STACK
+
+// 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-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-CC1-ALL: "-fsanitize=memtag-stack,memtag-heap,memtag-globals"
+// CHECK-CC1-STACK: "-fsanitize=memtag-stack"
+// CHECK-CC1-HEAP: "-fsanitize=memtag-heap"
+
+// 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: "-fsanitize=memtag-stack"
+// CHECK-NONE-NOT: "-fsanitize=memtag-heap"
+// CHECK-NONE-NOT: ld{{.*}} "-z" "memtag
+
+void f() {}
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 1b7b3a79554f3..8df13a61ea3dd 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -510,17 +510,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 ded79d6ec47ed..a6914616b8886 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 8157365f89b30..6b51fa41f0bf0 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1334,9 +1334,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);
@@ -4299,7 +4300,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
@@ -4327,12 +4328,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
}
@@ -4527,8 +4528,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
More information about the cfe-commits
mailing list