Skip to content

[AArch64][PAC] Emit !dbg locations in *_vfpthunk_ functions#179688

Merged
atrosinenko merged 2 commits into
mainfrom
users/atrosinenko/pauth-vfpthunk-dbginfo
Feb 28, 2026
Merged

[AArch64][PAC] Emit !dbg locations in *_vfpthunk_ functions#179688
atrosinenko merged 2 commits into
mainfrom
users/atrosinenko/pauth-vfpthunk-dbginfo

Conversation

@atrosinenko
Copy link
Copy Markdown
Contributor

@atrosinenko atrosinenko commented Feb 4, 2026

The usage of pointers to member functions with Pointer Authentication requires generation of *_vfpthunk_ functions. These thunk functions can be later inlined and optimized by replacing the indirect call instruction with a direct one and then inlining that function call.

In absence of !dbg metadata attached to the original call instruction, such inlining ultimately results in an assertion "!dbg attachment points at wrong subprogram for function" in the assertions-enabled builds. By manually executing opt with -verify-each option on the LLVM IR produced by the frontend, an actual issue can be observed: "inlinable function call in a function with debug info must have a !dbg location" after the replacement of indirect call instruction with the direct one takes place.

This commit fixes the issue by attaching artificial !dbg locations to the original call instruction (as well as most other instructions in *_vfpthunk_ function) the same way it is done for other compiler-generated helper functions.

@atrosinenko atrosinenko marked this pull request as ready for review February 4, 2026 15:34
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. labels Feb 4, 2026
@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Feb 4, 2026

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-codegen

Author: Anatoly Trosinenko (atrosinenko)

Changes

In absence of !dbg metadata, it is possible for indirect authenticated call to be replaced with a direct call instruction without !dbg metadata. This may result in an error reported by LLVM IR verifier ("inlinable function call in a function with debug info must have a !dbg location") or an assertion triggered after inlining this call ("!dbg attachment points at wrong subprogram for function").


Full diff: https://github.com/llvm/llvm-project/pull/179688.diff

2 Files Affected:

  • (modified) clang/lib/CodeGen/ItaniumCXXABI.cpp (+4)
  • (added) clang/test/DebugInfo/CXX/ptrauth-member-function-pointer-debuglocs.cpp (+31)
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index a6c80cd083bb8..63cb4b6989917 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -3479,6 +3479,10 @@ ItaniumCXXABI::getOrCreateVirtualFunctionPointerThunk(const CXXMethodDecl *MD) {
 
   CGF.StartFunction(GlobalDecl(), FnInfo.getReturnType(), ThunkFn, FnInfo,
                     FunctionArgs, MD->getLocation(), SourceLocation());
+
+  // Emit an artificial location for this function.
+  auto AL = ApplyDebugLocation::CreateArtificial(CGF);
+
   llvm::Value *ThisVal = loadIncomingCXXThis(CGF);
   setCXXABIThisValue(CGF, ThisVal);
 
diff --git a/clang/test/DebugInfo/CXX/ptrauth-member-function-pointer-debuglocs.cpp b/clang/test/DebugInfo/CXX/ptrauth-member-function-pointer-debuglocs.cpp
new file mode 100644
index 0000000000000..cfc10a5207087
--- /dev/null
+++ b/clang/test/DebugInfo/CXX/ptrauth-member-function-pointer-debuglocs.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios   -fptrauth-calls -fptrauth-intrinsics \
+// RUN:     -emit-llvm -std=c++11 -O1 -disable-llvm-passes \
+// RUN:     -debug-info-kind=limited %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics \
+// RUN:     -emit-llvm -std=c++11 -O1 -disable-llvm-passes \
+// RUN:     -debug-info-kind=limited %s -o - | FileCheck %s
+
+// Check that compiler-generated *_vfpthunk_ function has a !dbg location
+// attached to the call instruction.
+
+// CHECK:      define {{.*}}@_ZN1A2f0Ev_vfpthunk_{{.*}} !dbg
+// CHECK-NOT:  define
+// CHECK:        musttail call void %{{[0-9]+}}(ptr
+// CHECK-SAME:     [ "ptrauth"(i32 0, i64 %{{[0-9]+}}) ]
+// CHECK-SAME:     !dbg
+
+volatile long T;
+
+struct A {
+  virtual void f0() {
+    T = 0;
+  }
+};
+typedef void (A::*MFP)();
+
+void caller() {
+  A a;
+
+  MFP x = &A::f0;
+  (a.*x)();
+}

Copy link
Copy Markdown
Contributor

@ojhunt ojhunt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code change is fine, but I'd like the test to be a bit more thorough about verifying the emitted info and info linkage

FunctionArgs, MD->getLocation(), SourceLocation());

// Emit an artificial location for this function.
auto AL = ApplyDebugLocation::CreateArtificial(CGF);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there really nothing better that we can do? I guess not as anything else would be confusing.

It would be nice if there was a way do say

// Compiler generated code
T virtual_member_thunk_for_X(this, ...) {
  return this->virtualFunction(); // You are here
}

But that's so very far outside of the scope of this PR :D

