Does clang cc1 frontend support arm64e/PAC builtins/qualifiers?

Hello

When compiling a C program like

int main() {
	int b = 10;
	int * __ptrauth(0,0,0) a = &b;
	return *a;
}

Using the following command:

clang -cc1 -x cpp-output -triple “arm64e-apple-macosx10.14.0” main.c

I am getting the following error:

main.c:4:8: error: this target does not support pointer authentication
    4 |         int * __ptrauth(0,0,0) a = &b;
      |               ^~~~~~~~~~~~~~~~
1 error generated.

However this command completes successfully:

clang -target "arm64e-apple-macosx10.14.0" main.c

Is it possible to make the frontend process arm64e-specific pointer qualifiers and builtins like __builtin_ptrauth_strip correctly, perhaps there are some extra arguments that need to be provided, apart from the arm64e target triple?

Thanks

Answering my own question: there’s an argument -fptrauth-intrinsics that enables such builtins/qualifiers.

If you build with the clang from the llvm21.1 version of clang, or main using -target arm64e-apple-macosx/darwin/ios (or whatever the iOS target name is) should work and have all the appropriate features enabled and the appropriate ABI set up. If they are not, please file a bug and CC me.

You found that the -fptrauth-intrinsics flag gives you the intrinsics and qualifier, but (if using llvm21.1/main does not work) you’d want at least -fptrauth-calls as well. But it should absolutely be setting up the compiler state correctly, so if not that is a bug.

(the site also says this is your first post, so hi! if you’re interested learning about pointer auth there is a #pauth channel in the llvm discord, and there is documentation about a lot of it in the clang pointer authentication documentation)

Thanks for your reply. Just for the clarification, the problem was with the frontend (clang -cc1), not the driver (clang). The driver correctly handles ptrauth stuff, as long as the -target or -arch contains arm64e in it, but if the frontend is invoked directly via clang -cc1 (without the driver) it looks like it needs to be told explicitly to handle -fptrauth-intrinsics, as just passing -triple with arm64e in it is not enough in this case (and the frontend doesn’t accept -arch or -target arguments)

Oh yeah, sorry it did not occur to me to look for that -cc1 isn’t really how you should be interfacing with clang, as the driver is responsible for selecting the flags required to match a given target’s ABI. -cc1 is what you would use if you know the entire ABI and platform/library configuration.

You should really consider -cc1 to be more of an implementation detail than an interface you should really be using directly.

-fprauth-intrinsics is not the only flag you need, and not all targets use the same flags, so me providing you a list won’t guarantee the correct abi everywhere, or that the list won’t change over time. Of course this doesn’t cover other flags that the driver might also set, that aren’t ABI changes, it may just mean missing optimizations or language features.

Is there a reason you are using -cc1?

Thanks @waffles_the_dog , -cc1 is just to demonstrate invoking frontend directly, as the project does some low-level compiler fiddling, bypassing the driver. -fptrauth-intrinsics actually was enough (I’ve found some other CLI arguments as well, but only __ptrauth(0,0,0) qualifier is needed for now)

If you’re using c++ or objective-c you’ll want to copy the full set of flags the driver produces for your target or you might hit abi problems

If I may ask a follow up question. I am having a similar issue with llc (or is it better to create a separate question thread for it?)

Is there a way to instruct llc to “support” the @llvm.ptrauth.resign.load.relative intrinsic?

Consider the following test.ll IR:

; Declare the intrinsic
declare i64 @llvm.ptrauth.resign.load.relative(i64, i32 immarg, i64, i32 immarg, i64, i64 immarg)

define i64 @test_resign(i64 %ptr) {
entry:
  ; Just call the intrinsic with some dummy arguments
  %res = call i64 @llvm.ptrauth.resign.load.relative(
      i64 %ptr, 
      i32 0, 
      i64 42, 
      i32 0, 
      i64 42, 
      i64 0
  )
  ret i64 %res
}

When llc is used to compile it into a binary,

llc -mtriple=arm64e-apple-macosx26.0 test.ll -o -

I am getting the following incorrect assembly, where the intrinsic is treated as a function call:

.section	__TEXT,__text,regular,pure_instructions
	.build_version macos, 26, 0
	.globl	_test_resign                    ; -- Begin function test_resign
	.p2align	2
_test_resign:                           ; @test_resign
	.cfi_startproc
; %bb.0:                                ; %entry
	stp	x29, x30, [sp, #-16]!           ; 16-byte Folded Spill
	.cfi_def_cfa_offset 16
	.cfi_offset w30, -8
	.cfi_offset w29, -16
	mov	w1, #0                          ; =0x0
	mov	w2, #42                         ; =0x2a
	mov	w3, #0                          ; =0x0
	mov	w4, #42                         ; =0x2a
	mov	x5, #0                          ; =0x0
	bl	_llvm.ptrauth.resign.load.relative
	ldp	x29, x30, [sp], #16             ; 16-byte Folded Reload
	ret
	.cfi_endproc
                                        ; -- End function
.subsections_via_symbols

however when I use clang driver, like this clang -S -target arm64e-apple-macosx26.0 test.ll -o test.s I am getting correct assembly with signing instructions:

	.section	__TEXT,__text,regular,pure_instructions
	.build_version macos, 11, 0
	.globl	_test_resign                    ; -- Begin function test_resign
	.p2align	2
_test_resign:                           ; @test_resign
	.cfi_startproc
; %bb.0:                                ; %entry
	mov	x16, x0
	mov	x17, #42                        ; =0x2a
	autia	x16, x17
	mov	x17, x16
	xpaci	x17
	cmp	x16, x17
	b.eq	Lauth_success_0
	mov	x16, x17
	b	Lresign_end_0
Lauth_success_0:
	ldrsw	x17, [x16, #0]!
	add	x16, x16, x17
	mov	x17, #42                        ; =0x2a
	pacia	x16, x17
Lresign_end_0:
	mov	x0, x16
	ret
	.cfi_endproc
                                        ; -- End function
.subsections_via_symbols

Interestingly, other more common ptrauth intrinsics, like @llvm.ptrauth.sign, are treated correctly by both llc and clang.

I tried several llvm branches, including one that comes with Apple Swift 6.2 repo, and none of them have llc support this intrinsic “by default”.

I am wondering if llc binary may have some hidden arguments that would enable such intrinsics?

If I may ask a follow up question. I am having a similar issue with llc (or is it better to create a separate question thread for it?)

I think the appropriate thing to do is to start a new question - just copy and paste the non “should I start a new question” bit :smiley:

Moved it to Is there a way to instruct llc to “support” the @llvm.ptrauth.resign.load.relative intrinsic?