[llvm] [ASan] Emit global metadata in a named section (PR #212890)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 4 08:28:17 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-compiler-rt-sanitizer
Author: Schrodinger ZHU Yifan (SchrodingerZhu)
<details>
<summary>Changes</summary>
Introduce `-asan-globals-metadata-section` option to emit global variable descriptions in a user-specified named section.
The idea originates from @<!-- -->ostannard's baremetal ASan demo. The motivation is that baremetals may not have a standard startup process and may need special handling for the metadata arrangement. This option allows a baremetal firmware to initialize its shadow properly during the booting process by iterating metadata from a dedicated section.
This implementation exposes the option as a string, allowing the user to specify the section name (e.g. -asan-globals-metadata-section=my_section). If the option is empty (default), the standard registration mechanism is used.
Co-authored-by: Oliver Stannard <oliver.stannard@<!-- -->arm.com>
Assisted-by: Gemini based automation tools (human-in-the-loop)
---
Full diff: https://github.com/llvm/llvm-project/pull/212890.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp (+38-1)
- (added) llvm/test/Instrumentation/AddressSanitizer/global-metadata-section.ll (+13)
``````````diff
diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
index 2fae7d8c15ff3..67469364fae24 100644
--- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
@@ -19,6 +19,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DepthFirstIterator.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
@@ -469,6 +470,12 @@ static cl::opt<int> ClDebugMin("asan-debug-min", cl::desc("Debug min inst"),
static cl::opt<int> ClDebugMax("asan-debug-max", cl::desc("Debug max inst"),
cl::Hidden, cl::init(-1));
+static cl::opt<std::string> ClGlobalsMetadataSection(
+ "asan-globals-metadata-section",
+ cl::desc("Emit global variable descriptions in a named section for "
+ "baremetal/linker-script targets"),
+ cl::init(""));
+
STATISTIC(NumInstrumentedReads, "Number of instrumented reads");
STATISTIC(NumInstrumentedWrites, "Number of instrumented writes");
STATISTIC(NumOptimizedAccessesToGlobalVar,
@@ -1016,6 +1023,9 @@ class ModuleAddressSanitizer {
ArrayRef<GlobalVariable *> ExtendedGlobals,
ArrayRef<Constant *> MetadataInitializers,
const std::string &UniqueModuleId);
+ void InstrumentGlobalsMetadataSection(
+ IRBuilder<> &IRB, ArrayRef<GlobalVariable *> ExtendedGlobals,
+ ArrayRef<Constant *> MetadataInitializers, StringRef SectionName);
void InstrumentGlobalsMachO(IRBuilder<> &IRB,
ArrayRef<GlobalVariable *> ExtendedGlobals,
ArrayRef<Constant *> MetadataInitializers);
@@ -2535,6 +2545,26 @@ void ModuleAddressSanitizer::instrumentGlobalsELF(
}
}
+void ModuleAddressSanitizer::InstrumentGlobalsMetadataSection(
+ IRBuilder<> &IRB, ArrayRef<GlobalVariable *> ExtendedGlobals,
+ ArrayRef<Constant *> MetadataInitializers, StringRef SectionName) {
+ auto MetadataGlobals = llvm::to_vector_of<GlobalValue *>(llvm::map_range(
+ llvm::zip_equal(ExtendedGlobals, MetadataInitializers), [&](auto &&Pair) {
+ auto [G, Initializer] = Pair;
+ GlobalVariable *Metadata =
+ CreateMetadataGlobal(Initializer, G->getName());
+ MDNode *MD = MDNode::get(M.getContext(), ValueAsMetadata::get(G));
+ Metadata->setMetadata(LLVMContext::MD_associated, MD);
+ Metadata->setSection(SectionName);
+ return Metadata;
+ }));
+
+ // Update llvm.compiler.used, adding the new metadata globals. This is
+ // needed so that during LTO these variables stay alive.
+ if (!MetadataGlobals.empty())
+ appendToCompilerUsed(M, MetadataGlobals);
+}
+
void ModuleAddressSanitizer::InstrumentGlobalsMachO(
IRBuilder<> &IRB, ArrayRef<GlobalVariable *> ExtendedGlobals,
ArrayRef<Constant *> MetadataInitializers) {
@@ -2781,7 +2811,14 @@ void ModuleAddressSanitizer::instrumentGlobals(IRBuilder<> &IRB,
}
appendToCompilerUsed(M, ArrayRef<GlobalValue *>(GlobalsToAddToUsedList));
- if (UseGlobalsGC && TargetTriple.isOSBinFormatELF()) {
+ std::string ELFUniqueModuleId =
+ (UseGlobalsGC && TargetTriple.isOSBinFormatELF()) ? getUniqueModuleId(&M)
+ : "";
+
+ if (!ClGlobalsMetadataSection.empty()) {
+ InstrumentGlobalsMetadataSection(IRB, NewGlobals, Initializers,
+ ClGlobalsMetadataSection);
+ } else if (UseGlobalsGC && TargetTriple.isOSBinFormatELF()) {
// Use COMDAT and register globals even if n == 0 to ensure that (a) the
// linkage unit will only have one module constructor, and (b) the register
// function will be called. The module destructor is not created when n ==
diff --git a/llvm/test/Instrumentation/AddressSanitizer/global-metadata-section.ll b/llvm/test/Instrumentation/AddressSanitizer/global-metadata-section.ll
new file mode 100644
index 0000000000000..9ccbc0c3c19a5
--- /dev/null
+++ b/llvm/test/Instrumentation/AddressSanitizer/global-metadata-section.ll
@@ -0,0 +1,13 @@
+; Test that the -asan-globals-metadata-section option works as expected
+;
+; RUN: opt < %s -passes=asan -asan-globals-metadata-section=my_section -S | FileCheck %s
+target triple = "x86_64-unknown-linux-gnu"
+
+ at global = global i32 0, align 4
+
+; CHECK: @__asan_global_global = {{.*}} section "my_section"{{.*}}
+; CHECK: @llvm.compiler.used = {{.*}}ptr @__asan_global_global{{.*}}
+
+; CHECK: define internal void @asan.module_ctor()
+; CHECK-NOT: __asan_register
+; CHECK: ret void
``````````
</details>
https://github.com/llvm/llvm-project/pull/212890
More information about the llvm-commits
mailing list