[Lldb-commits] [lldb] [lldb] Support PtrAuth in the expression evaluator (PR #186001)

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Wed Mar 11 16:54:48 PDT 2026


https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/186001

>From f0dc8d382443969353593f74d6dd5621bcea4c5e Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Wed, 11 Mar 2026 16:21:35 -0700
Subject: [PATCH] [lldb] Support PtrAuth in the expression evaluator

---
 .../ExpressionParser/Clang/CMakeLists.txt     |   3 +-
 .../Clang/ClangExpressionParser.cpp           |  23 ++-
 .../Clang/ClangExpressionParser.h             |  10 +-
 .../Clang/ClangFunctionCaller.cpp             |   8 +-
 .../ExpressionParser/Clang/IRForTarget.cpp    |  14 +-
 .../ExpressionParser/Clang/IRForTarget.h      |   3 +
 .../Clang/InjectPointerSigningFixups.cpp      | 176 ++++++++++++++++++
 .../Clang/InjectPointerSigningFixups.h        |  27 +++
 .../CPlusPlus/CPPLanguageRuntime.cpp          |  12 ++
 .../expression/ptr-auth-fixups/Makefile       |   5 +
 .../ptr-auth-fixups/TestPtrAuthFixups.py      |  29 +++
 .../expression/ptr-auth-fixups/main.c         |   9 +
 12 files changed, 313 insertions(+), 6 deletions(-)
 create mode 100644 lldb/source/Plugins/ExpressionParser/Clang/InjectPointerSigningFixups.cpp
 create mode 100644 lldb/source/Plugins/ExpressionParser/Clang/InjectPointerSigningFixups.h
 create mode 100644 lldb/test/API/commands/expression/ptr-auth-fixups/Makefile
 create mode 100644 lldb/test/API/commands/expression/ptr-auth-fixups/TestPtrAuthFixups.py
 create mode 100644 lldb/test/API/commands/expression/ptr-auth-fixups/main.c

diff --git a/lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt b/lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt
index fe56d5072ce3a..5a4a161e34d53 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt
+++ b/lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt
@@ -21,8 +21,9 @@ add_lldb_library(lldbPluginExpressionParserClang
   ClangUtilityFunction.cpp
   CppModuleConfiguration.cpp
   CxxModuleHandler.cpp
-  IRForTarget.cpp
   IRDynamicChecks.cpp
+  IRForTarget.cpp
+  InjectPointerSigningFixups.cpp
   NameSearchContext.cpp
 
   DEPENDS
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
index 176eddf65e911..f5a2c2b107173 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
@@ -728,7 +728,8 @@ static void SetupImportStdModuleLangOpts(CompilerInstance &compiler,
 ClangExpressionParser::ClangExpressionParser(
     ExecutionContextScope *exe_scope, Expression &expr,
     bool generate_debug_info, DiagnosticManager &diagnostic_manager,
-    std::vector<std::string> include_directories, std::string filename)
+    std::vector<std::string> include_directories, std::string filename,
+    bool force_disable_ptrauth_codegen)
     : ExpressionParser(exe_scope, expr, generate_debug_info), m_compiler(),
       m_pp_callbacks(nullptr),
       m_include_directories(std::move(include_directories)),
@@ -766,6 +767,11 @@ ClangExpressionParser::ClangExpressionParser(
   // 2. Configure the compiler with a set of default options that are
   // appropriate for most situations.
   SetupTargetOpts(*m_compiler, *target_sp);
+  llvm::Triple triple = target_sp->GetArchitecture().GetTriple();
+  if (triple.isArm64e() && !force_disable_ptrauth_codegen) {
+    m_compiler->getLangOpts().PointerAuthIntrinsics = true;
+    m_compiler->getLangOpts().PointerAuthCalls = true;
+  }
 
   // 3. Create and install the target on the compiler.
   m_compiler->createDiagnostics();
@@ -794,6 +800,12 @@ ClangExpressionParser::ClangExpressionParser(
 
   // 4. Set language options.
   SetupLangOpts(*m_compiler, *exe_scope, expr, diagnostic_manager);
+  if (triple.isArm64e() && !force_disable_ptrauth_codegen) {
+    auto &lang_opts = m_compiler->getLangOpts();
+    lang_opts.PointerAuthIntrinsics = true;
+    lang_opts.PointerAuthCalls = true;
+  }
+
   auto *clang_expr = dyn_cast<ClangUserExpression>(&m_expr);
   if (clang_expr && clang_expr->DidImportCxxModules()) {
     LLDB_LOG(log, "Adding lang options for importing C++ modules");
@@ -811,6 +823,13 @@ ClangExpressionParser::ClangExpressionParser(
   else
     m_compiler->getCodeGenOpts().setDebugInfo(codegenoptions::NoDebugInfo);
 
+  ArchSpec target_arch = target_sp->GetArchitecture();
+  if (triple.isArm64e() && !force_disable_ptrauth_codegen) {
+    PointerAuthOptions &ptrauth_opts = m_compiler->getCodeGenOpts().PointerAuth;
+    clang::CompilerInvocation::setDefaultPointerAuthOptions(
+        ptrauth_opts, m_compiler->getLangOpts(), triple);
+  }
+
   // Disable some warnings.
   SetupDefaultClangDiagnostics(*m_compiler);
 
@@ -1542,7 +1561,7 @@ lldb_private::Status ClangExpressionParser::DoPrepareForExecution(
     StreamString error_stream;
     IRForTarget ir_for_target(decl_map, m_expr.NeedsVariableResolution(),
                               *execution_unit_sp, error_stream,
-                              function_name.AsCString());
+                              execution_policy, function_name.AsCString());
 
     if (!ir_for_target.runOnModule(*execution_unit_sp->GetModule())) {
       err = Status(error_stream.GetString().str());
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
index 734ad51c9646e..8b15a30c07610 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
@@ -63,11 +63,19 @@ class ClangExpressionParser : public ExpressionParser {
   /// @param[in] filename
   ///     Name of the source file that should be used when rendering
   ///     diagnostics (i.e. errors, warnings or notes from Clang).
+  ///
+  /// \param[in] force_disable_ptrauth_codegen
+  ///     (rare) force pointer authentication code generation to be
+  ///     disabled for this expression.  Normally the decision of
+  ///     whether to generate ptrauth codegen or not is determined
+  ///     by the ArchSpec or ABI; this is for overriding the normal
+  ///     codegen.
   ClangExpressionParser(ExecutionContextScope *exe_scope, Expression &expr,
                         bool generate_debug_info,
                         DiagnosticManager &diagnostic_manager,
                         std::vector<std::string> include_directories = {},
-                        std::string filename = "<clang expression>");
+                        std::string filename = "<clang expression>",
+                        bool force_disable_ptrauth_codegen = false);
 
   /// Destructor
   ~ClangExpressionParser() override;
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp
index d2db319afb7a0..76c24454ffbbf 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp
@@ -188,9 +188,15 @@ ClangFunctionCaller::CompileFunction(lldb::ThreadSP thread_to_use_sp,
 
   lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock());
   if (jit_process_sp) {
+    // We will be passing in unauthenticated function addresses to the
+    // FunctionCaller code, so we need to force disable pointer auth
+    // codegen for this one code snippet.
+    const bool force_disable_ptrauth_codegen = true;
     const bool generate_debug_info = true;
     auto *clang_parser = new ClangExpressionParser(
-        jit_process_sp.get(), *this, generate_debug_info, diagnostic_manager);
+        jit_process_sp.get(), *this, generate_debug_info, diagnostic_manager,
+        std::vector<std::string>(), "<clang expression>",
+        force_disable_ptrauth_codegen);
     num_errors = clang_parser->Parse(diagnostic_manager);
     m_parser.reset(clang_parser);
   } else {
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp b/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp
index a343766ce9c4f..2789f0a10b5ad 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "IRForTarget.h"
+#include "InjectPointerSigningFixups.h"
 
 #include "ClangExpressionDeclMap.h"
 #include "ClangUtil.h"
@@ -73,10 +74,11 @@ IRForTarget::IRForTarget(lldb_private::ClangExpressionDeclMap *decl_map,
                          bool resolve_vars,
                          lldb_private::IRExecutionUnit &execution_unit,
                          lldb_private::Stream &error_stream,
+                         lldb_private::ExecutionPolicy execution_policy,
                          const char *func_name)
     : m_resolve_vars(resolve_vars), m_func_name(func_name),
       m_decl_map(decl_map), m_error_stream(error_stream),
-      m_execution_unit(execution_unit),
+      m_execution_unit(execution_unit), m_policy(execution_policy),
       m_entry_instruction_finder(FindEntryInstruction) {}
 
 /* Handy utility functions used at several places in the code */
@@ -1758,6 +1760,16 @@ bool IRForTarget::runOnModule(Module &llvm_module) {
     }
   }
 
+  ////////////////////////////////////////////////////////////////////////
+  // Run function-level passes that only make sense on the main function
+  //
+  if (auto Err =
+          lldb_private::InjectPointerSigningFixupCode(*m_module, m_policy)) {
+    LLDB_LOGF(log, "InsertPointerSigningFixups() failed:\n\"%s\"",
+              toString(std::move(Err)).c_str());
+    return false;
+  }
+
   if (log && log->GetVerbose()) {
     std::string s;
     raw_string_ostream oss(s);
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h b/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h
index 45027fcd6fa49..d710bdc950935 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h
+++ b/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h
@@ -85,6 +85,7 @@ class IRForTarget {
   IRForTarget(lldb_private::ClangExpressionDeclMap *decl_map, bool resolve_vars,
               lldb_private::IRExecutionUnit &execution_unit,
               lldb_private::Stream &error_stream,
+              lldb_private::ExecutionPolicy execution_policy,
               const char *func_name = "$__lldb_expr");
 
   /// Run this IR transformer on a single module
@@ -352,6 +353,8 @@ class IRForTarget {
   /// ASTResultSynthesizer::SynthesizeBodyResult)
   bool m_result_is_pointer = false;
 
+  lldb_private::ExecutionPolicy m_policy;
+
   class FunctionValueCache {
   public:
     typedef std::function<llvm::Value *(llvm::Function *)> Maker;
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/InjectPointerSigningFixups.cpp b/lldb/source/Plugins/ExpressionParser/Clang/InjectPointerSigningFixups.cpp
new file mode 100644
index 0000000000000..f2e75968e2635
--- /dev/null
+++ b/lldb/source/Plugins/ExpressionParser/Clang/InjectPointerSigningFixups.cpp
@@ -0,0 +1,176 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "InjectPointerSigningFixups.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/Triple.h"
+
+using namespace llvm;
+
+namespace {
+
+struct PtrAuthFixup {
+  GlobalVariable *GV;
+  ConstantPtrAuth *CPA;
+  SmallVector<unsigned, 4> Indices;
+};
+
+/// Recursively walk a constant looking for ConstantPtrAuth expressions.
+/// When found, record the global variable containing it and the index path
+/// to reach it within the initializer.
+void findPtrAuth(Constant *C, GlobalVariable &GV,
+                 SmallVector<unsigned, 4> &Indices,
+                 SmallVectorImpl<PtrAuthFixup> &Fixups) {
+  if (auto *CPA = dyn_cast<ConstantPtrAuth>(C)) {
+    Fixups.push_back({&GV, CPA, Indices});
+    return;
+  }
+  for (unsigned I = 0, E = C->getNumOperands(); I != E; ++I) {
+    if (auto *COp = dyn_cast<Constant>(C->getOperand(I))) {
+      Indices.push_back(I);
+      findPtrAuth(COp, GV, Indices, Fixups);
+      Indices.pop_back();
+    }
+  }
+}
+
+} // namespace
+
+namespace lldb_private {
+
+Error InjectPointerSigningFixupCode(llvm::Module &M,
+                                    ExecutionPolicy execution_policy) {
+  // If we cannot execute fixups, don't insert them.
+  if (execution_policy == eExecutionPolicyNever)
+    return Error::success();
+
+  llvm::Triple T(M.getTargetTriple());
+
+  // Bail out if we don't need pointer signing fixups.
+  if (!T.isArm64e())
+    return Error::success();
+
+  // Collect all ConstantPtrAuth expressions in global initializers.
+  SmallVector<PtrAuthFixup, 8> Fixups;
+  for (auto &G : M.globals()) {
+    if (!G.hasInitializer())
+      continue;
+    SmallVector<unsigned, 4> Indices;
+    findPtrAuth(G.getInitializer(), G, Indices, Fixups);
+  }
+
+  if (Fixups.empty())
+    return Error::success();
+
+  // Set up types and intrinsics.
+  auto &Ctx = M.getContext();
+  Type *Int32Ty = Type::getInt32Ty(Ctx);
+  Type *IntPtrTy = Type::getInt64Ty(Ctx);
+  Function *BlendIntrinsic =
+      Intrinsic::getOrInsertDeclaration(&M, Intrinsic::ptrauth_blend);
+  Function *SignIntrinsic =
+      Intrinsic::getOrInsertDeclaration(&M, Intrinsic::ptrauth_sign);
+
+  // Create the fixup function.
+  Function *FixupFn = Function::Create(
+      FunctionType::get(Type::getVoidTy(Ctx), false),
+      GlobalValue::InternalLinkage, "lldb.arm64.sign_pointers", &M);
+  FixupFn->insert(FixupFn->end(), BasicBlock::Create(Ctx));
+  IRBuilder<> B(&FixupFn->back());
+
+  for (auto &Fixup : Fixups) {
+    GlobalVariable *GV = Fixup.GV;
+    ConstantPtrAuth *CPA = Fixup.CPA;
+
+    // Build a GEP to the location of the ConstantPtrAuth within the global.
+    Value *Loc;
+    if (Fixup.Indices.empty()) {
+      Loc = GV;
+    } else {
+      SmallVector<Value *, 4> GEPIndices;
+      GEPIndices.push_back(ConstantInt::get(Int32Ty, 0));
+      for (unsigned Idx : Fixup.Indices)
+        GEPIndices.push_back(ConstantInt::get(Int32Ty, Idx));
+      Loc = B.CreateGEP(GV->getValueType(), GV, GEPIndices);
+    }
+
+    Type *PtrTy = CPA->getType();
+
+    // Load the raw (unsigned) pointer.
+    Value *RawPtr = B.CreateLoad(PtrTy, Loc);
+
+    // Compute the discriminator, blending with the address if needed.
+    Value *Disc = CPA->getDiscriminator();
+    if (CPA->hasAddressDiscriminator())
+      Disc = B.CreateCall(BlendIntrinsic,
+                          {B.CreatePointerCast(Loc, IntPtrTy), Disc});
+
+    // Sign the pointer.
+    Value *SignedPtr =
+        B.CreateCall(SignIntrinsic, {B.CreatePointerCast(RawPtr, IntPtrTy),
+                                     CPA->getKey(), Disc});
+
+    // Store the signed pointer back.
+    B.CreateStore(B.CreateBitOrPointerCast(SignedPtr, PtrTy), Loc);
+
+    // Replace the ConstantPtrAuth in the initializer with the unsigned pointer.
+    CPA->replaceAllUsesWith(CPA->getPointer());
+  }
+
+  // Close off the fixup function.
+  B.CreateRetVoid();
+
+  // Update the global ctors list to call the pointer fixup function first.
+  auto *UInt8PtrTy = PointerType::getUnqual(Ctx);
+  StructType *CtorType =
+      StructType::get(Ctx, {Int32Ty, FixupFn->getType(), UInt8PtrTy});
+  Constant *PtrFixupCtor =
+      ConstantStruct::get(CtorType, {ConstantInt::get(Int32Ty, 0), FixupFn,
+                                     Constant::getNullValue(UInt8PtrTy)});
+
+  const char *LLVMGlobalCtorsName = "llvm.global_ctors";
+  GlobalVariable *OldCtorList = M.getNamedGlobal(LLVMGlobalCtorsName);
+  SmallVector<Constant *, 4> CtorListArgs;
+  CtorListArgs.push_back(PtrFixupCtor);
+
+  if (OldCtorList) {
+    // If the old ctors list has any uses then bail out: we do not know how to
+    // rewrite them.
+    if (OldCtorList->getNumUses() != 0) {
+      std::string ErrStr;
+      raw_string_ostream S(ErrStr);
+      S << "Global ctors variable has users, so can not be rewritten to "
+           "include pointer fixups: '"
+        << *OldCtorList << "'";
+      return make_error<StringError>(S.str(), inconvertibleErrorCode());
+    }
+
+    for (auto &Op : OldCtorList->getInitializer()->operands())
+      CtorListArgs.push_back(cast<Constant>(Op.get()));
+  }
+
+  ArrayType *CtorListType = ArrayType::get(CtorType, CtorListArgs.size());
+  Constant *CtorListInit = ConstantArray::get(CtorListType, CtorListArgs);
+
+  GlobalVariable *NewCtorList = new GlobalVariable(
+      M, CtorListType, false, GlobalValue::AppendingLinkage, CtorListInit);
+
+  if (OldCtorList) {
+    NewCtorList->takeName(OldCtorList);
+    OldCtorList->eraseFromParent();
+  } else
+    NewCtorList->setName(LLVMGlobalCtorsName);
+
+  return Error::success();
+}
+
+} // namespace lldb_private
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/InjectPointerSigningFixups.h b/lldb/source/Plugins/ExpressionParser/Clang/InjectPointerSigningFixups.h
new file mode 100644
index 0000000000000..291d286b0748c
--- /dev/null
+++ b/lldb/source/Plugins/ExpressionParser/Clang/InjectPointerSigningFixups.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_INJECTPOINTERSIGNINGFIXUPS_H
+#define LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_INJECTPOINTERSIGNINGFIXUPS_H
+
+#include "lldb/lldb-private-enumerations.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+class Function;
+class Module;
+} // namespace llvm
+
+namespace lldb_private {
+
+llvm::Error InjectPointerSigningFixupCode(llvm::Module &M,
+                                          ExecutionPolicy execution_policy);
+
+} // namespace lldb_private
+
+#endif
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
index 75527edca8fe4..c517ec8611932 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
@@ -250,12 +250,20 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(
   lldb::addr_t vtable_address =
       process->ReadPointerFromMemory(member_f_pointer_value, status);
 
+  ABISP abi_sp = process->GetABI();
+  if (abi_sp)
+    vtable_address = abi_sp->FixCodeAddress(vtable_address);
+
   if (status.Fail())
     return optional_info;
 
   lldb::addr_t vtable_address_first_entry =
       process->ReadPointerFromMemory(vtable_address + address_size, status);
 
+  if (abi_sp)
+    vtable_address_first_entry =
+        abi_sp->FixCodeAddress(vtable_address_first_entry);
+
   if (status.Fail())
     return optional_info;
 
@@ -265,6 +273,10 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(
   lldb::addr_t possible_function_address =
       process->ReadPointerFromMemory(address_after_vtable, status);
 
+  if (abi_sp)
+    possible_function_address =
+        abi_sp->FixCodeAddress(possible_function_address);
+
   if (status.Fail())
     return optional_info;
 
diff --git a/lldb/test/API/commands/expression/ptr-auth-fixups/Makefile b/lldb/test/API/commands/expression/ptr-auth-fixups/Makefile
new file mode 100644
index 0000000000000..ac50baa81423e
--- /dev/null
+++ b/lldb/test/API/commands/expression/ptr-auth-fixups/Makefile
@@ -0,0 +1,5 @@
+C_SOURCES := main.c
+
+override ARCH := arm64e
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/expression/ptr-auth-fixups/TestPtrAuthFixups.py b/lldb/test/API/commands/expression/ptr-auth-fixups/TestPtrAuthFixups.py
new file mode 100644
index 0000000000000..a22030c1f5154
--- /dev/null
+++ b/lldb/test/API/commands/expression/ptr-auth-fixups/TestPtrAuthFixups.py
@@ -0,0 +1,29 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestPtrAuthFixups(TestBase):
+    @skipUnlessDarwin
+    @skipUnlessArch("arm64")
+    def test_static_function_pointer(self):
+        """Test that a static function pointer initialized in an expression
+        gets correctly signed on arm64e via the pointer signing fixup pass."""
+        self.build()
+
+        lldbutil.run_to_source_breakpoint(
+            self, "// break here", lldb.SBFileSpec("main.c", False)
+        )
+
+        self.expect_expr(
+            "static int (*fp)(int, int) = &add; fp(5, 6);",
+            result_type="int",
+            result_value="11",
+        )
+
+        self.expect_expr(
+            "static int (*fp)(int, int) = &mul; fp(4, 5);",
+            result_type="int",
+            result_value="20",
+        )
diff --git a/lldb/test/API/commands/expression/ptr-auth-fixups/main.c b/lldb/test/API/commands/expression/ptr-auth-fixups/main.c
new file mode 100644
index 0000000000000..a7db8e5df83db
--- /dev/null
+++ b/lldb/test/API/commands/expression/ptr-auth-fixups/main.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int add(int a, int b) { return a + b; }
+int mul(int a, int b) { return a * b; }
+
+int main(void) {
+  printf("%d %d\n", add(2, 3), mul(4, 5));
+  return 0; // break here
+}



More information about the lldb-commits mailing list