https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/187611
>From 35e207f6e5566eac6d5421069ca0a2888f0b5e53 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <[email protected]> Date: Thu, 19 Mar 2026 16:42:30 -0700 Subject: [PATCH 1/4] [lldb] Support arm64e C++ vtable pointer signing When targeting arm64e, vtable pointers are signed with a discriminator that incorporates the object's address (PointerAuthVTPtrAddressDiscrimination) and class type (PointerAuthVTPtrTypeDiscrimination). I had to make a small change to clang, specifically in getPointerAuthDeclDiscriminator(). Previously, that was computing the discriminator based on getMangledName(). The latter returns the AsmLabelAttr, which for functions imported by lldb, is prefixed with `$__lldb_func`, causing a different discriminator to be generated. --- clang/lib/CodeGen/CGPointerAuth.cpp | 18 ++++++- .../Clang/ClangExpressionParser.cpp | 2 + .../expression/ptrauth-vtable/Makefile | 8 ++++ .../TestPtrAuthVTableExpressions.py | 48 +++++++++++++++++++ .../expression/ptrauth-vtable/main.cpp | 27 +++++++++++ 5 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 lldb/test/API/commands/expression/ptrauth-vtable/Makefile create mode 100644 lldb/test/API/commands/expression/ptrauth-vtable/TestPtrAuthVTableExpressions.py create mode 100644 lldb/test/API/commands/expression/ptrauth-vtable/main.cpp diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index 84b5c86e69a57..a083d10e9dbec 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "CGCXXABI.h" #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "clang/CodeGen/CodeGenABITypes.h" @@ -62,8 +63,21 @@ CodeGenModule::getPointerAuthDeclDiscriminator(GlobalDecl Declaration) { uint16_t &EntityHash = PtrAuthDiscriminatorHashes[Declaration]; if (EntityHash == 0) { - StringRef Name = getMangledName(Declaration); - EntityHash = llvm::getPointerAuthStableSipHash(Name); + const auto *ND = cast<NamedDecl>(Declaration.getDecl()); + // If the declaration has an AsmLabelAttr (e.g., LLDB expression evaluator + // attaches one to map imported decls to debuggee symbols), the asm label + // would be used as the mangled name, producing a wrong discriminator. + // Compute the real C++ mangled name instead so the discriminator matches + // what the original translation unit used. + if (ND->hasAttr<AsmLabelAttr>()) { + SmallString<256> Buffer; + llvm::raw_svector_ostream Out(Buffer); + getCXXABI().getMangleContext().mangleCXXName(Declaration, Out); + EntityHash = llvm::getPointerAuthStableSipHash(Out.str()); + } else { + StringRef Name = getMangledName(Declaration); + EntityHash = llvm::getPointerAuthStableSipHash(Name); + } } return EntityHash; diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index 38e5298f9cc93..a69a4ec3d5294 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -731,6 +731,8 @@ static void SetPointerAuthOptionsForArm64e(LangOptions &lang_opts) { lang_opts.PointerAuthReturns = true; lang_opts.PointerAuthAuthTraps = true; lang_opts.PointerAuthIndirectGotos = true; + lang_opts.PointerAuthVTPtrAddressDiscrimination = true; + lang_opts.PointerAuthVTPtrTypeDiscrimination = true; lang_opts.PointerAuthObjcIsa = true; lang_opts.PointerAuthObjcClassROPointers = true; lang_opts.PointerAuthObjcInterfaceSel = true; diff --git a/lldb/test/API/commands/expression/ptrauth-vtable/Makefile b/lldb/test/API/commands/expression/ptrauth-vtable/Makefile new file mode 100644 index 0000000000000..3c6bc2dd007e1 --- /dev/null +++ b/lldb/test/API/commands/expression/ptrauth-vtable/Makefile @@ -0,0 +1,8 @@ +CXX_SOURCES := main.cpp + +override ARCH := arm64e + +# We need an arm64e stblib. +USE_SYSTEM_STDLIB := 1 + +include Makefile.rules diff --git a/lldb/test/API/commands/expression/ptrauth-vtable/TestPtrAuthVTableExpressions.py b/lldb/test/API/commands/expression/ptrauth-vtable/TestPtrAuthVTableExpressions.py new file mode 100644 index 0000000000000..07a806dd53355 --- /dev/null +++ b/lldb/test/API/commands/expression/ptrauth-vtable/TestPtrAuthVTableExpressions.py @@ -0,0 +1,48 @@ +""" +VTable pointers are signed with a discriminator that incorporates the object's +address (PointerAuthVTPtrAddressDiscrimination) and class type ( +PointerAuthVTPtrTypeDiscrimination). +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestPtrAuthVTableExpressions(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessArm64eSupported + def test_virtual_call_on_debuggee_object(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "// break here", lldb.SBFileSpec("main.cpp", False) + ) + + self.expect_expr("d.value()", result_type="int", result_value="20") + self.expect_expr("od.value()", result_type="int", result_value="30") + + @skipUnlessArm64eSupported + def test_virtual_call_through_base_pointer(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "// break here", lldb.SBFileSpec("main.cpp", False) + ) + + self.expect_expr( + "base_ptr->value()", result_type="int", result_value="20" + ) + + @skipUnlessArm64eSupported + def test_virtual_call_via_helper(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "// break here", lldb.SBFileSpec("main.cpp", False) + ) + + self.expect_expr("call_value(&d)", result_type="int", result_value="20") + self.expect_expr("call_value(&od)", result_type="int", result_value="30") + self.expect_expr( + "call_value(base_ptr)", result_type="int", result_value="20" + ) diff --git a/lldb/test/API/commands/expression/ptrauth-vtable/main.cpp b/lldb/test/API/commands/expression/ptrauth-vtable/main.cpp new file mode 100644 index 0000000000000..d9dec9b9a6a41 --- /dev/null +++ b/lldb/test/API/commands/expression/ptrauth-vtable/main.cpp @@ -0,0 +1,27 @@ +#include <cstdio> + +class Base { +public: + virtual int value() { return 10; } + virtual ~Base() = default; +}; + +class Derived : public Base { +public: + int value() override { return 20; } +}; + +class OtherDerived : public Base { +public: + int value() override { return 30; } +}; + +int call_value(Base *obj) { return obj->value(); } + +int main() { + Derived d; + OtherDerived od; + Base *base_ptr = &d; + printf("%d %d %d\n", d.value(), od.value(), base_ptr->value()); + return 0; // break here +} >From 015c13b8b4b18c63f5d29912d4cc0ed81c75e4c2 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <[email protected]> Date: Thu, 19 Mar 2026 17:12:57 -0700 Subject: [PATCH 2/4] Formatting --- .../ptrauth-vtable/TestPtrAuthVTableExpressions.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lldb/test/API/commands/expression/ptrauth-vtable/TestPtrAuthVTableExpressions.py b/lldb/test/API/commands/expression/ptrauth-vtable/TestPtrAuthVTableExpressions.py index 07a806dd53355..92a30b6e6548f 100644 --- a/lldb/test/API/commands/expression/ptrauth-vtable/TestPtrAuthVTableExpressions.py +++ b/lldb/test/API/commands/expression/ptrauth-vtable/TestPtrAuthVTableExpressions.py @@ -30,9 +30,7 @@ def test_virtual_call_through_base_pointer(self): self, "// break here", lldb.SBFileSpec("main.cpp", False) ) - self.expect_expr( - "base_ptr->value()", result_type="int", result_value="20" - ) + self.expect_expr("base_ptr->value()", result_type="int", result_value="20") @skipUnlessArm64eSupported def test_virtual_call_via_helper(self): @@ -43,6 +41,4 @@ def test_virtual_call_via_helper(self): self.expect_expr("call_value(&d)", result_type="int", result_value="20") self.expect_expr("call_value(&od)", result_type="int", result_value="30") - self.expect_expr( - "call_value(base_ptr)", result_type="int", result_value="20" - ) + self.expect_expr("call_value(base_ptr)", result_type="int", result_value="20") >From 11d8ba5dfb44a004256aa62a822cc71f7c67ac8c Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <[email protected]> Date: Fri, 20 Mar 2026 07:42:33 -0700 Subject: [PATCH 3/4] Implement Michael's suggestion of checking the prefix --- clang/lib/CodeGen/CGPointerAuth.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index a083d10e9dbec..5e0ef91f51ffb 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -64,12 +64,13 @@ CodeGenModule::getPointerAuthDeclDiscriminator(GlobalDecl Declaration) { if (EntityHash == 0) { const auto *ND = cast<NamedDecl>(Declaration.getDecl()); - // If the declaration has an AsmLabelAttr (e.g., LLDB expression evaluator - // attaches one to map imported decls to debuggee symbols), the asm label - // would be used as the mangled name, producing a wrong discriminator. - // Compute the real C++ mangled name instead so the discriminator matches - // what the original translation unit used. - if (ND->hasAttr<AsmLabelAttr>()) { + constexpr static llvm::StringLiteral LLDBLabelPrefix = "$__lldb_func:"; + if (ND->hasAttr<AsmLabelAttr>() && + ND->getAttr<AsmLabelAttr>()->getLabel().starts_with(LLDBLabelPrefix)) { + // If the declaration comes from LLDB, the asm label has a prefix that + // would producing a different discriminator. Compute the real C++ mangled + // name instead so the discriminator matches what the original translation + // unit used. SmallString<256> Buffer; llvm::raw_svector_ostream Out(Buffer); getCXXABI().getMangleContext().mangleCXXName(Declaration, Out); >From fb2c8502546d0137788f8827748f6ab926ab2819 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <[email protected]> Date: Tue, 24 Mar 2026 08:29:20 -0700 Subject: [PATCH 4/4] [lldb] Move prefix into Mangle.h so it can be shared --- clang/include/clang/AST/Mangle.h | 5 +++++ clang/lib/AST/Mangle.cpp | 10 ++++------ clang/lib/CodeGen/CGPointerAuth.cpp | 4 ++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h index ca72dcfd4483d..13fa0d1c880bc 100644 --- a/clang/include/clang/AST/Mangle.h +++ b/clang/include/clang/AST/Mangle.h @@ -317,6 +317,11 @@ class ASTNameGenerator { class Implementation; std::unique_ptr<Implementation> Impl; }; + +/// Constants used by LLDB for mangling. +struct LLDBManglingABI { + static constexpr llvm::StringLiteral FunctionLabelPrefix = "$__lldb_func:"; +}; } // namespace clang #endif diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp index 780b2c585c810..58216667116a8 100644 --- a/clang/lib/AST/Mangle.cpp +++ b/clang/lib/AST/Mangle.cpp @@ -152,8 +152,6 @@ bool MangleContext::shouldMangleDeclName(const NamedDecl *D) { return shouldMangleCXXName(D); } -static llvm::StringRef g_lldb_func_call_label_prefix = "$__lldb_func:"; - /// Given an LLDB function call label, this function prints the label /// into \c Out, together with the structor type of \c GD (if the /// decl is a constructor/destructor). LLDB knows how to handle mangled @@ -167,9 +165,9 @@ static llvm::StringRef g_lldb_func_call_label_prefix = "$__lldb_func:"; /// static void emitLLDBAsmLabel(llvm::StringRef label, GlobalDecl GD, llvm::raw_ostream &Out) { - assert(label.starts_with(g_lldb_func_call_label_prefix)); + assert(label.starts_with(LLDBManglingABI::FunctionLabelPrefix)); - Out << g_lldb_func_call_label_prefix; + Out << LLDBManglingABI::FunctionLabelPrefix; if (auto *Ctor = llvm::dyn_cast<clang::CXXConstructorDecl>(GD.getDecl())) { Out << "C"; @@ -180,7 +178,7 @@ static void emitLLDBAsmLabel(llvm::StringRef label, GlobalDecl GD, Out << "D" << GD.getDtorType(); } - Out << label.substr(g_lldb_func_call_label_prefix.size()); + Out << label.substr(LLDBManglingABI::FunctionLabelPrefix.size()); } void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) { @@ -216,7 +214,7 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) { if (!UserLabelPrefix.empty()) Out << '\01'; // LLVM IR Marker for __asm("foo") - if (ALA->getLabel().starts_with(g_lldb_func_call_label_prefix)) + if (ALA->getLabel().starts_with(LLDBManglingABI::FunctionLabelPrefix)) emitLLDBAsmLabel(ALA->getLabel(), GD, Out); else Out << ALA->getLabel(); diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index 5e0ef91f51ffb..28d3289dfe04f 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -64,9 +64,9 @@ CodeGenModule::getPointerAuthDeclDiscriminator(GlobalDecl Declaration) { if (EntityHash == 0) { const auto *ND = cast<NamedDecl>(Declaration.getDecl()); - constexpr static llvm::StringLiteral LLDBLabelPrefix = "$__lldb_func:"; if (ND->hasAttr<AsmLabelAttr>() && - ND->getAttr<AsmLabelAttr>()->getLabel().starts_with(LLDBLabelPrefix)) { + ND->getAttr<AsmLabelAttr>()->getLabel().starts_with( + LLDBManglingABI::FunctionLabelPrefix)) { // If the declaration comes from LLDB, the asm label has a prefix that // would producing a different discriminator. Compute the real C++ mangled // name instead so the discriminator matches what the original translation _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
