[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