// CHECK: define {{.*}}@_ZN1A2f0Ev_vfpthunk_{{.*}} !dbg
// CHECK-NOT: define
// CHECK: musttail call void %{{[0-9]+}}(ptr
// CHECK-SAME: [ "ptrauth"(i32 0, i64 %{{[0-9]+}}) ]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to include the diversifier here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added matching of the corresponding call to @llvm.ptrauth.blend in ee7d2b7.

// CHECK-NOT: define
// CHECK: musttail call void %{{[0-9]+}}(ptr
// CHECK-SAME: [ "ptrauth"(i32 0, i64 %{{[0-9]+}}) ]
// CHECK-SAME: !dbg
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

include the debug info index and the required debug identities so we can record that the pseudo address correctly ties to the thunk scope

e.g.

musttail .... !dbg [[DEBUG_INDEX:![0-9]+]]
[[SCOPE_INDEX:![0-9]+]] = distinct !DISubprogram(linkageName: "_ZN1A2f0Ev_vfpthunk_",
[[DEBUG_INDEX]] = !DILocation(line: 0, scope: [[SCOPE_INDEX]])

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added more checks in ee7d2b7, thank you.

For the record, here is the code that is generated for both triples (except for the difference in the particular metadata indices):

define linkonce_odr hidden void @_ZN1A2f0Ev_vfpthunk_(ptr noundef %this) #3 !dbg !63 {
entry:
  %this.addr = alloca ptr, align 8
  store ptr %this, ptr %this.addr, align 8, !tbaa !58
  %this1 = load ptr, ptr %this.addr, align 8, !dbg !65
  %0 = load ptr, ptr %this.addr, align 8, !dbg !65, !tbaa !58
  %vtable = load ptr, ptr %this1, align 8, !dbg !65, !tbaa !49
  %1 = ptrtoint ptr %vtable to i64, !dbg !65
  %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 0), !dbg !65
  %3 = inttoptr i64 %2 to ptr, !dbg !65
  %vfn = getelementptr inbounds ptr, ptr %3, i64 0, !dbg !65
  %4 = load ptr, ptr %vfn, align 8, !dbg !65
  %5 = ptrtoint ptr %vfn to i64, !dbg !65
  %6 = call i64 @llvm.ptrauth.blend(i64 %5, i64 9385), !dbg !65
  musttail call void %4(ptr noundef nonnull align 8 dereferenceable(8) %0) [ "ptrauth"(i32 0, i64 %6) ], !dbg !65
  ret void, !dbg !65

7:                                                ; No predecessors!
  ret void, !dbg !65

8:                                                ; No predecessors!
  ret void, !dbg !65
}

That is, alloca and store has no !dbg attached, all other instructions refer to !65 and the function itself refers to !63. Here are metadata nodes:

