[Lldb-commits] [lldb] [lldb] Interpret the ptrauth intrinsics in IRInterpreter (PR #232467)
Yao Qi via lldb-commits
lldb-commits at lists.llvm.org
Fri Oct 16 02:36:33 PDT 2026
https://github.com/qiyao updated https://github.com/llvm/llvm-project/pull/232467
>From bf633e42f1f20567a01a1e243271a9f9ea9fa6df Mon Sep 17 00:00:00 2001
From: Yao Qi <yao_qi at apple.com>
Date: Thu, 15 Oct 2026 13:41:54 +0100
Subject: [PATCH 1/3] [lldb] Interpret the ptrauth intrinsics in IRInterpreter
When lldb evaluates an expression with `IRInterpreter` on arm64e, it reads
through a `__ptrauth`-qualified pointer compiles to calls to
`@llvm.ptrauth.blend` and `@llvm.ptrauth.auth`. The interpreter knows
neither, so the read fails:
```
(lldb) expr -j 0 -- *s_ptr->p
error: Can't evaluate the expression without a running target due to: Interpreter doesn't handle one of the expression's opcodes
```
Interpret the three intrinsics instead. The same read now works:
```
(lldb) expr -j 0 -- *s_ptr->p
(int) $0 = 123
```
The test forces the interpreter with `expr -j 0` and runs on arm64e only.
This upstreams the implementation and the test, where the co-authors
below wrote them.
Co-authored-by: Adrian Prantl <aprantl at apple.com>
Co-authored-by: Vedant Kumar <vsk at apple.com>
Co-authored-by: Fred Riss <friss at apple.com>
---
lldb/source/Expression/IRInterpreter.cpp | 52 +++++++++++++++++++
.../ptrauth_blend/Makefile | 2 +
.../ptrauth_blend/TestPtrauthBlendInterp.py | 11 ++++
.../ptrauth_diagnostics/ptrauth_blend/main.c | 15 ++++++
4 files changed, 80 insertions(+)
create mode 100644 lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/Makefile
create mode 100644 lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/TestPtrauthBlendInterp.py
create mode 100644 lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/main.c
diff --git a/lldb/source/Expression/IRInterpreter.cpp b/lldb/source/Expression/IRInterpreter.cpp
index e1b5afd4a97bc2..b39e7ef3cd2b65 100644
--- a/lldb/source/Expression/IRInterpreter.cpp
+++ b/lldb/source/Expression/IRInterpreter.cpp
@@ -83,6 +83,9 @@ static bool CanIgnoreCall(const CallInst *call) {
break;
case llvm::Intrinsic::dbg_declare:
case llvm::Intrinsic::dbg_value:
+ case llvm::Intrinsic::ptrauth_auth:
+ case llvm::Intrinsic::ptrauth_blend:
+ case llvm::Intrinsic::ptrauth_strip:
return true;
}
}
@@ -1453,6 +1456,55 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
case Instruction::Call: {
const CallInst *call_inst = cast<CallInst>(inst);
+ if (auto *fn = call_inst->getCalledFunction()) {
+ if (fn->isIntrinsic() &&
+ ((fn->getIntrinsicID() == llvm::Intrinsic::ptrauth_auth &&
+ call_inst->arg_size() == 3) ||
+ (fn->getIntrinsicID() == llvm::Intrinsic::ptrauth_strip &&
+ call_inst->arg_size() == 2)) &&
+ exe_ctx.GetProcessPtr()) {
+ llvm::Value *auth_ptr = call_inst->getArgOperand(0);
+ // Extract the argument's value.
+ lldb_private::Scalar auth_ptr_val;
+ if (!frame.EvaluateValue(auth_ptr_val, auth_ptr, module)) {
+ error = lldb_private::Status::FromErrorStringWithFormat(
+ "unable to evaluate argument %d", 0);
+ return false;
+ }
+
+ if (lldb::ABISP abi_sp = exe_ctx.GetProcessPtr()->GetABI()) {
+ auto stripped_ptr = lldb_private::Scalar(
+ abi_sp->FixCodeAddress(auth_ptr_val.ULongLong()));
+ frame.AssignValue(call_inst, stripped_ptr, module);
+ }
+ }
+
+ if (fn->isIntrinsic() &&
+ fn->getIntrinsicID() == llvm::Intrinsic::ptrauth_blend) {
+ llvm::Value *addr = call_inst->getArgOperand(0);
+ llvm::Value *lobits = call_inst->getArgOperand(1);
+
+ // Extract the address.
+ lldb_private::Scalar addr_val;
+ if (!frame.EvaluateValue(addr_val, addr, module)) {
+ error = lldb_private::Status::FromErrorStringWithFormat(
+ "unable to evaluate argument %d", 0);
+ return false;
+ }
+
+ // Extract the low bits to blend in.
+ lldb_private::Scalar lobits_val;
+ if (!frame.EvaluateValue(lobits_val, lobits, module)) {
+ error = lldb_private::Status::FromErrorStringWithFormat(
+ "unable to evaluate argument %d", 1);
+ return false;
+ }
+
+ lldb_private::Scalar blend_val = addr_val | (lobits_val << 48);
+ frame.AssignValue(call_inst, blend_val, module);
+ }
+ }
+
if (CanIgnoreCall(call_inst))
break;
diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/Makefile b/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/Makefile
new file mode 100644
index 00000000000000..c9319d6e6888a4
--- /dev/null
+++ b/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/Makefile
@@ -0,0 +1,2 @@
+C_SOURCES := main.c
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/TestPtrauthBlendInterp.py b/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/TestPtrauthBlendInterp.py
new file mode 100644
index 00000000000000..a04390f04ae2cc
--- /dev/null
+++ b/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/TestPtrauthBlendInterp.py
@@ -0,0 +1,11 @@
+from lldbsuite.test import lldbinline
+from lldbsuite.test import decorators
+
+lldbinline.MakeInlineTest(
+ __file__,
+ globals(),
+ [
+ decorators.skipIf(archs=decorators.no_match(["arm64e"])),
+ decorators.skipIf(dwarf_version=["<", "4"]),
+ ],
+)
diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/main.c b/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/main.c
new file mode 100644
index 00000000000000..a2977cd4564720
--- /dev/null
+++ b/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/main.c
@@ -0,0 +1,15 @@
+#define auth __ptrauth(2, 1, 9484)
+
+struct mytype {
+ int *auth p;
+};
+
+int main() {
+ int x = 123;
+ struct mytype s;
+ s.p = &x;
+ struct mytype *s_ptr = &s;
+ //% self.filecheck("expr -j 0 -- *s_ptr->p", "main.c")
+ // CHECK: (int) {{.*}} = 123
+ return 0;
+}
>From 72c502ef52bb36c2ec4bc1b2b87419aa075343e2 Mon Sep 17 00:00:00 2001
From: Yao Qi <yao_qi at apple.com>
Date: Thu, 15 Oct 2026 19:46:08 +0100
Subject: [PATCH 2/3] [lldb][test] Use Arm64eTestBase in TestPtrauthBlendInterp
`TestPtrauthBlendInterp.py` runs only when the whole test suite targets
arm64e. A normal run on an arm64 skips it:
```
SKIPPED: lldb-api :: functionalities/ptrauth_diagnostics/ptrauth_blend/TestPtrauthBlendInterp.py (1 of 1)
```
Derive the test from `Arm64eTestBase`, like the other arm64e tests.
---
.../ptrauth_blend/TestPtrauthBlendInterp.py | 34 +++++++++++++------
.../ptrauth_diagnostics/ptrauth_blend/main.c | 4 +--
2 files changed, 24 insertions(+), 14 deletions(-)
diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/TestPtrauthBlendInterp.py b/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/TestPtrauthBlendInterp.py
index a04390f04ae2cc..23b4f98b8b774d 100644
--- a/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/TestPtrauthBlendInterp.py
+++ b/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/TestPtrauthBlendInterp.py
@@ -1,11 +1,23 @@
-from lldbsuite.test import lldbinline
-from lldbsuite.test import decorators
-
-lldbinline.MakeInlineTest(
- __file__,
- globals(),
- [
- decorators.skipIf(archs=decorators.no_match(["arm64e"])),
- decorators.skipIf(dwarf_version=["<", "4"]),
- ],
-)
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbarm64e import Arm64eTestBase
+
+
+class TestPtrauthBlendInterp(Arm64eTestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+ SHARED_BUILD_TESTCASE = False
+
+ def test_interpreter_reads_ptrauth_field(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "// break here", lldb.SBFileSpec("main.c", False)
+ )
+
+ # Disable the JIT, so that IRInterpreter evaluates the expression.
+ options = lldb.SBExpressionOptions()
+ options.SetAllowJIT(False)
+ self.expect_expr(
+ "*s_ptr->p", result_type="int", result_value="123", options=options
+ )
diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/main.c b/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/main.c
index a2977cd4564720..dc0ab08d02a0c6 100644
--- a/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/main.c
+++ b/lldb/test/API/functionalities/ptrauth_diagnostics/ptrauth_blend/main.c
@@ -9,7 +9,5 @@ int main() {
struct mytype s;
s.p = &x;
struct mytype *s_ptr = &s;
- //% self.filecheck("expr -j 0 -- *s_ptr->p", "main.c")
- // CHECK: (int) {{.*}} = 123
- return 0;
+ return 0; // break here
}
>From 98091c279d214dccc7175b7938ee83f553b306a6 Mon Sep 17 00:00:00 2001
From: Yao Qi <yao_qi at apple.com>
Date: Fri, 16 Oct 2026 10:35:12 +0100
Subject: [PATCH 3/3] Replace FromErrorStringWithFormat with FromErrorString
the argument is known in advance.
---
lldb/source/Expression/IRInterpreter.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lldb/source/Expression/IRInterpreter.cpp b/lldb/source/Expression/IRInterpreter.cpp
index b39e7ef3cd2b65..30373301503acf 100644
--- a/lldb/source/Expression/IRInterpreter.cpp
+++ b/lldb/source/Expression/IRInterpreter.cpp
@@ -1467,8 +1467,8 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
// Extract the argument's value.
lldb_private::Scalar auth_ptr_val;
if (!frame.EvaluateValue(auth_ptr_val, auth_ptr, module)) {
- error = lldb_private::Status::FromErrorStringWithFormat(
- "unable to evaluate argument %d", 0);
+ error = lldb_private::Status::FromErrorString(
+ "unable to evaluate argument 0");
return false;
}
More information about the lldb-commits
mailing list