[llvm-branch-commits] [lldb] 4263404 - [lldb] Support weakly imported symbols with arm64e (#202728)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Jun 17 12:02:51 PDT 2026


Author: Alex Langford
Date: 2026-06-17T11:29:28-07:00
New Revision: 42634048c6768891487be239c0ceb409b09da39f

URL: https://github.com/llvm/llvm-project/commit/42634048c6768891487be239c0ceb409b09da39f
DIFF: https://github.com/llvm/llvm-project/commit/42634048c6768891487be239c0ceb409b09da39f.diff

LOG: [lldb] Support weakly imported symbols with arm64e (#202728)

The use of weakly imported symbols may introduce inline CPAs into
functions in the form of instruction operands. For example, from
TestWeakSymbols.py, we may see IR that looks like this:
```
%cmp = icmp ne ptr ptrauth (ptr @_Z20absent_weak_functionv, i32 0), null
```
which corresponds to this C line:
```
if (&absent_weak_function != null) {
```

Similar to walking global initializers, LLDB must also walk all
instructions looking for CPAs in instruction operands and handle them
accordingly.

I've renamed functions and structs to distinguish between these two
scenarios.

Added: 
    lldb/test/API/commands/expression/ptrauth-weak-symbols/Makefile
    lldb/test/API/commands/expression/ptrauth-weak-symbols/TestPtrauthWeakSymbols.py
    lldb/test/API/commands/expression/ptrauth-weak-symbols/dylib.c
    lldb/test/API/commands/expression/ptrauth-weak-symbols/dylib.h
    lldb/test/API/commands/expression/ptrauth-weak-symbols/main.c
    lldb/test/API/commands/expression/ptrauth-weak-symbols/module.modulemap

Modified: 
    lldb/source/Plugins/ExpressionParser/Clang/InjectPointerSigningFixups.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/ExpressionParser/Clang/InjectPointerSigningFixups.cpp b/lldb/source/Plugins/ExpressionParser/Clang/InjectPointerSigningFixups.cpp
index e8676d4f22ece..67b5c13aeca66 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/InjectPointerSigningFixups.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/InjectPointerSigningFixups.cpp
@@ -51,7 +51,8 @@ struct ExprStep {
   unsigned OperandIdx;
 };
 
-struct PtrAuthFixup {
+/// Used to keep track of ConstantPtrAuth expressions in global initializers.
+struct GlobalInitPtrAuthFixup {
   GlobalVariable *GV;
   ConstantPtrAuth *CPA;
   /// ConstantAggregate types are walekd via GEP indices.
@@ -59,19 +60,29 @@ struct PtrAuthFixup {
   /// ConstantExpr types are traversed via ExprStep (ConstantExpr + Operand
   /// index).
   SmallVector<ExprStep> ExprPath;
-  PtrAuthFixup(GlobalVariable *GV, ConstantPtrAuth *CPA,
-               const SmallVectorImpl<unsigned> &GEPPath,
-               const SmallVectorImpl<ExprStep> &ExprPath)
+  GlobalInitPtrAuthFixup(GlobalVariable *GV, ConstantPtrAuth *CPA,
+                         const SmallVectorImpl<unsigned> &GEPPath,
+                         const SmallVectorImpl<ExprStep> &ExprPath)
       : GV(GV), CPA(CPA), GEPPath(GEPPath.begin(), GEPPath.end()),
         ExprPath(ExprPath.begin(), ExprPath.end()) {}
 };
+
+/// Used to keep track of extern_weak ConstantPtrAuth expressions inlined in
+/// instructions.
+struct WeakInlinePtrAuthFixup {
+  Instruction *Inst;
+  unsigned OperandIdx;
+  ConstantPtrAuth *CPA;
+};
 } // namespace
 
-/// Recursively walk a constant looking for ConstantPtrAuth expressions.
-static void findPtrAuth(Constant *C, GlobalVariable &GV,
-                        SmallVectorImpl<unsigned> &GEPPath,
-                        SmallVectorImpl<ExprStep> &ExprPath,
-                        SmallVectorImpl<PtrAuthFixup> &Fixups) {
+/// Recursively walk a constant looking for ConstantPtrAuth expressions in
+/// global initializers.
+static void
+findGlobalInitPtrAuth(Constant *C, GlobalVariable &GV,
+                      SmallVectorImpl<unsigned> &GEPPath,
+                      SmallVectorImpl<ExprStep> &ExprPath,
+                      SmallVectorImpl<GlobalInitPtrAuthFixup> &Fixups) {
   if (auto *CPA = dyn_cast<ConstantPtrAuth>(C)) {
     Fixups.emplace_back(&GV, CPA, GEPPath, ExprPath);
     return;
@@ -80,7 +91,7 @@ static void findPtrAuth(Constant *C, GlobalVariable &GV,
     for (unsigned I = 0, E = C->getNumOperands(); I != E; ++I) {
       if (auto *COp = dyn_cast<Constant>(C->getOperand(I))) {
         GEPPath.push_back(I);
-        findPtrAuth(COp, GV, GEPPath, ExprPath, Fixups);
+        findGlobalInitPtrAuth(COp, GV, GEPPath, ExprPath, Fixups);
         GEPPath.pop_back();
       }
     }
@@ -91,7 +102,7 @@ static void findPtrAuth(Constant *C, GlobalVariable &GV,
     for (unsigned I = 0, E = C->getNumOperands(); I != E; ++I) {
       if (auto *COp = dyn_cast<Constant>(C->getOperand(I))) {
         ExprPath.push_back({CE, I});
-        findPtrAuth(COp, GV, GEPPath, ExprPath, Fixups);
+        findGlobalInitPtrAuth(COp, GV, GEPPath, ExprPath, Fixups);
         ExprPath.pop_back();
       }
     }
@@ -113,16 +124,37 @@ Error InjectPointerSigningFixupCode(llvm::Module &M,
     return Error::success();
 
   // Collect all ConstantPtrAuth expressions in global initializers.
-  SmallVector<PtrAuthFixup> Fixups;
+  SmallVector<GlobalInitPtrAuthFixup> GlobalInitFixups;
   for (auto &G : M.globals()) {
     if (!G.hasInitializer())
       continue;
     SmallVector<unsigned> GEPPath;
     SmallVector<ExprStep> ExprPath;
-    findPtrAuth(G.getInitializer(), G, GEPPath, ExprPath, Fixups);
+    findGlobalInitPtrAuth(G.getInitializer(), G, GEPPath, ExprPath,
+                          GlobalInitFixups);
+  }
+
+  // Collect all inline ConstantPtrAuth expressions for extern_weak globals in
+  // functions.
+  SmallVector<WeakInlinePtrAuthFixup> WeakInlineFixups;
+  for (auto &F : M.functions()) {
+    for (auto &BB : F) {
+      for (auto &Inst : BB) {
+        for (unsigned OpIdx = 0, E = Inst.getNumOperands(); OpIdx != E;
+             OpIdx++) {
+          auto *CPA = dyn_cast<ConstantPtrAuth>(Inst.getOperand(OpIdx));
+          if (!CPA)
+            continue;
+          auto *GV = dyn_cast<GlobalValue>(CPA->getPointer());
+          if (!GV || !GV->hasExternalWeakLinkage())
+            continue;
+          WeakInlineFixups.push_back({&Inst, OpIdx, CPA});
+        }
+      }
+    }
   }
 
-  if (Fixups.empty())
+  if (GlobalInitFixups.empty() && WeakInlineFixups.empty())
     return Error::success();
 
   // Set up types and intrinsics.
@@ -141,7 +173,7 @@ Error InjectPointerSigningFixupCode(llvm::Module &M,
   FixupFn->insert(FixupFn->end(), BasicBlock::Create(Ctx));
   IRBuilder<> B(&FixupFn->back());
 
-  for (auto &Fixup : Fixups) {
+  for (auto &Fixup : GlobalInitFixups) {
     GlobalVariable *GV = Fixup.GV;
     ConstantPtrAuth *CPA = Fixup.CPA;
 
@@ -202,6 +234,30 @@ Error InjectPointerSigningFixupCode(llvm::Module &M,
   // Close off the fixup function.
   B.CreateRetVoid();
 
+  // Rewrite extern_weak inline CPA operands.
+  for (auto &Fixup : WeakInlineFixups) {
+    IRBuilder<> B(Fixup.Inst);
+    ConstantPtrAuth *CPA = Fixup.CPA;
+    Type *PtrTy = CPA->getType();
+
+    Value *Disc = CPA->getDiscriminator();
+    if (CPA->hasAddressDiscriminator()) {
+      Value *AddrDisc =
+          B.CreatePointerCast(CPA->getAddrDiscriminator(), IntPtrTy);
+      Disc = B.CreateCall(BlendIntrinsic, {AddrDisc, Disc});
+    }
+
+    // Signing a pointer value of `0x0` yields a non-null but invalid pointer.
+    // We'll emit a runtime guard around signing the pointer.
+    Value *RawPtr = B.CreatePtrToInt(CPA->getPointer(), IntPtrTy);
+    Value *NullCheck = B.CreateIsNull(RawPtr);
+    Value *SignedPtr =
+        B.CreateCall(SignIntrinsic, {RawPtr, CPA->getKey(), Disc});
+    Value *Result =
+        B.CreateSelect(NullCheck, Constant::getNullValue(IntPtrTy), SignedPtr);
+    Fixup.Inst->setOperand(Fixup.OperandIdx, B.CreateIntToPtr(Result, PtrTy));
+  }
+
   // Update the global ctors list to call the pointer fixup function first.
   auto *UInt8PtrTy = PointerType::getUnqual(Ctx);
   StructType *CtorType =

diff  --git a/lldb/test/API/commands/expression/ptrauth-weak-symbols/Makefile b/lldb/test/API/commands/expression/ptrauth-weak-symbols/Makefile
new file mode 100644
index 0000000000000..1636e9b303263
--- /dev/null
+++ b/lldb/test/API/commands/expression/ptrauth-weak-symbols/Makefile
@@ -0,0 +1,20 @@
+C_SOURCES := main.c
+CFLAGS_EXTRAS := -std=c99 -fmodules
+LD_EXTRAS := -ldylib -L.
+
+all: a.out hidden/libdylib.dylib
+
+a.out: libdylib.dylib
+
+include Makefile.rules
+
+libdylib.dylib: dylib.c
+	"$(MAKE)" -C $(BUILDDIR) -f $(MAKEFILE_RULES) \
+		C_SOURCES= DYLIB_C_SOURCES=dylib.c DYLIB_NAME=dylib \
+		CFLAGS_EXTRAS=-DHAS_THEM LD_EXTRAS=-dynamiclib
+
+hidden/libdylib.dylib:
+	mkdir hidden
+	"$(MAKE)" -C $(BUILDDIR)/hidden -f $(MAKEFILE_RULES) \
+		C_SOURCES= DYLIB_C_SOURCES=dylib.c DYLIB_NAME=dylib \
+		LD_EXTRAS=-dynamiclib

diff  --git a/lldb/test/API/commands/expression/ptrauth-weak-symbols/TestPtrauthWeakSymbols.py b/lldb/test/API/commands/expression/ptrauth-weak-symbols/TestPtrauthWeakSymbols.py
new file mode 100644
index 0000000000000..e570d8fec1727
--- /dev/null
+++ b/lldb/test/API/commands/expression/ptrauth-weak-symbols/TestPtrauthWeakSymbols.py
@@ -0,0 +1,65 @@
+"""
+Test that we can compile expressions referring to absent weak symbols from a dylib on arm64e.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbarm64e import Arm64eTestBase
+
+
+class TestWeakSymbolsInExpressionsOnArm64e(Arm64eTestBase):
+    SHARED_BUILD_TESTCASE = False
+    NO_DEBUG_INFO_TESTCASE = True
+
+    @skipIf(compiler="clang", compiler_version=["<", "19.0"])
+    def test_weak_symbol_in_expr(self):
+        self.build()
+
+        hidden_dir = os.path.join(self.getBuildDir(), "hidden")
+        hidden_dylib = os.path.join(hidden_dir, "libdylib.dylib")
+
+        launch_info = lldb.SBLaunchInfo(None)
+        launch_info.SetWorkingDirectory(self.getBuildDir())
+        launch_info.SetLaunchFlags(lldb.eLaunchFlagInheritTCCFromParent)
+
+        self.target, _, thread, _ = lldbutil.run_to_source_breakpoint(
+            self,
+            "Set a breakpoint here",
+            lldb.SBFileSpec("main.c"),
+            launch_info=launch_info,
+            extra_images=[hidden_dylib],
+        )
+
+        # First import the dylib module to get the type info for the weak
+        # symbol. Add the source dir to the module search path and then run
+        # @import to introduce it into the expression context.
+        self.dbg.HandleCommand(
+            f"settings set target.clang-module-search-paths {self.getSourceDir()}"
+        )
+
+        self.frame = thread.frames[0]
+        self.assertTrue(self.frame.IsValid(), "Got a good frame")
+        options = lldb.SBExpressionOptions()
+        options.SetLanguage(lldb.eLanguageTypeObjC)
+        result = self.frame.EvaluateExpression("@import Dylib", options)
+
+        # Now run expressions that reference absent and present weak function symbols.
+        absent_expr = (
+            "if (&absent_weak_function != NULL) { sink = 100; } else { sink = 0; }; 10"
+        )
+        present_expr = (
+            "if (&present_weak_function != NULL) { sink = 100; } else { sink = 0; }; 10"
+        )
+
+        value = self.target.FindFirstGlobalVariable("sink")
+        value.SetValueFromCString("0")
+
+        result = self.frame.EvaluateExpression(absent_expr)
+        self.assertSuccess(result.GetError(), "absent_weak_function expr failed")
+        self.assertEqual(value.GetValueAsSigned(), 0)
+
+        result = self.frame.EvaluateExpression(present_expr)
+        self.assertSuccess(result.GetError(), "present_weak_function expr failed")
+        self.assertEqual(value.GetValueAsSigned(), 100)

diff  --git a/lldb/test/API/commands/expression/ptrauth-weak-symbols/dylib.c b/lldb/test/API/commands/expression/ptrauth-weak-symbols/dylib.c
new file mode 100644
index 0000000000000..4a86442086de1
--- /dev/null
+++ b/lldb/test/API/commands/expression/ptrauth-weak-symbols/dylib.c
@@ -0,0 +1,7 @@
+#include "dylib.h"
+
+int present_weak_function() { return 10; }
+
+#if defined(HAS_THEM)
+int absent_weak_function() { return 15; }
+#endif

diff  --git a/lldb/test/API/commands/expression/ptrauth-weak-symbols/dylib.h b/lldb/test/API/commands/expression/ptrauth-weak-symbols/dylib.h
new file mode 100644
index 0000000000000..fbc0eee87b48e
--- /dev/null
+++ b/lldb/test/API/commands/expression/ptrauth-weak-symbols/dylib.h
@@ -0,0 +1,3 @@
+extern int absent_weak_function() __attribute__((weak_import));
+
+extern int present_weak_function() __attribute__((weak_import));

diff  --git a/lldb/test/API/commands/expression/ptrauth-weak-symbols/main.c b/lldb/test/API/commands/expression/ptrauth-weak-symbols/main.c
new file mode 100644
index 0000000000000..d7f8253173283
--- /dev/null
+++ b/lldb/test/API/commands/expression/ptrauth-weak-symbols/main.c
@@ -0,0 +1,13 @@
+#include "dylib.h"
+
+int sink = 0;
+
+int main() {
+  // Set a breakpoint here
+  if (absent_weak_function)
+    sink = 6;
+  if (present_weak_function)
+    sink = 7;
+
+  return sink;
+}

diff  --git a/lldb/test/API/commands/expression/ptrauth-weak-symbols/module.modulemap b/lldb/test/API/commands/expression/ptrauth-weak-symbols/module.modulemap
new file mode 100644
index 0000000000000..6f7671400bc33
--- /dev/null
+++ b/lldb/test/API/commands/expression/ptrauth-weak-symbols/module.modulemap
@@ -0,0 +1,3 @@
+module Dylib {
+  header "dylib.h"
+}


        


More information about the llvm-branch-commits mailing list