!63 = distinct !DISubprogram(linkageName: "_ZN1A2f0Ev_vfpthunk_", scope: !10, file: !10, line: 28, type: !64, flags: DIFlagArtificial | DIFlagThunk, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
!64 = !DISubroutineType(types: !51)
!65 = !DILocation(line: 0, scope: !63)

Copy link
Copy Markdown
Contributor

@kovdan01 kovdan01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if the PR description could be slightly enhanced. Particularly, these things do not seem clear for me after reading the PR description:

  1. Why is this issue specific for virtual function pointer thunks
  2. Why the issue is pauth-specific and does not occur with pauth disabled

I would be happy to take another look at the changes when we have answers for these questions.

@kovdan01 kovdan01 requested a review from ojhunt February 10, 2026 21:11
@atrosinenko atrosinenko force-pushed the users/atrosinenko/pauth-vfpthunk-dbginfo branch from ee7d2b7 to 271506f Compare February 11, 2026 19:44
Copy link
Copy Markdown
Contributor Author

@atrosinenko atrosinenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kovdan01 Clarified the description, thanks! Hopefully this resolves your other questions as well.

@kovdan01
Copy link
Copy Markdown
Contributor

kovdan01 commented Feb 13, 2026

Thanks @atrosinenko for answering my previous questions! Everything became much clearer, and I've resolved my previous comments.

The changes LGTM if nobody else has objections on merging this. I also suppose that you might want to rebase the changes in order to re-trigger CI builds - current failures seem unrelated (smth wrong with lldb)

@kovdan01
Copy link
Copy Markdown
Contributor

@ojhunt Could you please take another look at the PR? You've previously requested changes for this PR and, as far as I can see, your previous comments should now be addressed.

In absence of `!dbg` metadata, it is possible for indirect
authenticated call to be replaced with a direct call instruction
without `!dbg` metadata. This may result in an error reported by
LLVM IR verifier ("inlinable function call in a function with
debug info must have a !dbg location") or an assertion triggered
after inlining this call ("!dbg attachment points at wrong
subprogram for function").
@atrosinenko atrosinenko force-pushed the users/atrosinenko/pauth-vfpthunk-dbginfo branch from 271506f to b0d0e2e Compare February 18, 2026 09:22
@atrosinenko
Copy link
Copy Markdown
Contributor Author

Ping

@atrosinenko
Copy link
Copy Markdown
Contributor Author

Ping.

vvereschaka added a commit to vvereschaka/llvm-zorg that referenced this pull request Feb 26, 2026
…-pauthtest' builder.

Added '-fno-inline' compiler flag to avoid clang crash.

See: llvm/llvm-project#179688
vvereschaka added a commit to llvm/llvm-zorg that referenced this pull request Feb 26, 2026
…-pauthtest' builder. (#773)

Added '-fno-inline' compiler flag to avoid clang crash.

See: llvm/llvm-project#179688
@kovdan01
Copy link
Copy Markdown
Contributor

@ojhunt Could you please take another look at the PR? You've previously requested changes for this PR and, as far as I can see, your previous comments should now be addressed.

While this PR is unmerged, the pauthtest buildbot has to use -fno-inline, and it would be nice to have this PR merged and delete the option from build config (see llvm/llvm-zorg#773)

vvereschaka added a commit to vvereschaka/llvm-zorg that referenced this pull request Feb 27, 2026
…aarch64-pauthtest' builder.

Added '-fno-inline' compiler flag to avoid clang crash.

See: llvm/llvm-project#179688

Reland PR llvm#773 with fixed options for the testsuite compiler
llvm#773
vvereschaka added a commit to llvm/llvm-zorg that referenced this pull request Feb 27, 2026
…aarch64-pauthtest' builder. (#774)

Added '-fno-inline' compiler flag to avoid clang crash.

See: llvm/llvm-project#179688

Reland PR #773 with fixed options for the testsuite compiler
(#773)
@atrosinenko atrosinenko merged commit 903acc2 into main Feb 28, 2026
10 checks passed
@atrosinenko atrosinenko deleted the users/atrosinenko/pauth-vfpthunk-dbginfo branch February 28, 2026 11:35
@atrosinenko atrosinenko added this to the LLVM 22.x Release milestone Mar 2, 2026
@atrosinenko
Copy link
Copy Markdown
Contributor Author

/cherry-pick 903acc2

@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Mar 2, 2026

/pull-request #184166

sahas3 pushed a commit to sahas3/llvm-project that referenced this pull request Mar 4, 2026
…#179688)

The usage of pointers to member functions with Pointer Authentication
requires generation of `*_vfpthunk_` functions. These thunk functions
can be later inlined and optimized by replacing the indirect call
instruction with a direct one and then inlining that function call.

In absence of `!dbg` metadata attached to the original call instruction,
such inlining ultimately results in an assertion "!dbg attachment points
at wrong subprogram for function" in the assertions-enabled builds. By
manually executing `opt` with `-verify-each` option on the LLVM IR
produced by the frontend, an actual issue can be observed: "inlinable
function call in a function with debug info must have a !dbg location"
after the replacement of indirect call instruction with the direct one
takes place.

This commit fixes the issue by attaching artificial `!dbg` locations to
the original call instruction (as well as most other instructions in
`*_vfpthunk_` function) the same way it is done for other
compiler-generated helper functions.
sujianIBM pushed a commit to sujianIBM/llvm-project that referenced this pull request Mar 5, 2026
…#179688)

The usage of pointers to member functions with Pointer Authentication
requires generation of `*_vfpthunk_` functions. These thunk functions
can be later inlined and optimized by replacing the indirect call
instruction with a direct one and then inlining that function call.

In absence of `!dbg` metadata attached to the original call instruction,
such inlining ultimately results in an assertion "!dbg attachment points
at wrong subprogram for function" in the assertions-enabled builds. By
manually executing `opt` with `-verify-each` option on the LLVM IR
produced by the frontend, an actual issue can be observed: "inlinable
function call in a function with debug info must have a !dbg location"
after the replacement of indirect call instruction with the direct one
takes place.

This commit fixes the issue by attaching artificial `!dbg` locations to
the original call instruction (as well as most other instructions in
`*_vfpthunk_` function) the same way it is done for other
compiler-generated helper functions.
dyung pushed a commit to llvmbot/llvm-project that referenced this pull request Mar 6, 2026
…#179688)

The usage of pointers to member functions with Pointer Authentication
requires generation of `*_vfpthunk_` functions. These thunk functions
can be later inlined and optimized by replacing the indirect call
instruction with a direct one and then inlining that function call.

In absence of `!dbg` metadata attached to the original call instruction,
such inlining ultimately results in an assertion "!dbg attachment points
at wrong subprogram for function" in the assertions-enabled builds. By
manually executing `opt` with `-verify-each` option on the LLVM IR
produced by the frontend, an actual issue can be observed: "inlinable
function call in a function with debug info must have a !dbg location"
after the replacement of indirect call instruction with the direct one
takes place.

This commit fixes the issue by attaching artificial `!dbg` locations to
the original call instruction (as well as most other instructions in
`*_vfpthunk_` function) the same way it is done for other
compiler-generated helper functions.

(cherry picked from commit 903acc2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:codegen IR generation bugs: mangling, exceptions, etc. clang Clang issues not falling into any other category

Projects

Development

Successfully merging this pull request may close these issues.

4 participants