From a34910a6e58f4c441b1ef314898308720542ce6c Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Thu, 25 Jan 2024 00:32:48 +0300 Subject: [PATCH 1/9] [AArch64][PAC][CodeGen] Fix emitting `DW_AT_LLVM_ptrauth_address_discriminated` The Dwarf attribute `DW_AT_LLVM_ptrauth_address_discriminated` was emitted for all ptrauth-qualified types, even if the address discrimination was disabled. This was related to incorrect usage of `std::optional` holding the value of the attribute. --- llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp | 2 +- llvm/test/DebugInfo/AArch64/ptrauth.ll | 67 +++++++++++++---------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp index 97eda02a730b..f42cf9cf8727 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -805,7 +805,7 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DIDerivedType *DTy) { if (auto Key = DTy->getPtrAuthKey()) addUInt(Buffer, dwarf::DW_AT_LLVM_ptrauth_key, dwarf::DW_FORM_data1, *Key); if (auto AddrDisc = DTy->isPtrAuthAddressDiscriminated()) - if (AddrDisc) + if (AddrDisc.value()) addFlag(Buffer, dwarf::DW_AT_LLVM_ptrauth_address_discriminated); if (auto Disc = DTy->getPtrAuthExtraDiscriminator()) addUInt(Buffer, dwarf::DW_AT_LLVM_ptrauth_extra_discriminator, diff --git a/llvm/test/DebugInfo/AArch64/ptrauth.ll b/llvm/test/DebugInfo/AArch64/ptrauth.ll index 067063c4d1d5..4f84fe4f9629 100644 --- a/llvm/test/DebugInfo/AArch64/ptrauth.ll +++ b/llvm/test/DebugInfo/AArch64/ptrauth.ll @@ -1,35 +1,41 @@ ; RUN: llc %s -filetype=obj -mtriple arm64e-apple-darwin -o - \ ; RUN: | llvm-dwarfdump - | FileCheck %s -; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d2)") +; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 0, 0x04d2)") ; CHECK: 0x{{0+}}[[TY]]: DW_TAG_LLVM_ptrauth_type ; CHECK-NEXT: DW_AT_type {{.*}}"void *" ; CHECK-NEXT: DW_AT_LLVM_ptrauth_key (0x04) -; CHECK-NEXT: DW_AT_LLVM_ptrauth_address_discriminated (true) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_extra_discriminator (0x04d2) -; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d3, "isa-pointer")") +; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d3)") ; CHECK: 0x{{0+}}[[TY]]: DW_TAG_LLVM_ptrauth_type ; CHECK-NEXT: DW_AT_type {{.*}}"void *" ; CHECK-NEXT: DW_AT_LLVM_ptrauth_key (0x04) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_address_discriminated (true) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_extra_discriminator (0x04d3) -; CHECK-NEXT: DW_AT_LLVM_ptrauth_isa_pointer (true) -; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d4, "authenticates-null-values")") +; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d4, "isa-pointer")") ; CHECK: 0x{{0+}}[[TY]]: DW_TAG_LLVM_ptrauth_type ; CHECK-NEXT: DW_AT_type {{.*}}"void *" ; CHECK-NEXT: DW_AT_LLVM_ptrauth_key (0x04) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_address_discriminated (true) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_extra_discriminator (0x04d4) -; CHECK-NEXT: DW_AT_LLVM_ptrauth_authenticates_null_values (true) +; CHECK-NEXT: DW_AT_LLVM_ptrauth_isa_pointer (true) -; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d5, "isa-pointer,authenticates-null-values")") +; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d5, "authenticates-null-values")") ; CHECK: 0x{{0+}}[[TY]]: DW_TAG_LLVM_ptrauth_type ; CHECK-NEXT: DW_AT_type {{.*}}"void *" ; CHECK-NEXT: DW_AT_LLVM_ptrauth_key (0x04) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_address_discriminated (true) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_extra_discriminator (0x04d5) +; CHECK-NEXT: DW_AT_LLVM_ptrauth_authenticates_null_values (true) + +; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d6, "isa-pointer,authenticates-null-values")") +; CHECK: 0x{{0+}}[[TY]]: DW_TAG_LLVM_ptrauth_type +; CHECK-NEXT: DW_AT_type {{.*}}"void *" +; CHECK-NEXT: DW_AT_LLVM_ptrauth_key (0x04) +; CHECK-NEXT: DW_AT_LLVM_ptrauth_address_discriminated (true) +; CHECK-NEXT: DW_AT_LLVM_ptrauth_extra_discriminator (0x04d6) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_isa_pointer (true) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_authenticates_null_values (true) @@ -37,25 +43,28 @@ target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" @p = common global i8* null, align 8, !dbg !0 -!llvm.dbg.cu = !{!8} -!llvm.module.flags = !{!16, !17} - -!0 = !DIGlobalVariableExpression(var: !4, expr: !DIExpression()) -!1 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression()) -!2 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) -!3 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression()) -!4 = distinct !DIGlobalVariable(name: "p1", scope: !8, file: !9, line: 1, type: !12, isLocal: false, isDefinition: true) -!5 = distinct !DIGlobalVariable(name: "p2", scope: !8, file: !9, line: 1, type: !13, isLocal: false, isDefinition: true) -!6 = distinct !DIGlobalVariable(name: "p3", scope: !8, file: !9, line: 1, type: !14, isLocal: false, isDefinition: true) -!7 = distinct !DIGlobalVariable(name: "p4", scope: !8, file: !9, line: 1, type: !15, isLocal: false, isDefinition: true) -!8 = distinct !DICompileUnit(language: DW_LANG_C99, file: !9, emissionKind: FullDebug, globals: !11) -!9 = !DIFile(filename: "/tmp/p.c", directory: "/") -!10 = !{} -!11 = !{!0,!1,!2,!3} -!12 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !18, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1234) -!13 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !18, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1235, ptrAuthIsaPointer: true) -!14 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !18, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1236, ptrAuthAuthenticatesNullValues: true) -!15 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !18, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1237, ptrAuthIsaPointer: true, ptrAuthAuthenticatesNullValues: true) -!16 = !{i32 2, !"Dwarf Version", i32 4} -!17 = !{i32 2, !"Debug Info Version", i32 3} -!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null) +!llvm.dbg.cu = !{!10} +!llvm.module.flags = !{!19, !20} + +!0 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression()) +!1 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) +!2 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression()) +!3 = !DIGlobalVariableExpression(var: !8, expr: !DIExpression()) +!4 = !DIGlobalVariableExpression(var: !9, expr: !DIExpression()) +!5 = distinct !DIGlobalVariable(name: "p1", scope: !10, file: !11, line: 1, type: !14, isLocal: false, isDefinition: true) +!6 = distinct !DIGlobalVariable(name: "p2", scope: !10, file: !11, line: 1, type: !15, isLocal: false, isDefinition: true) +!7 = distinct !DIGlobalVariable(name: "p3", scope: !10, file: !11, line: 1, type: !16, isLocal: false, isDefinition: true) +!8 = distinct !DIGlobalVariable(name: "p4", scope: !10, file: !11, line: 1, type: !17, isLocal: false, isDefinition: true) +!9 = distinct !DIGlobalVariable(name: "p5", scope: !10, file: !11, line: 1, type: !18, isLocal: false, isDefinition: true) +!10 = distinct !DICompileUnit(language: DW_LANG_C99, file: !11, emissionKind: FullDebug, globals: !13) +!11 = !DIFile(filename: "/tmp/p.c", directory: "/") +!12 = !{} +!13 = !{!0,!1,!2,!3,!4} +!14 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !21, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: false, ptrAuthExtraDiscriminator: 1234) +!15 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !21, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1235) +!16 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !21, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1236, ptrAuthIsaPointer: true) +!17 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !21, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1237, ptrAuthAuthenticatesNullValues: true) +!18 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !21, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1238, ptrAuthIsaPointer: true, ptrAuthAuthenticatesNullValues: true) +!19 = !{i32 2, !"Dwarf Version", i32 4} +!20 = !{i32 2, !"Debug Info Version", i32 3} +!21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null) From c8d71dfb8ed17f53a1f2164e86f41a0ba11640d6 Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Mon, 12 Feb 2024 19:34:07 +0300 Subject: [PATCH 2/9] [llvm-readobj][AArch64][ELF][PAC] Support `GNU_PROPERTY_AARCH64_FEATURE_PAUTH` This adds support for `GNU_PROPERTY_AARCH64_FEATURE_PAUTH` feature handling in llvm-readobj and llvm-readelf. The following constants for supported platforms are also introduced: - `GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_INVALID = 0` - `GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_BAREMETAL = 1` - `GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_LINUX = 2` For the linux platform, output of the tools contains descriptions of PAuth features which are enabled/disabled depending on the version value. --- llvm/include/llvm/BinaryFormat/ELF.h | 8 + .../ELF/AArch64/aarch64-feature-pauth.s | 378 +++++++++++++++--- .../ELF/AArch64/aarch64-note-gnu-property.s | 2 + llvm/tools/llvm-readobj/ELFDumper.cpp | 82 ++-- 4 files changed, 381 insertions(+), 89 deletions(-) diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 326c737afa1f..2cbde7afa7cc 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -1673,6 +1673,7 @@ enum : unsigned { GNU_PROPERTY_STACK_SIZE = 1, GNU_PROPERTY_NO_COPY_ON_PROTECTED = 2, GNU_PROPERTY_AARCH64_FEATURE_1_AND = 0xc0000000, + GNU_PROPERTY_AARCH64_FEATURE_PAUTH = 0xc0000001, GNU_PROPERTY_X86_FEATURE_1_AND = 0xc0000002, GNU_PROPERTY_X86_UINT32_OR_LO = 0xc0008000, @@ -1690,6 +1691,13 @@ enum : unsigned { GNU_PROPERTY_AARCH64_FEATURE_1_PAC = 1 << 1, }; +// aarch64 PAuth platforms +enum : unsigned { + GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_INVALID = 0, + GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_BAREMETAL = 1, + GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_LINUX = 2, +}; + // x86 processor feature bits. enum : unsigned { GNU_PROPERTY_X86_FEATURE_1_IBT = 1 << 0, diff --git a/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-feature-pauth.s b/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-feature-pauth.s index 1bbc6729bc70..cc3affaea9f2 100644 --- a/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-feature-pauth.s +++ b/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-feature-pauth.s @@ -1,69 +1,8 @@ +# TODO: it looks like that tests might be shortened using some kind of templates + # RUN: rm -rf %t && split-file %s %t && cd %t -# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag.s -o tag.o -# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag-short.s -o tag-short.o -# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag-long.s -o tag-long.o - -# RUN: llvm-readelf --notes tag.o | FileCheck --check-prefix NORMAL %s -# RUN: llvm-readelf --notes tag-short.o | FileCheck --check-prefix SHORT %s -# RUN: llvm-readelf --notes tag-long.o | FileCheck --check-prefix LONG %s - -# NORMAL: AArch64 PAuth ABI tag: platform 0x2a, version 0x1 -# SHORT: AArch64 PAuth ABI tag: -# LONG: AArch64 PAuth ABI tag: platform 0x2a, version 0x1, additional info 0xEFCDAB8967452301 - -# RUN: llvm-readobj --notes tag.o | FileCheck --check-prefix LLVM-NORMAL %s -# RUN: llvm-readobj --notes tag-short.o | FileCheck --check-prefix LLVM-SHORT %s -# RUN: llvm-readobj --notes tag-long.o | FileCheck --check-prefix LLVM-LONG %s - -// LLVM-SHORT: Notes [ -// LLVM-SHORT-NEXT: NoteSection { -// LLVM-SHORT-NEXT: Name: .note.AARCH64-PAUTH-ABI-tag -// LLVM-SHORT-NEXT: Offset: 0x40 -// LLVM-SHORT-NEXT: Size: 0x1C -// LLVM-SHORT-NEXT: Note { -// LLVM-SHORT-NEXT: Owner: ARM -// LLVM-SHORT-NEXT: Data size: 0xC -// LLVM-SHORT-NEXT: Type: NT_ARM_TYPE_PAUTH_ABI_TAG -// LLVM-SHORT-NEXT: Description data ( -// LLVM-SHORT-NEXT: 0000: 2A000000 00000000 01000000 -// LLVM-SHORT-NEXT: ) -// LLVM-SHORT-NEXT: } -// LLVM-SHORT-NEXT: } -// LLVM-SHORT-NEXT: ] - -// LLVM-NORMAL: Notes [ -// LLVM-NORMAL-NEXT: NoteSection { -// LLVM-NORMAL-NEXT: Name: .note.AARCH64-PAUTH-ABI-tag -// LLVM-NORMAL-NEXT: Offset: 0x40 -// LLVM-NORMAL-NEXT: Size: 0x20 -// LLVM-NORMAL-NEXT: Note { -// LLVM-NORMAL-NEXT: Owner: ARM -// LLVM-NORMAL-NEXT: Data size: 0x10 -// LLVM-NORMAL-NEXT: Type: NT_ARM_TYPE_PAUTH_ABI_TAG -// LLVM-NORMAL-NEXT: Platform: 42 -// LLVM-NORMAL-NEXT: Version: 1 -// LLVM-NORMAL-NEXT: } -// LLVM-NORMAL-NEXT: } -// LLVM-NORMAL-NEXT: ] - -// LLVM-LONG: Notes [ -// LLVM-LONG-NEXT: NoteSection { -// LLVM-LONG-NEXT: Name: .note.AARCH64-PAUTH-ABI-tag -// LLVM-LONG-NEXT: Offset: 0x40 -// LLVM-LONG-NEXT: Size: 0x28 -// LLVM-LONG-NEXT: Note { -// LLVM-LONG-NEXT: Owner: ARM -// LLVM-LONG-NEXT: Data size: 0x18 -// LLVM-LONG-NEXT: Type: NT_ARM_TYPE_PAUTH_ABI_TAG -// LLVM-LONG-NEXT: Platform: 42 -// LLVM-LONG-NEXT: Version: 1 -// LLVM-LONG-NEXT: Additional info: EFCDAB8967452301 -// LLVM-LONG-NEXT: } -// LLVM-LONG-NEXT: } -// LLVM-LONG-NEXT: ] - -#--- abi-tag.s +#--- tag-42-1.s .section ".note.AARCH64-PAUTH-ABI-tag", "a" .long 4 @@ -74,7 +13,82 @@ .quad 42 // platform .quad 1 // version -#--- abi-tag-short.s +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu tag-42-1.s -o tag-42-1.o +# RUN: llvm-readelf --notes tag-42-1.o | \ +# RUN: FileCheck --check-prefix ELF-TAG -DPLATFORM=0x2a -DVERSION=0x1 %s +# RUN: llvm-readobj --notes tag-42-1.o | \ +# RUN: FileCheck --check-prefix OBJ-TAG -DPLATFORM=42 -DVERSION=1 %s + +# ELF-TAG: AArch64 PAuth ABI tag: platform [[PLATFORM]], version [[VERSION]] + +# OBJ-TAG: Notes [ +# OBJ-TAG-NEXT: NoteSection { +# OBJ-TAG-NEXT: Name: .note.AARCH64-PAUTH-ABI-tag +# OBJ-TAG-NEXT: Offset: 0x40 +# OBJ-TAG-NEXT: Size: 0x20 +# OBJ-TAG-NEXT: Note { +# OBJ-TAG-NEXT: Owner: ARM +# OBJ-TAG-NEXT: Data size: 0x10 +# OBJ-TAG-NEXT: Type: NT_ARM_TYPE_PAUTH_ABI_TAG +# OBJ-TAG-NEXT: Platform: [[PLATFORM]] +# OBJ-TAG-NEXT: Version: [[VERSION]] +# OBJ-TAG-NEXT: } +# OBJ-TAG-NEXT: } +# OBJ-TAG-NEXT: ] + +#--- tag-0-0.s + +.section ".note.AARCH64-PAUTH-ABI-tag", "a" +.long 4 +.long 16 +.long 1 +.asciz "ARM" + +.quad 0 // platform +.quad 0 // version + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu tag-0-0.s -o tag-0-0.o +# RUN: llvm-readelf --notes tag-0-0.o | \ +# RUN: FileCheck --check-prefix ELF-TAG -DPLATFORM="0x0 (invalid)" -DVERSION=0x0 %s +# RUN: llvm-readobj --notes tag-0-0.o | \ +# RUN: FileCheck --check-prefix OBJ-TAG -DPLATFORM=0 -DVERSION=0 %s + +#--- tag-1-0.s + +.section ".note.AARCH64-PAUTH-ABI-tag", "a" +.long 4 +.long 16 +.long 1 +.asciz "ARM" + +.quad 1 // platform +.quad 0 // version + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu tag-1-0.s -o tag-1-0.o +# RUN: llvm-readelf --notes tag-1-0.o | \ +# RUN: FileCheck --check-prefix ELF-TAG -DPLATFORM="0x1 (baremetal)" -DVERSION=0x0 %s +# RUN: llvm-readobj --notes tag-1-0.o | \ +# RUN: FileCheck --check-prefix OBJ-TAG -DPLATFORM=1 -DVERSION=0 %s + +#--- tag-2-21.s + +.section ".note.AARCH64-PAUTH-ABI-tag", "a" +.long 4 +.long 16 +.long 1 +.asciz "ARM" + +.quad 2 // platform +.quad 21 // version + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu tag-2-21.s -o tag-2-21.o +# RUN: llvm-readelf --notes tag-2-21.o | \ +# RUN: FileCheck --check-prefix ELF-TAG -DPLATFORM="0x2 (linux)" \ +# RUN: -DVERSION="0x15 (PointerAuthCalls, !PointerAuthReturns, PointerAuthVTPtrAddressDiscrimination, !PointerAuthVTPtrTypeDiscrimination, PointerAuthInitFini)" %s +# RUN: llvm-readobj --notes tag-2-21.o | \ +# RUN: FileCheck --check-prefix OBJ-TAG -DPLATFORM=2 -DVERSION=21 %s + +#--- tag-short.s .section ".note.AARCH64-PAUTH-ABI-tag", "a" .long 4 @@ -85,7 +99,29 @@ .quad 42 .word 1 -#--- abi-tag-long.s +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu tag-short.s -o tag-short.o +# RUN: llvm-readelf --notes tag-short.o | FileCheck --check-prefix ELF-TAG-SHORT %s +# RUN: llvm-readobj --notes tag-short.o | FileCheck --check-prefix OBJ-TAG-SHORT %s + +# ELF-TAG-SHORT: AArch64 PAuth ABI tag: + +# OBJ-TAG-SHORT: Notes [ +# OBJ-TAG-SHORT-NEXT: NoteSection { +# OBJ-TAG-SHORT-NEXT: Name: .note.AARCH64-PAUTH-ABI-tag +# OBJ-TAG-SHORT-NEXT: Offset: 0x40 +# OBJ-TAG-SHORT-NEXT: Size: 0x1C +# OBJ-TAG-SHORT-NEXT: Note { +# OBJ-TAG-SHORT-NEXT: Owner: ARM +# OBJ-TAG-SHORT-NEXT: Data size: 0xC +# OBJ-TAG-SHORT-NEXT: Type: NT_ARM_TYPE_PAUTH_ABI_TAG +# OBJ-TAG-SHORT-NEXT: Description data ( +# OBJ-TAG-SHORT-NEXT: 0000: 2A000000 00000000 01000000 +# OBJ-TAG-SHORT-NEXT: ) +# OBJ-TAG-SHORT-NEXT: } +# OBJ-TAG-SHORT-NEXT: } +# OBJ-TAG-SHORT-NEXT: ] + +#--- tag-long.s .section ".note.AARCH64-PAUTH-ABI-tag", "a" .long 4 @@ -96,3 +132,213 @@ .quad 42 // platform .quad 1 // version .quad 0x0123456789ABCDEF // extra data + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu tag-long.s -o tag-long.o +# RUN: llvm-readelf --notes tag-long.o | FileCheck --check-prefix ELF-TAG-LONG %s +# RUN: llvm-readobj --notes tag-long.o | FileCheck --check-prefix OBJ-TAG-LONG %s + +# ELF-TAG-LONG: AArch64 PAuth ABI tag: + +# OBJ-TAG-LONG: Notes [ +# OBJ-TAG-LONG-NEXT: NoteSection { +# OBJ-TAG-LONG-NEXT: Name: .note.AARCH64-PAUTH-ABI-tag +# OBJ-TAG-LONG-NEXT: Offset: 0x40 +# OBJ-TAG-LONG-NEXT: Size: 0x28 +# OBJ-TAG-LONG-NEXT: Note { +# OBJ-TAG-LONG-NEXT: Owner: ARM +# OBJ-TAG-LONG-NEXT: Data size: 0x18 +# OBJ-TAG-LONG-NEXT: Type: NT_ARM_TYPE_PAUTH_ABI_TAG +# OBJ-TAG-LONG-NEXT: Description data ( +# OBJ-TAG-LONG-NEXT: 0000: 2A000000 00000000 01000000 00000000 +# OBJ-TAG-LONG-NEXT: 0010: EFCDAB89 67452301 +# OBJ-TAG-LONG-NEXT: ) +# OBJ-TAG-LONG-NEXT: } +# OBJ-TAG-LONG-NEXT: } +# OBJ-TAG-LONG-NEXT: ] + +#--- gnu-42-1.s + +.section ".note.gnu.property", "a" + .long 4 /* Name length is always 4 ("GNU") */ + .long end - begin /* Data length */ + .long 5 /* Type: NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" /* Name */ + .p2align 3 +begin: + /* PAuth ABI property note */ + .long 0xc0000001 /* Type: GNU_PROPERTY_AARCH64_FEATURE_PAUTH */ + .long 16 /* Data size */ + .quad 42 /* PAuth ABI platform */ + .quad 1 /* PAuth ABI version */ + .p2align 3 /* Align to 8 byte for 64 bit */ +end: + +# RUN: llvm-mc -filetype=obj -triple aarch64-linux-gnu gnu-42-1.s -o gnu-42-1.o +# RUN: llvm-readelf --notes gnu-42-1.o | \ +# RUN: FileCheck --check-prefix=ELF-GNU -DPLATFORM=0x2a -DVERSION=0x1 %s +# RUN: llvm-readobj --notes gnu-42-1.o | \ +# RUN: FileCheck --check-prefix=OBJ-GNU -DPLATFORM=0x2a -DVERSION=0x1 %s + +# ELF-GNU: Displaying notes found in: .note.gnu.property +# ELF-GNU-NEXT: Owner Data size Description +# ELF-GNU-NEXT: GNU 0x00000018 NT_GNU_PROPERTY_TYPE_0 (property note) +# ELF-GNU-NEXT: AArch64 PAuth ABI tag: platform [[PLATFORM]], version [[VERSION]] + +# OBJ-GNU: Notes [ +# OBJ-GNU-NEXT: NoteSection { +# OBJ-GNU-NEXT: Name: .note.gnu.property +# OBJ-GNU-NEXT: Offset: 0x40 +# OBJ-GNU-NEXT: Size: 0x28 +# OBJ-GNU-NEXT: Note { +# OBJ-GNU-NEXT: Owner: GNU +# OBJ-GNU-NEXT: Data size: 0x18 +# OBJ-GNU-NEXT: Type: NT_GNU_PROPERTY_TYPE_0 (property note) +# OBJ-GNU-NEXT: Property [ +# OBJ-GNU-NEXT: AArch64 PAuth ABI tag: platform [[PLATFORM]], version [[VERSION]] +# OBJ-GNU-NEXT: ] +# OBJ-GNU-NEXT: } +# OBJ-GNU-NEXT: } +# OBJ-GNU-NEXT: ] + +#--- gnu-0-0.s + +.section ".note.gnu.property", "a" + .long 4 /* Name length is always 4 ("GNU") */ + .long end - begin /* Data length */ + .long 5 /* Type: NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" /* Name */ + .p2align 3 +begin: + /* PAuth ABI property note */ + .long 0xc0000001 /* Type: GNU_PROPERTY_AARCH64_FEATURE_PAUTH */ + .long 16 /* Data size */ + .quad 0 /* PAuth ABI platform */ + .quad 0 /* PAuth ABI version */ + .p2align 3 /* Align to 8 byte for 64 bit */ +end: + +# RUN: llvm-mc -filetype=obj -triple aarch64-linux-gnu gnu-0-0.s -o gnu-0-0.o +# RUN: llvm-readelf --notes gnu-0-0.o | \ +# RUN: FileCheck --check-prefix=ELF-GNU -DPLATFORM="0x0 (invalid)" -DVERSION=0x0 %s +# RUN: llvm-readobj --notes gnu-0-0.o | \ +# RUN: FileCheck --check-prefix=OBJ-GNU -DPLATFORM="0x0 (invalid)" -DVERSION=0x0 %s + +#--- gnu-1-0.s + +.section ".note.gnu.property", "a" + .long 4 /* Name length is always 4 ("GNU") */ + .long end - begin /* Data length */ + .long 5 /* Type: NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" /* Name */ + .p2align 3 +begin: + /* PAuth ABI property note */ + .long 0xc0000001 /* Type: GNU_PROPERTY_AARCH64_FEATURE_PAUTH */ + .long 16 /* Data size */ + .quad 1 /* PAuth ABI platform */ + .quad 0 /* PAuth ABI version */ + .p2align 3 /* Align to 8 byte for 64 bit */ +end: + +# RUN: llvm-mc -filetype=obj -triple aarch64-linux-gnu gnu-1-0.s -o gnu-1-0.o +# RUN: llvm-readelf --notes gnu-1-0.o | \ +# RUN: FileCheck --check-prefix=ELF-GNU -DPLATFORM="0x1 (baremetal)" -DVERSION=0x0 %s +# RUN: llvm-readobj --notes gnu-1-0.o | \ +# RUN: FileCheck --check-prefix=OBJ-GNU -DPLATFORM="0x1 (baremetal)" -DVERSION=0x0 %s + +#--- gnu-2-21.s + +.section ".note.gnu.property", "a" + .long 4 /* Name length is always 4 ("GNU") */ + .long end - begin /* Data length */ + .long 5 /* Type: NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" /* Name */ + .p2align 3 +begin: + /* PAuth ABI property note */ + .long 0xc0000001 /* Type: GNU_PROPERTY_AARCH64_FEATURE_PAUTH */ + .long 16 /* Data size */ + .quad 2 /* PAuth ABI platform */ + .quad 21 /* PAuth ABI version */ + .p2align 3 /* Align to 8 byte for 64 bit */ +end: + +# RUN: llvm-mc -filetype=obj -triple aarch64-linux-gnu gnu-2-21.s -o gnu-2-21.o +# RUN: llvm-readelf --notes gnu-2-21.o | \ +# RUN: FileCheck --check-prefix=ELF-GNU -DPLATFORM="0x2 (linux)" \ +# RUN: -DVERSION="0x15 (PointerAuthCalls, !PointerAuthReturns, PointerAuthVTPtrAddressDiscrimination, !PointerAuthVTPtrTypeDiscrimination, PointerAuthInitFini)" %s +# RUN: llvm-readobj --notes gnu-2-21.o | \ +# RUN: FileCheck --check-prefix=OBJ-GNU -DPLATFORM="0x2 (linux)" \ +# RUN: -DVERSION="0x15 (PointerAuthCalls, !PointerAuthReturns, PointerAuthVTPtrAddressDiscrimination, !PointerAuthVTPtrTypeDiscrimination, PointerAuthInitFini)" %s + +#--- gnu-short.s + +.section ".note.gnu.property", "a" + .long 4 /* Name length is always 4 ("GNU") */ + .long end - begin /* Data length */ + .long 5 /* Type: NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" /* Name */ + .p2align 3 +begin: + /* PAuth ABI property note */ + .long 0xc0000001 /* Type: GNU_PROPERTY_AARCH64_FEATURE_PAUTH */ + .long 12 /* Data size */ + .quad 42 /* PAuth ABI platform */ + .word 1 /* PAuth ABI version */ + .p2align 3 /* Align to 8 byte for 64 bit */ +end: + +# RUN: llvm-mc -filetype=obj -triple aarch64-linux-gnu gnu-short.s -o gnu-short.o +# RUN: llvm-readelf --notes gnu-short.o | \ +# RUN: FileCheck --check-prefix=ELF-GNU-ERR -DSIZE=28 -DDATASIZE=18 \ +# RUN: -DERR="" %s +# RUN: llvm-readobj --notes gnu-short.o | \ +# RUN: FileCheck --check-prefix=OBJ-GNU-ERR -DSIZE=28 -DDATASIZE=18 \ +# RUN: -DERR="" %s + +# ELF-GNU-ERR: Displaying notes found in: .note.gnu.property +# ELF-GNU-ERR-NEXT: Owner Data size Description +# ELF-GNU-ERR-NEXT: GNU 0x000000[[DATASIZE]] NT_GNU_PROPERTY_TYPE_0 (property note) +# ELF-GNU-ERR-NEXT: AArch64 PAuth ABI tag: [[ERR]] + +# OBJ-GNU-ERR: Notes [ +# OBJ-GNU-ERR-NEXT: NoteSection { +# OBJ-GNU-ERR-NEXT: Name: .note.gnu.property +# OBJ-GNU-ERR-NEXT: Offset: 0x40 +# OBJ-GNU-ERR-NEXT: Size: 0x[[SIZE]] +# OBJ-GNU-ERR-NEXT: Note { +# OBJ-GNU-ERR-NEXT: Owner: GNU +# OBJ-GNU-ERR-NEXT: Data size: 0x[[DATASIZE]] +# OBJ-GNU-ERR-NEXT: Type: NT_GNU_PROPERTY_TYPE_0 (property note) +# OBJ-GNU-ERR-NEXT: Property [ +# OBJ-GNU-ERR-NEXT: AArch64 PAuth ABI tag: [[ERR]] +# OBJ-GNU-ERR-NEXT: ] +# OBJ-GNU-ERR-NEXT: } +# OBJ-GNU-ERR-NEXT: } +# OBJ-GNU-ERR-NEXT: ] + +#--- gnu-long.s + +.section ".note.gnu.property", "a" + .long 4 /* Name length is always 4 ("GNU") */ + .long end - begin /* Data length */ + .long 5 /* Type: NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" /* Name */ + .p2align 3 +begin: + /* PAuth ABI property note */ + .long 0xc0000001 /* Type: GNU_PROPERTY_AARCH64_FEATURE_PAUTH */ + .long 24 /* Data size */ + .quad 42 /* PAuth ABI platform */ + .quad 1 /* PAuth ABI version */ + .quad 0x0123456789ABCDEF + .p2align 3 /* Align to 8 byte for 64 bit */ +end: + +# RUN: llvm-mc -filetype=obj -triple aarch64-linux-gnu gnu-long.s -o gnu-long.o +# RUN: llvm-readelf --notes gnu-long.o | \ +# RUN: FileCheck --check-prefix=ELF-GNU-ERR -DSIZE=30 -DDATASIZE=20 \ +# RUN: -DERR="" %s +# RUN: llvm-readobj --notes gnu-long.o | \ +# RUN: FileCheck --check-prefix=OBJ-GNU-ERR -DSIZE=30 -DDATASIZE=20 \ +# RUN: -DERR="" %s diff --git a/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-note-gnu-property.s b/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-note-gnu-property.s index 872a3f150fdf..b680bf48cd1a 100644 --- a/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-note-gnu-property.s +++ b/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-note-gnu-property.s @@ -1,3 +1,5 @@ +// See tests for GNU_PROPERTY_AARCH64_FEATURE_PAUTH in aarch64-feature-pauth.s + // RUN: llvm-mc -filetype=obj -triple aarch64-linux-gnu %s -o %t // RUN: llvm-readelf --notes %t | FileCheck %s --check-prefix=GNU // RUN: llvm-readobj --notes %t | FileCheck %s --check-prefix=LLVM diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 25b78a94fe7a..23a149ca6335 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -5105,6 +5105,51 @@ template void GNUELFDumper::printAddrsig() { } } +template +static bool printAArch64PAuthABITag(raw_ostream &OS, uint32_t DataSize, + ArrayRef Desc) { + OS << " AArch64 PAuth ABI tag: "; + // DataSize - size without padding, Desc.size() - size with padding + if (DataSize != 16) { + OS << format("", DataSize); + return false; + } + + uint64_t Platform = + support::endian::read64(Desc.data() + 0); + std::string PlatformDesc = [Platform]() { + switch (Platform) { + case GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_INVALID: + return " (invalid)"; + case GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_BAREMETAL: + return " (baremetal)"; + case GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_LINUX: + return " (linux)"; + default: + return ""; + } + }(); + + uint64_t Version = + support::endian::read64(Desc.data() + 8); + std::string VersionDesc = [Platform, Version]() -> std::string { + if (Platform != GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_LINUX) + return ""; + // TODO: notify if Version contains bits higher than 4 set + return std::string(" (") + ((Version & (1 << 0)) ? "" : "!") + + "PointerAuthCalls, " + ((Version & (1 << 1)) ? "" : "!") + + "PointerAuthReturns, " + ((Version & (1 << 2)) ? "" : "!") + + "PointerAuthVTPtrAddressDiscrimination, " + + ((Version & (1 << 3)) ? "" : "!") + + "PointerAuthVTPtrTypeDiscrimination, " + + ((Version & (1 << 4)) ? "" : "!") + "PointerAuthInitFini)"; + }(); + OS << format("platform 0x%x%s, version 0x%x%s", Platform, + PlatformDesc.c_str(), Version, VersionDesc.c_str()); + + return true; +} + template static std::string getGNUProperty(uint32_t Type, uint32_t DataSize, ArrayRef Data) { @@ -5161,6 +5206,9 @@ static std::string getGNUProperty(uint32_t Type, uint32_t DataSize, if (PrData) OS << format("", PrData); return OS.str(); + case GNU_PROPERTY_AARCH64_FEATURE_PAUTH: + printAArch64PAuthABITag(OS, DataSize, Data); + return OS.str(); case GNU_PROPERTY_X86_FEATURE_2_NEEDED: case GNU_PROPERTY_X86_FEATURE_2_USED: OS << "x86 feature " @@ -5365,26 +5413,13 @@ static bool printAndroidNote(raw_ostream &OS, uint32_t NoteType, template static bool printAArch64Note(raw_ostream &OS, uint32_t NoteType, ArrayRef Desc) { - if (NoteType != NT_ARM_TYPE_PAUTH_ABI_TAG) - return false; - - OS << " AArch64 PAuth ABI tag: "; - if (Desc.size() < 16) { - OS << format("", Desc.size()); - return false; + switch (NoteType) { + case NT_ARM_TYPE_PAUTH_ABI_TAG: + // TODO: if Desc.size() includes alignment, pass the real length from the + // caller + return printAArch64PAuthABITag(OS, Desc.size(), Desc); } - - uint64_t platform = - support::endian::read64(Desc.data() + 0); - uint64_t version = - support::endian::read64(Desc.data() + 8); - OS << format("platform 0x%x, version 0x%x", platform, version); - - if (Desc.size() > 16) - OS << ", additional info 0x" - << toHex(ArrayRef(Desc.data() + 16, Desc.size() - 16)); - - return true; + return false; } template @@ -7640,7 +7675,7 @@ static bool printAarch64NoteLLVMStyle(uint32_t NoteType, ArrayRef Desc, if (NoteType != NT_ARM_TYPE_PAUTH_ABI_TAG) return false; - if (Desc.size() < 16) + if (Desc.size() != 16) return false; uint64_t platform = @@ -7650,9 +7685,8 @@ static bool printAarch64NoteLLVMStyle(uint32_t NoteType, ArrayRef Desc, W.printNumber("Platform", platform); W.printNumber("Version", version); - if (Desc.size() > 16) - W.printString("Additional info", - toHex(ArrayRef(Desc.data() + 16, Desc.size() - 16))); + // TODO: print nice description for supported tuples (see + // printAArch64PAuthABITag). return true; } @@ -7796,6 +7830,8 @@ template void LLVMELFDumper::printNotes() { } else if (Name == "ARM") { if (printAarch64NoteLLVMStyle(Type, Descriptor, W)) return Error::success(); + // TODO: notify about parsing failure, now we just silently print raw + // section contents } if (!Descriptor.empty()) { W.printBinaryBlock("Description data", Descriptor); From a0cc186396cb71ace1981059a5e3da4af7001573 Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Mon, 12 Feb 2024 19:43:50 +0300 Subject: [PATCH 3/9] [AArch64][PAC][clang][ELF] Support PAuth ABI compatibility tag Emit PAuth ABI compatibility tag values as llvm module flags: - `aarch64-elf-pauthabi-platform` - `aarch64-elf-pauthabi-version` --- clang/lib/CodeGen/CodeGenModule.cpp | 18 +++++++++++++++ clang/test/CodeGen/aarch64-elf-pauthabi.c | 28 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 clang/test/CodeGen/aarch64-elf-pauthabi.c diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index d8ecf681f6d6..75ccd28057ac 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -51,6 +51,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" #include "llvm/IR/AttributeMask.h" #include "llvm/IR/CallingConv.h" @@ -1075,6 +1076,23 @@ void CodeGenModule::Release() { if (!LangOpts.isSignReturnAddressWithAKey()) getModule().addModuleFlag(llvm::Module::Min, "sign-return-address-with-bkey", 1); + + if (getTriple().isOSLinux() && getTriple().isOSBinFormatELF()) { + uint64_t PAuthABIVersion = + (LangOpts.PointerAuthCalls << 0) | + (LangOpts.PointerAuthReturns << 1) | + (LangOpts.PointerAuthVTPtrAddressDiscrimination << 2) | + (LangOpts.PointerAuthVTPtrTypeDiscrimination << 3) | + (LangOpts.PointerAuthInitFini << 4); + if (PAuthABIVersion != 0) { + getModule().addModuleFlag( + llvm::Module::Error, "aarch64-elf-pauthabi-platform", + llvm::ELF::GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_LINUX); + getModule().addModuleFlag(llvm::Module::Error, + "aarch64-elf-pauthabi-version", + PAuthABIVersion); + } + } } if (!CodeGenOpts.MemoryProfileOutput.empty()) { diff --git a/clang/test/CodeGen/aarch64-elf-pauthabi.c b/clang/test/CodeGen/aarch64-elf-pauthabi.c new file mode 100644 index 000000000000..5b65dfed9338 --- /dev/null +++ b/clang/test/CodeGen/aarch64-elf-pauthabi.c @@ -0,0 +1,28 @@ +// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -mbranch-protection=pauthabi %s | FileCheck %s --check-prefix=ALL +// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-returns %s | FileCheck %s --check-prefix=RET +// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-calls %s | FileCheck %s --check-prefix=CALL +// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-calls -fptrauth-vtable-pointer-address-discrimination %s | FileCheck %s --check-prefix=VPTRADDR +// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination %s | FileCheck %s --check-prefix=VPTRTYPE +// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-calls -fptrauth-init-fini %s | FileCheck %s --check-prefix=INITFINI + +// REQUIRES: aarch64-registered-target + +// ALL: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} +// ALL: !{i32 1, !"aarch64-elf-pauthabi-version", i32 31} + +// RET: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} +// RET: !{i32 1, !"aarch64-elf-pauthabi-version", i32 2} + +// CALL: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} +// CALL: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1} + +// VPTRADDR: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} +// VPTRADDR: !{i32 1, !"aarch64-elf-pauthabi-version", i32 5} + +// VPTRTYPE: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} +// VPTRTYPE: !{i32 1, !"aarch64-elf-pauthabi-version", i32 9} + +// INITFINI: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} +// INITFINI: !{i32 1, !"aarch64-elf-pauthabi-version", i32 17} + +void foo() {} From a3ddb4e8aba8e997b76d2311b9f319afaaeabe90 Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Mon, 12 Feb 2024 19:46:04 +0300 Subject: [PATCH 4/9] [AArch64][PAC][MC][ELF] Support PAuth ABI compatibility tag Emit `GNU_PROPERTY_AARCH64_FEATURE_PAUTH` property in `.note.gnu.property` section depending on `aarch64-elf-pauthabi-platform` and `aarch64-elf-pauthabi-version` llvm module flags. --- llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 19 +++++-- .../MCTargetDesc/AArch64TargetStreamer.cpp | 34 ++++++++++--- .../MCTargetDesc/AArch64TargetStreamer.h | 3 +- .../AArch64/note-gnu-property-elf-pauthabi.ll | 50 +++++++++++++++++++ 4 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/note-gnu-property-elf-pauthabi.ll diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index 152105c20b11..1095a1f5787a 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -265,6 +265,8 @@ void AArch64AsmPrinter::emitStartOfAsmFile(Module &M) { S, MCConstantExpr::create(Feat00Value, MMI->getContext())); } + // TODO: enhance naming to distinguish MachO ptrauth ABI version and ELF + // pauthabi platform and version if (TM.getTargetTriple().isOSBinFormatMachO()) EmitPtrAuthVersion(M); @@ -283,13 +285,24 @@ void AArch64AsmPrinter::emitStartOfAsmFile(Module &M) { if (Sign->getZExtValue()) Flags |= ELF::GNU_PROPERTY_AARCH64_FEATURE_1_PAC; - if (Flags == 0) - return; + uint64_t PAuthABIPlatform = -1; + if (const auto *PAP = mdconst::extract_or_null( + M.getModuleFlag("aarch64-elf-pauthabi-platform"))) + PAuthABIPlatform = PAP->getZExtValue(); + uint64_t PAuthABIVersion = -1; + if (const auto *PAV = mdconst::extract_or_null( + M.getModuleFlag("aarch64-elf-pauthabi-version"))) + PAuthABIVersion = PAV->getZExtValue(); + + if ((PAuthABIPlatform == uint64_t(-1)) != (PAuthABIVersion == uint64_t(-1))) + report_fatal_error( + "either both or no 'aarch64-elf-pauthabi-platform' and " + "'aarch64-elf-pauthabi-version' module flags must be present"); // Emit a .note.gnu.property section with the flags. auto *TS = static_cast(OutStreamer->getTargetStreamer()); - TS->emitNoteSection(Flags); + TS->emitNoteSection(Flags, PAuthABIPlatform, PAuthABIVersion); } void AArch64AsmPrinter::emitFunctionHeaderComment() { diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp index e1d6dd7a056b..0b13f5868e60 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp @@ -56,10 +56,20 @@ void AArch64TargetStreamer::emitConstantPools() { void AArch64TargetStreamer::finish() { if (MarkBTIProperty) emitNoteSection(ELF::GNU_PROPERTY_AARCH64_FEATURE_1_BTI); + // TODO: do we need to handle GNU_PROPERTY_AARCH64_FEATURE_PAUTH here? } -void AArch64TargetStreamer::emitNoteSection(unsigned Flags) { - if (Flags == 0) +void AArch64TargetStreamer::emitNoteSection(unsigned Flags, + uint64_t PAuthABIPlatform, + uint64_t PAuthABIVersion) { + assert((PAuthABIPlatform == uint64_t(-1)) == + (PAuthABIVersion == uint64_t(-1))); + uint64_t DescSz = 0; + if (Flags != 0) + DescSz += 4 * 4; + if (PAuthABIPlatform != uint64_t(-1)) + DescSz += 4 + 4 + 8 * 2; + if (DescSz == 0) return; MCStreamer &OutStreamer = getStreamer(); @@ -80,15 +90,25 @@ void AArch64TargetStreamer::emitNoteSection(unsigned Flags) { // Emit the note header. OutStreamer.emitValueToAlignment(Align(8)); OutStreamer.emitIntValue(4, 4); // data size for "GNU\0" - OutStreamer.emitIntValue(4 * 4, 4); // Elf_Prop size + OutStreamer.emitIntValue(DescSz, 4); // Elf_Prop array size OutStreamer.emitIntValue(ELF::NT_GNU_PROPERTY_TYPE_0, 4); OutStreamer.emitBytes(StringRef("GNU", 4)); // note name // Emit the PAC/BTI properties. - OutStreamer.emitIntValue(ELF::GNU_PROPERTY_AARCH64_FEATURE_1_AND, 4); - OutStreamer.emitIntValue(4, 4); // data size - OutStreamer.emitIntValue(Flags, 4); // data - OutStreamer.emitIntValue(0, 4); // pad + if (Flags != 0) { + OutStreamer.emitIntValue(ELF::GNU_PROPERTY_AARCH64_FEATURE_1_AND, 4); + OutStreamer.emitIntValue(4, 4); // data size + OutStreamer.emitIntValue(Flags, 4); // data + OutStreamer.emitIntValue(0, 4); // pad + } + + // Emit the PAuth ABI compatibility info + if (PAuthABIPlatform != uint64_t(-1)) { + OutStreamer.emitIntValue(ELF::GNU_PROPERTY_AARCH64_FEATURE_PAUTH, 4); + OutStreamer.emitIntValue(8 * 2, 4); // data size + OutStreamer.emitIntValue(PAuthABIPlatform, 8); + OutStreamer.emitIntValue(PAuthABIVersion, 8); + } OutStreamer.endSection(Nt); OutStreamer.switchSection(Cur); diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h index b3bce9960772..716ca1b3286c 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h @@ -35,7 +35,8 @@ class AArch64TargetStreamer : public MCTargetStreamer { void emitCurrentConstantPool(); /// Callback used to implement the .note.gnu.property section. - void emitNoteSection(unsigned Flags); + void emitNoteSection(unsigned Flags, uint64_t PAuthABIPlatform = -1, + uint64_t PAuthABIVersion = -1); /// Callback used to implement the .inst directive. virtual void emitInst(uint32_t Inst); diff --git a/llvm/test/CodeGen/AArch64/note-gnu-property-elf-pauthabi.ll b/llvm/test/CodeGen/AArch64/note-gnu-property-elf-pauthabi.ll new file mode 100644 index 000000000000..46d88c69690d --- /dev/null +++ b/llvm/test/CodeGen/AArch64/note-gnu-property-elf-pauthabi.ll @@ -0,0 +1,50 @@ +; RUN: rm -rf %t && split-file %s %t && cd %t + +;--- ok.ll + +; RUN: llc -mtriple=aarch64-linux ok.ll -o - | \ +; RUN: FileCheck %s --check-prefix=ASM +; RUN: llc -mtriple=aarch64-linux ok.ll -filetype=obj -o - | \ +; RUN: llvm-readelf --notes - | FileCheck %s --check-prefix=OBJ + +!llvm.module.flags = !{!0, !1} + +!0 = !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} +!1 = !{i32 1, !"aarch64-elf-pauthabi-version", i32 31} + +; ASM: .section .note.gnu.property,"a",@note +; ASM-NEXT: .p2align 3, 0x0 +; ASM-NEXT: .word 4 +; ASM-NEXT: .word 24 +; ASM-NEXT: .word 5 +; ASM-NEXT: .asciz "GNU" +; 3221225473 = 0xc0000001 = GNU_PROPERTY_AARCH64_FEATURE_PAUTH +; ASM-NEXT: .word 3221225473 +; ASM-NEXT: .word 16 +; ASM-NEXT: .xword 2 +; ASM-NEXT: .xword 31 + +; OBJ: Displaying notes found in: .note.gnu.property +; OBJ-NEXT: Owner Data size Description +; OBJ-NEXT: GNU 0x00000018 NT_GNU_PROPERTY_TYPE_0 (property note) +; OBJ-NEXT: AArch64 PAuth ABI tag: platform 0x2 (linux), version 0x1f (PointerAuthCalls, PointerAuthReturns, PointerAuthVTPtrAddressDiscrimination, PointerAuthVTPtrTypeDiscrimination, PointerAuthInitFini) + +; ERR: either both or no 'aarch64-elf-pauthabi-platform' and 'aarch64-elf-pauthabi-version' module flags must be present + +;--- err1.ll + +; RUN: not --crash llc -mtriple=aarch64-linux err1.ll 2>&1 -o - | \ +; RUN: FileCheck %s --check-prefix=ERR + +!llvm.module.flags = !{!0} + +!0 = !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} + +;--- err2.ll + +; RUN: not --crash llc -mtriple=aarch64-linux err2.ll 2>&1 -o - | \ +; RUN: FileCheck %s --check-prefix=ERR + +!llvm.module.flags = !{!0} + +!0 = !{i32 1, !"aarch64-elf-pauthabi-version", i32 31} From e4ff3bfc7da92ec223c8a108d40da33e9f710133 Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Thu, 8 Feb 2024 17:51:00 +0300 Subject: [PATCH 5/9] [AArch64][PAC][lld][ELF] Support `GNU_PROPERTY_AARCH64_FEATURE_PAUTH` - In new PAuth ABI version, support for extra data (except platform and version) is dropped. Change container for holding the tag from `SmallVector` to `std::optional>`. - In new PAuth ABI version, the GNU property section becomes the main way of ELF marking. Support for alternative way is temporarily disabled in this commit unless the corresponding mainline PR and ARM PAuth ABI PR are merged. --- lld/ELF/Config.h | 3 +- lld/ELF/Driver.cpp | 20 ++-- lld/ELF/InputFiles.cpp | 53 +++++++--- lld/ELF/InputFiles.h | 2 +- lld/ELF/SyntheticSections.cpp | 52 ++++++---- lld/ELF/SyntheticSections.h | 2 +- lld/ELF/Writer.cpp | 15 ++- lld/test/ELF/aarch64-feature-pauth-gnu-prop.s | 96 +++++++++++++++++++ lld/test/ELF/aarch64-feature-pauth.s | 6 ++ 9 files changed, 202 insertions(+), 47 deletions(-) create mode 100644 lld/test/ELF/aarch64-feature-pauth-gnu-prop.s diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 842c21fa2ac5..598416016da7 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -27,6 +27,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/GlobPattern.h" #include "llvm/Support/PrettyStackTrace.h" +#include #include #include #include @@ -492,7 +493,7 @@ struct Ctx { llvm::raw_fd_ostream openAuxiliaryFile(llvm::StringRef, std::error_code &); - SmallVector aarch64PauthAbiTag; + std::optional> aarch64PauthAbiTag; }; LLVM_LIBRARY_VISIBILITY extern Ctx ctx; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 8cf6438f5ae4..4f370e2b7eb8 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -2662,7 +2662,7 @@ static void getAarch64PauthInfo() { auto NonEmptyIt = std::find_if( ctx.objectFiles.begin(), ctx.objectFiles.end(), - [](const ELFFileBase *f) { return !f->aarch64PauthAbiTag.empty(); }); + [](const ELFFileBase *f) { return f->aarch64PauthAbiTag.has_value(); }); if (NonEmptyIt == ctx.objectFiles.end()) return; @@ -2670,9 +2670,9 @@ static void getAarch64PauthInfo() { StringRef F1 = (*NonEmptyIt)->getName(); for (ELFFileBase *F : ArrayRef(ctx.objectFiles)) { StringRef F2 = F->getName(); - const SmallVector &D1 = ctx.aarch64PauthAbiTag; - const SmallVector &D2 = F->aarch64PauthAbiTag; - if (D1.empty() != D2.empty()) { + std::optional> D1 = ctx.aarch64PauthAbiTag; + std::optional> D2 = F->aarch64PauthAbiTag; + if (D1.has_value() != D2.has_value()) { auto Helper = [](StringRef Report, const Twine &Msg) { if (Report == "warning") warn(Msg); @@ -2681,19 +2681,19 @@ static void getAarch64PauthInfo() { }; Helper(config->zPauthReport, - (D1.empty() ? F1.str() : F2.str()) + + (D1.has_value() ? F2.str() : F1.str()) + " has no AArch64 PAuth compatibility info while " + - (D1.empty() ? F2.str() : F1.str()) + + (D1.has_value() ? F1.str() : F2.str()) + " has one; either all or no input files must have it"); } - if (!D1.empty() && !D2.empty() && - !std::equal(D1.begin(), D1.end(), D2.begin(), D2.end())) + if (D1.has_value() && D2.has_value() && + !std::equal(D1->begin(), D1->end(), D2->begin(), D2->end())) errorOrWarn( "incompatible values of AArch64 PAuth compatibility info found" "\n" + - F1 + ": 0x" + toHex(ArrayRef(D1.data(), D1.size())) + "\n" + F2 + - ": 0x" + toHex(ArrayRef(D2.data(), D2.size()))); + F1 + ": 0x" + toHex(ArrayRef(D1->data(), D1->size())) + "\n" + F2 + + ": 0x" + toHex(ArrayRef(D2->data(), D2->size()))); } } diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 873438033a65..821a14003df1 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -31,6 +31,8 @@ #include "llvm/Support/TarWriter.h" #include "llvm/Support/raw_ostream.h" +#include + using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; @@ -878,11 +880,14 @@ void ObjFile::initializeSections(bool ignoreComdats, // of zero or more type-length-value fields. We want to find a field of a // certain type. It seems a bit too much to just store a 32-bit value, perhaps // the ABI is unnecessarily complicated. -template static uint32_t readAndFeatures(const InputSection &sec) { +template +static std::pair>> +readGnuProperty(const InputSection &sec) { using Elf_Nhdr = typename ELFT::Nhdr; using Elf_Note = typename ELFT::Note; uint32_t featuresSet = 0; + std::optional> aarch64PauthAbiTag; ArrayRef data = sec.content(); auto reportFatal = [&](const uint8_t *place, const char *msg) { fatal(toString(sec.file) + ":(" + sec.name + "+0x" + @@ -924,6 +929,19 @@ template static uint32_t readAndFeatures(const InputSection &sec) { if (size < 4) reportFatal(place, "FEATURE_1_AND entry is too short"); featuresSet |= read32(desc.data()); + } else if (config->emachine == EM_AARCH64 && + type == GNU_PROPERTY_AARCH64_FEATURE_PAUTH) { + if (aarch64PauthAbiTag != std::nullopt) + reportFatal(data.data(), + "multiple GNU_PROPERTY_AARCH64_FEATURE_PAUTH properties " + "are not allowed"); + if (size != 16) + reportFatal( + data.data(), + "size of GNU_PROPERTY_AARCH64_FEATURE_PAUTH property must be 16"); + aarch64PauthAbiTag = std::array{}; + memcpy(aarch64PauthAbiTag->data(), desc.data(), + aarch64PauthAbiTag->size()); } // Padding is present in the note descriptor, if necessary. @@ -934,7 +952,7 @@ template static uint32_t readAndFeatures(const InputSection &sec) { data = data.slice(nhdr->getSize(sec.addralign)); } - return featuresSet; + return {featuresSet, aarch64PauthAbiTag}; } // Extract compatibility info for aarch64 pointer authentication from the @@ -966,13 +984,15 @@ static void readAArch64PauthAbiTag(const InputSection &sec, ObjFile &f) { " (ARM expected)"); ArrayRef desc = note.getDesc(sec.addralign); - if (desc.size() < 16) { - reportError("too short AArch64 PAuth compatibility info " - "(at least 16 bytes expected)"); + if (desc.size() != 16) { + reportError("invalid AArch64 PAuth compatibility info length " + "(exactly 16 bytes expected)"); return; } - f.aarch64PauthAbiTag = SmallVector(iterator_range(desc)); + f.aarch64PauthAbiTag = std::array{}; + memcpy(f.aarch64PauthAbiTag->data(), desc.data(), + f.aarch64PauthAbiTag->size()); } template @@ -1029,15 +1049,24 @@ InputSectionBase *ObjFile::createInputSection(uint32_t idx, // .note.gnu.property containing a single AND'ed bitmap, we discard an input // file's .note.gnu.property section. if (name == ".note.gnu.property") { - this->andFeatures = readAndFeatures(InputSection(*this, sec, name)); + std::tie(this->andFeatures, this->aarch64PauthAbiTag) = + readGnuProperty(InputSection(*this, sec, name)); return &InputSection::discarded; } - if (config->emachine == EM_AARCH64 && - name == ".note.AARCH64-PAUTH-ABI-tag") { - readAArch64PauthAbiTag(InputSection(*this, sec, name), *this); - return &InputSection::discarded; - } + // TODO: alternative PAuth ELF marking way + // To be implemented after the following PRs are merged: + // - https://github.com/ARM-software/abi-aa/pull/240 + // - https://github.com/llvm/llvm-project/pull/72714 + // + // if (config->emachine == EM_AARCH64 && + // name == ".note.AARCH64-PAUTH-ABI-tag") { + // if (this->aarch64PauthAbiTag != std::nullopt) + // error("cannot mix two PAuth ABI tag ELF marking ways in one object + // file"); + // readAArch64PauthAbiTag(InputSection(*this, sec, name), *this); + // return &InputSection::discarded; + // } // Split stacks is a feature to support a discontiguous stack, // commonly used in the programming language Go. For the details, diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index 7b6eb0720068..63a7a5da03fd 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -218,7 +218,7 @@ class ELFFileBase : public InputFile { public: uint32_t andFeatures = 0; bool hasCommonSyms = false; - SmallVector aarch64PauthAbiTag; + std::optional> aarch64PauthAbiTag; }; // .o file. diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 51a017d8fb6c..24737733a814 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -314,33 +314,56 @@ GnuPropertySection::GnuPropertySection() config->wordsize, ".note.gnu.property") {} void GnuPropertySection::writeTo(uint8_t *buf) { + write32(buf, 4); // Name size + write32(buf + 4, getSize() - 16); // Content size + write32(buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type + memcpy(buf + 12, "GNU", 4); // Name string + uint32_t featureAndType = config->emachine == EM_AARCH64 ? GNU_PROPERTY_AARCH64_FEATURE_1_AND : GNU_PROPERTY_X86_FEATURE_1_AND; - write32(buf, 4); // Name size - write32(buf + 4, config->is64 ? 16 : 12); // Content size - write32(buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type - memcpy(buf + 12, "GNU", 4); // Name string - write32(buf + 16, featureAndType); // Feature type - write32(buf + 20, 4); // Feature size - write32(buf + 24, config->andFeatures); // Feature flags - if (config->is64) - write32(buf + 28, 0); // Padding + unsigned offset = 16; + + if (config->andFeatures != 0) { + write32(buf + offset + 0, featureAndType); // Feature type + write32(buf + offset + 4, 4); // Feature size + write32(buf + offset + 8, config->andFeatures); // Feature flags + if (config->is64) + write32(buf + offset + 12, 0); // Padding + offset += 16; + } + + if (ctx.aarch64PauthAbiTag != std::nullopt) { + write32(buf + offset + 0, GNU_PROPERTY_AARCH64_FEATURE_PAUTH); + write32(buf + offset + 4, 8 * 2); + memcpy(buf + offset + 8, ctx.aarch64PauthAbiTag->data(), + ctx.aarch64PauthAbiTag->size()); + } } -size_t GnuPropertySection::getSize() const { return config->is64 ? 32 : 28; } +size_t GnuPropertySection::getSize() const { + uint32_t contentSize = 0; + if (config->andFeatures != 0) + contentSize += config->is64 ? 16 : 12; + if (ctx.aarch64PauthAbiTag != std::nullopt) { + assert(config->emachine == EM_AARCH64); + contentSize += 4 + 4 + 8 * 2; + } + assert(contentSize != 0); + return contentSize + 16; +} AArch64PauthAbiTag::AArch64PauthAbiTag() : SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE, config->wordsize, ".note.AARCH64-PAUTH-ABI-tag") {} bool AArch64PauthAbiTag::isNeeded() const { - return !ctx.aarch64PauthAbiTag.empty(); + return ctx.aarch64PauthAbiTag != std::nullopt; } void AArch64PauthAbiTag::writeTo(uint8_t *buf) { - const SmallVector &data = ctx.aarch64PauthAbiTag; + std::array data = ctx.aarch64PauthAbiTag.value(); write32(buf, 4); // Name size write32(buf + 4, data.size()); // Content size write32(buf + 8, NT_ARM_TYPE_PAUTH_ABI_TAG); // Type @@ -349,11 +372,6 @@ void AArch64PauthAbiTag::writeTo(uint8_t *buf) { memset(buf + 16 + data.size(), 0, getSize() - 16 - data.size()); // Padding } -size_t AArch64PauthAbiTag::getSize() const { - return alignToPowerOf2(16 + ctx.aarch64PauthAbiTag.size(), - config->is64 ? 8 : 4); -} - BuildIdSection::BuildIdSection() : SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"), hashSize(getHashSize()) {} diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h index d183a547c682..5b81d2005228 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -150,7 +150,7 @@ class AArch64PauthAbiTag final : public SyntheticSection { public: AArch64PauthAbiTag(); void writeTo(uint8_t *buf) override; - size_t getSize() const override; + size_t getSize() const override { return 32; }; bool isNeeded() const override; }; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index ed68da5d442e..a1ff1d2a7969 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -521,13 +521,18 @@ template void elf::createSyntheticSections() { in.iplt = std::make_unique(); add(*in.iplt); - if (config->andFeatures) + if (config->andFeatures || ctx.aarch64PauthAbiTag.has_value()) add(*make()); - if (!ctx.aarch64PauthAbiTag.empty()) { - in.aarch64PauthAbiTag = std::make_unique(); - add(*in.aarch64PauthAbiTag); - } + // TODO: alternative PAuth ELF marking way + // To be implemented after the following PRs are merged: + // - https://github.com/ARM-software/abi-aa/pull/240 + // - https://github.com/llvm/llvm-project/pull/72714 + // + // if (!ctx.aarch64PauthAbiTag.empty()) { + // in.aarch64PauthAbiTag = std::make_unique(); + // add(*in.aarch64PauthAbiTag); + // } // .note.GNU-stack is always added when we are creating a re-linkable // object file. Other linkers are using the presence of this marker diff --git a/lld/test/ELF/aarch64-feature-pauth-gnu-prop.s b/lld/test/ELF/aarch64-feature-pauth-gnu-prop.s new file mode 100644 index 000000000000..976f9c1955c1 --- /dev/null +++ b/lld/test/ELF/aarch64-feature-pauth-gnu-prop.s @@ -0,0 +1,96 @@ +# REQUIRES: aarch64 + +# RUN: rm -rf %t && split-file %s %t && cd %t + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag1.s -o tag11.o +# RUN: cp tag11.o tag12.o +# RUN: ld.lld -shared tag11.o tag12.o -o tagok.so +# RUN: llvm-readelf -n tagok.so | FileCheck --check-prefix OK %s + +# OK: Properties: AArch64 PAuth ABI tag: platform 0x2a, version 0x1 + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag2.s -o tag2.o +# RUN: not ld.lld tag11.o tag12.o tag2.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR1 %s + +# ERR1: error: incompatible values of AArch64 PAuth compatibility info found +# ERR1: {{.*}}: 0x2A000000000000000{{1|2}}00000000000000 +# ERR1: {{.*}}: 0x2A000000000000000{{1|2}}00000000000000 + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag-short.s -o short.o +# RUN: not ld.lld short.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR2 %s + +# ERR2: error: short.o:(.note.gnu.property+0x0): size of GNU_PROPERTY_AARCH64_FEATURE_PAUTH property must be 16 + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag-multiple.s -o multiple.o +# RUN: not ld.lld multiple.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR3 %s +# ERR3: error: multiple.o:(.note.gnu.property+0x0): multiple GNU_PROPERTY_AARCH64_FEATURE_PAUTH properties are not allowed + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu no-info.s -o noinfo1.o +# RUN: cp noinfo1.o noinfo2.o +# RUN: not ld.lld -z pauth-report=error tag11.o noinfo1.o noinfo2.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR4 %s +# RUN: ld.lld -z pauth-report=warning tag11.o noinfo1.o noinfo2.o -o /dev/null 2>&1 | FileCheck --check-prefix WARN %s +# RUN: ld.lld -z pauth-report=none tag11.o noinfo1.o noinfo2.o -o /dev/null 2>&1 | FileCheck --check-prefix NONE %s + +# ERR4: error: {{.*}}noinfo1.o has no AArch64 PAuth compatibility info while {{.*}}tag11.o has one; either all or no input files must have it +# ERR4-NEXT: error: {{.*}}noinfo2.o has no AArch64 PAuth compatibility info while {{.*}}tag11.o has one; either all or no input files must have it +# WARN: warning: {{.*}}noinfo1.o has no AArch64 PAuth compatibility info while {{.*}}tag11.o has one; either all or no input files must have it +# WARN-NEXT: warning: {{.*}}noinfo2.o has no AArch64 PAuth compatibility info while {{.*}}tag11.o has one; either all or no input files must have it +# NONE-NOT: {{.*}} has no AArch64 PAuth compatibility info while {{.*}} has one; either all or no input files must have it + +#--- abi-tag-short.s + +# Version is 4 bytes instead of 8 bytes, must emit an error + +.section ".note.gnu.property", "a" +.long 4 +.long 20 +.long 5 +.asciz "GNU" +.long 0xc0000001 +.long 12 +.quad 2 +.long 31 + +#--- abi-tag-multiple.s + +.section ".note.gnu.property", "a" +.long 4 +.long 48 +.long 5 +.asciz "GNU" +.long 0xc0000001 +.long 16 +.quad 42 // platform +.quad 1 // version +.long 0xc0000001 +.long 16 +.quad 42 // platform +.quad 1 // version + +#--- abi-tag1.s + +.section ".note.gnu.property", "a" +.long 4 +.long 24 +.long 5 +.asciz "GNU" +.long 0xc0000001 +.long 16 +.quad 42 // platform +.quad 1 // version + +#--- abi-tag2.s + +.section ".note.gnu.property", "a" +.long 4 +.long 24 +.long 5 +.asciz "GNU" +.long 0xc0000001 +.long 16 +.quad 42 // platform +.quad 2 // version + +#--- no-info.s + +.section ".test", "a" diff --git a/lld/test/ELF/aarch64-feature-pauth.s b/lld/test/ELF/aarch64-feature-pauth.s index 0520b2f28631..c7fe27b330ac 100644 --- a/lld/test/ELF/aarch64-feature-pauth.s +++ b/lld/test/ELF/aarch64-feature-pauth.s @@ -1,5 +1,11 @@ # REQUIRES: aarch64 +# XFAIL: * +# TODO: alternative PAuth ELF marking way +# To be implemented after the following PRs are merged: +# - https://github.com/ARM-software/abi-aa/pull/240 +# - https://github.com/llvm/llvm-project/pull/72714 + # RUN: rm -rf %t && split-file %s %t && cd %t # RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag1.s -o tag11.o From aa37665ff0d275d690ae7882f1e1306aa1e6b954 Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Mon, 12 Feb 2024 12:42:41 +0300 Subject: [PATCH 6/9] [AArch64][PAC][lldb][ELF][Dwarf] Support `GNU_PROPERTY_AARCH64_FEATURE_PAUTH` Apply signing schema for user expressions when debugging AArch64 ELFs depending on the `GNU_PROPERTY_AARCH64_FEATURE_PAUTH` property in `.note.gnu.property` section in the executable object file. To avoid linking against ObjectFileELF plugin everywhere, define `ParseGNUPropertyAArch64PAuthABI` as a virtual function in the base `ObjectFile` class. --- lldb/include/lldb/Symbol/ObjectFile.h | 10 ++ lldb/include/lldb/Target/LanguageRuntime.h | 2 + .../Clang/ClangExpressionParser.cpp | 43 +++++ .../Plugins/ObjectFile/ELF/ObjectFileELF.cpp | 62 +++++++ .../Plugins/ObjectFile/ELF/ObjectFileELF.h | 4 + .../ObjectFile/ELF/TestObjectFileELF.cpp | 155 ++++++++++++++++++ 6 files changed, 276 insertions(+) diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h index 6348d8103f85..2027cb3251de 100644 --- a/lldb/include/lldb/Symbol/ObjectFile.h +++ b/lldb/include/lldb/Symbol/ObjectFile.h @@ -23,6 +23,7 @@ #include "llvm/Support/Threading.h" #include "llvm/Support/VersionTuple.h" #include +#include namespace lldb_private { @@ -331,6 +332,15 @@ class ObjectFile : public std::enable_shared_from_this, /// virtual void RelocateSection(lldb_private::Section *section); + /// Parse ELF's AArch64 PAuth ABI from GNU property section. If it's missing + /// or the object file is not an ELF, return std::nullopt. Define this as a + /// virtual function in a base class since direct usage of ObjectFileELF in + /// ClangExpressionParser requires linking against the ObjectFileELF plugin. + virtual std::optional> + ParseGNUPropertyAArch64PAuthABI() { + return std::nullopt; + } + /// Appends a Symbol for the specified so_addr to the symbol table. /// /// If verify_unique is false, the symbol table is not searched to determine diff --git a/lldb/include/lldb/Target/LanguageRuntime.h b/lldb/include/lldb/Target/LanguageRuntime.h index eff79a0bf0d0..1c875d8c8745 100644 --- a/lldb/include/lldb/Target/LanguageRuntime.h +++ b/lldb/include/lldb/Target/LanguageRuntime.h @@ -226,6 +226,8 @@ class LanguageRuntime : public Runtime, public PluginInterface { } LanguageRuntime(Process *process); + + std::pair m_aarch64_pauth_abi_tag; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index f65192a3225e..7cb1655ab7fb 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -36,6 +36,7 @@ #include "clang/Sema/SemaConsumer.h" #include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Debug.h" @@ -445,6 +446,11 @@ ClangExpressionParser::ClangExpressionParser( // Supported subsets of x86 if (target_machine == llvm::Triple::x86 || target_machine == llvm::Triple::x86_64) { + // FIXME: shouldn't this be placed after + // `auto target_info = TargetInfo::CreateTargetInfo(...)` + // (see `if (target_machine == llvm::Triple::aarch64)`)? + // It computes `Features` from `FeatureMap` and `FeaturesAsWritten` and + // erases initial `Features` vector. m_compiler->getTargetOpts().Features.push_back("+sse"); m_compiler->getTargetOpts().Features.push_back("+sse2"); } @@ -467,6 +473,22 @@ ClangExpressionParser::ClangExpressionParser( auto target_info = TargetInfo::CreateTargetInfo( m_compiler->getDiagnostics(), m_compiler->getInvocation().TargetOpts); + + std::optional> elf_pauth_abi_tag; + if (target_machine == llvm::Triple::aarch64) { + do { + Module *module_sp = target_sp->GetExecutableModulePointer(); + if (module_sp == nullptr) + break; + ObjectFile *obj_file = module_sp->GetObjectFile(); + if (obj_file == nullptr) + break; + elf_pauth_abi_tag = obj_file->ParseGNUPropertyAArch64PAuthABI(); + if (elf_pauth_abi_tag != std::nullopt) + target_info->getTargetOpts().Features.push_back("+pauth"); + } while (false); + } + if (log) { LLDB_LOGF(log, "Target datalayout string: '%s'", target_info->getDataLayoutString()); @@ -482,6 +504,23 @@ ClangExpressionParser::ClangExpressionParser( lldb::LanguageType language = expr.Language(); LangOptions &lang_opts = m_compiler->getLangOpts(); + if (elf_pauth_abi_tag != std::nullopt) { + uint64_t elf_pauth_abi_platform = elf_pauth_abi_tag->first; + // TODO: store this magic constant corresponding to Linux platform in some + // header + if (elf_pauth_abi_platform == + llvm::ELF::GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_LINUX) { + uint64_t elf_pauth_abi_version = elf_pauth_abi_tag->second; + lang_opts.PointerAuthCalls = elf_pauth_abi_version & (1 << 0); + lang_opts.PointerAuthReturns = elf_pauth_abi_version & (1 << 1); + lang_opts.PointerAuthVTPtrAddressDiscrimination = + elf_pauth_abi_version & (1 << 2); + lang_opts.PointerAuthVTPtrTypeDiscrimination = + elf_pauth_abi_version & (1 << 3); + lang_opts.PointerAuthInitFini = elf_pauth_abi_version & (1 << 4); + } + } + switch (language) { case lldb::eLanguageTypeC: case lldb::eLanguageTypeC89: @@ -622,6 +661,10 @@ ClangExpressionParser::ClangExpressionParser( else m_compiler->getCodeGenOpts().setDebugInfo(codegenoptions::NoDebugInfo); + CompilerInvocation::setDefaultPointerAuthOptions( + m_compiler->getCodeGenOpts().PointerAuth, lang_opts, + target_arch.GetTriple()); + // Disable some warnings. SetupDefaultClangDiagnostics(*m_compiler); diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index 2da971dff895..bff0940a6027 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -1284,6 +1284,68 @@ ObjectFileELF::RefineModuleDetailsFromNote(lldb_private::DataExtractor &data, return error; } +std::optional> +ObjectFileELF::ParseGNUPropertyAArch64PAuthABI() { + // TODO: store parsing results in some kind of cache to avoid recurrent + // parsing on multiple calls + assert(m_arch_spec.GetMachine() == llvm::Triple::aarch64); + + SectionList *SL = GetSectionList(); + if (SL == nullptr) + return std::nullopt; + + lldb::SectionSP GNUPropSecSP = + SL->FindSectionByName(ConstString(".note.gnu.property")); + if (GNUPropSecSP == nullptr) + return std::nullopt; + + DataExtractor Data; + lldb::offset_t Length = GNUPropSecSP->GetSectionData(Data); + if (Length < 16) + return std::nullopt; + + lldb::offset_t Offset = 0; + uint32_t NameSz = Data.GetU32(&Offset); + if (NameSz != 4) + return std::nullopt; + + uint32_t ContentSz = Data.GetU32(&Offset); + if (ContentSz + 16 != Length) // the section is ill-formed + return std::nullopt; + uint32_t SectionType = Data.GetU32(&Offset); + if (SectionType != NT_GNU_PROPERTY_TYPE_0) + return std::nullopt; + + llvm::StringRef Name = Data.GetCStr(&Offset, NameSz); + if (Name != "GNU") + return std::nullopt; + + while (Offset < Length) { + lldb::offset_t OldOffset = Offset; + + uint32_t FeatureType = Data.GetU32(&Offset); + uint32_t Size = Data.GetU32(&Offset); + if (OldOffset + 8 != Offset) // there were not enough bytes + return std::nullopt; + if (FeatureType != GNU_PROPERTY_AARCH64_FEATURE_PAUTH) { + Offset += Size; + Offset = + llvm::alignTo(Offset, (uint64_t(1) << GNUPropSecSP->GetLog2Align())); + continue; + } + if (Size != 16) + return std::nullopt; + + uint64_t PAuthABIPlatform = Data.GetU64(&Offset); + uint64_t PAuthABIVersion = Data.GetU64(&Offset); + if (OldOffset + 24 != Offset) // there were not enough bytes + return std::nullopt; + return std::make_pair(PAuthABIPlatform, PAuthABIVersion); + } + + return std::nullopt; +} + void ObjectFileELF::ParseARMAttributes(DataExtractor &data, uint64_t length, ArchSpec &arch_spec) { lldb::offset_t Offset = 0; diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h index bc8e34981a9d..d258977d96ea 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h @@ -12,6 +12,7 @@ #include #include +#include #include #include "lldb/Symbol/ObjectFile.h" @@ -155,6 +156,9 @@ class ObjectFileELF : public lldb_private::ObjectFile { void RelocateSection(lldb_private::Section *section) override; + std::optional> + ParseGNUPropertyAArch64PAuthABI() override; + protected: std::vector diff --git a/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp b/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp index 80abc5b80f84..b234f68b4227 100644 --- a/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp +++ b/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp @@ -117,6 +117,161 @@ TEST_F(ObjectFileELFTest, SectionsResolveConsistently) { EXPECT_EQ(text_sp, start->GetAddress().GetSection()); } +TEST_F(ObjectFileELFTest, GNUPropertyAArch64PAuthABI) { + // Successful parsing + { + llvm::StringRef SinglePropertyYaml = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_AARCH64 +Sections: + - Name: .note.gnu.property + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + AddressAlign: 0x8 + Notes: + - Name: GNU + Desc: 010000C01000000002000000000000001F00000000000000 + # ^^^^^^^^ + # type = 0xC0000001 = GNU_PROPERTY_AARCH64_FEATURE_PAUTH + # ^^^^^^^^ + # size = 0x00000010 = 16 + # ^^^^^^^^^^^^^^^^ + # platform = 0x0000000000000002 = 2 + # ^^^^^^^^^^^^^^^^ + # version = 0x000000000000001F = 31 + Type: NT_GNU_PROPERTY_TYPE_0 +... +)"; + llvm::StringRef MultiplePropertiesYaml = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_AARCH64 +Sections: + - Name: .note.gnu.property + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + AddressAlign: 0x8 + Notes: + - Name: GNU + Desc: 42424242040000001234567800000000010000C01000000002000000000000001F00000000000000 + # ^^^^^^^^ + # dummy property type = 0x42424242 + # ^^^^^^^^ + # dummy property size = 0x00000008 = 4 + # ^^^^^^^^________ + # dummy property contents = 0x78563412 and padding + # ^^^^^^^^ + # type = 0xC0000001 = GNU_PROPERTY_AARCH64_FEATURE_PAUTH + # ^^^^^^^^ + # size = 0x00000010 = 16 + # ^^^^^^^^^^^^^^^^ + # platform = 0x0000000000000002 = 2 + # ^^^^^^^^^^^^^^^^ + # version = 0x000000000000001F = 31 + Type: NT_GNU_PROPERTY_TYPE_0 +... +)"; + std::array, 2> TestFiles = { + TestFile::fromYaml(SinglePropertyYaml), + TestFile::fromYaml(MultiplePropertiesYaml)}; + + for (auto &TF : TestFiles) { + ASSERT_THAT_EXPECTED(TF, llvm::Succeeded()); + + auto module_sp = std::make_shared(TF->moduleSpec()); + ObjectFile *obj_file = module_sp->GetObjectFile(); + ASSERT_NE(nullptr, obj_file); + + std::optional> pauthabi = + obj_file->ParseGNUPropertyAArch64PAuthABI(); + ASSERT_NE(std::nullopt, pauthabi); + ASSERT_EQ(uint64_t(2), pauthabi->first); + ASSERT_EQ(uint64_t(31), pauthabi->second); + } + } + + // Error during parsing + { + llvm::StringRef InvalidNameSizeYaml = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_AARCH64 +Sections: + - Name: .note.gnu.property + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + AddressAlign: 0x8 + Notes: + - Name: XXXXX + Desc: 010000C01000000002000000000000001F00000000000000 + Type: NT_GNU_PROPERTY_TYPE_0 +... +)"; + llvm::StringRef InvalidNameYaml = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_AARCH64 +Sections: + - Name: .note.gnu.property + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + AddressAlign: 0x8 + Notes: + - Name: XXX + Desc: 010000C01000000002000000000000001F00000000000000 + Type: NT_GNU_PROPERTY_TYPE_0 +... +)"; + llvm::StringRef InvalidPropertySizeYaml = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_AARCH64 +Sections: + - Name: .note.gnu.property + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + AddressAlign: 0x8 + Notes: + - Name: XXXXX + Desc: 0000000000000000 + Type: NT_GNU_PROPERTY_TYPE_0 +... +)"; + std::array, 3> TestFiles = { + TestFile::fromYaml(InvalidNameSizeYaml), + TestFile::fromYaml(InvalidNameYaml), + TestFile::fromYaml(InvalidPropertySizeYaml)}; + + for (auto &TF : TestFiles) { + ASSERT_THAT_EXPECTED(TF, llvm::Succeeded()); + + auto module_sp = std::make_shared(TF->moduleSpec()); + ObjectFile *obj_file = module_sp->GetObjectFile(); + ASSERT_NE(nullptr, obj_file); + + std::optional> pauthabi = + obj_file->ParseGNUPropertyAArch64PAuthABI(); + ASSERT_EQ(std::nullopt, pauthabi); + } + } +} + // Test that GetModuleSpecifications works on an "atypical" object file which // has section headers right after the ELF header (instead of the more common // layout where the section headers are at the very end of the object file). From 8e62fa862afe823d56ae59380ff6c180b11f550e Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Mon, 12 Feb 2024 13:02:54 +0300 Subject: [PATCH 7/9] [AArch64][PAC][lldb][Dwarf] Support `__ptrauth`-qualified types This adds support for `DW_TAG_LLVM_ptrauth_type` entries corresponding to explicitly signed free function pointers in lldb user expressions. In this patch, as a temporary solution pointer auth schema corresponding to `-mbranch-protection=pauthabi` is enabled unconditionally. This also brings support for all kinds of implicitly signed pointers, including member function pointers, virtual function pointers, etc. --- lldb/include/lldb/Symbol/CompilerType.h | 13 ++ lldb/include/lldb/Symbol/Type.h | 4 +- lldb/include/lldb/Symbol/TypeSystem.h | 17 +++ .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 31 ++++ .../SymbolFile/DWARF/DWARFASTParserClang.h | 4 + .../TypeSystem/Clang/TypeSystemClang.cpp | 47 ++++++ .../TypeSystem/Clang/TypeSystemClang.h | 8 ++ lldb/source/Symbol/CompilerType.cpp | 32 +++++ lldb/source/Symbol/Type.cpp | 18 ++- lldb/source/Symbol/TypeSystem.cpp | 7 + .../DWARF/DWARFASTParserClangTests.cpp | 134 ++++++++++++++++++ 11 files changed, 308 insertions(+), 7 deletions(-) diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h index 2d7092d2c93f..4e5b4b76d459 100644 --- a/lldb/include/lldb/Symbol/CompilerType.h +++ b/lldb/include/lldb/Symbol/CompilerType.h @@ -215,6 +215,12 @@ class CompilerType { size_t GetPointerByteSize() const; /// \} + unsigned GetPtrAuthKey() const; + + unsigned GetPtrAuthDiscriminator() const; + + bool GetPtrAuthAddressDiversity() const; + /// Accessors. /// \{ @@ -322,6 +328,13 @@ class CompilerType { /// Create related types using the current type's AST CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) const; + + /// Return a new CompilerType adds a ptrauth modifier with given parameters to + /// this type if this type is valid and the type system supports ptrauth + /// modifiers, else return an invalid type. Note that this does not check if + /// this type is a pointer. + CompilerType AddPtrAuthModifier(unsigned key, bool isAddressDiscriminated, + unsigned extraDiscriminator) const; /// \} /// Exploring the type. diff --git a/lldb/include/lldb/Symbol/Type.h b/lldb/include/lldb/Symbol/Type.h index 046501931d21..5dcd98387c90 100644 --- a/lldb/include/lldb/Symbol/Type.h +++ b/lldb/include/lldb/Symbol/Type.h @@ -92,7 +92,9 @@ class Type : public std::enable_shared_from_this, public UserID { /// This type is the type whose UID is m_encoding_uid as an atomic type. eEncodingIsAtomicUID, /// This type is the synthetic type whose UID is m_encoding_uid. - eEncodingIsSyntheticUID + eEncodingIsSyntheticUID, + /// This type is a signed pointer. + eEncodingIsLLVMPtrAuthUID }; enum class ResolveState : unsigned char { diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index eb6e453e1aec..52313c86a558 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -215,6 +215,16 @@ class TypeSystem : public PluginInterface, virtual uint32_t GetPointerByteSize() = 0; + // TODO: are we allowed to insert virtual functions in the middle of the class + // interface and break ABI? + virtual unsigned GetPtrAuthKey(lldb::opaque_compiler_type_t type) = 0; + + virtual unsigned + GetPtrAuthDiscriminator(lldb::opaque_compiler_type_t type) = 0; + + virtual bool + GetPtrAuthAddressDiversity(lldb::opaque_compiler_type_t type) = 0; + // Accessors virtual ConstString GetTypeName(lldb::opaque_compiler_type_t type, @@ -279,6 +289,13 @@ class TypeSystem : public PluginInterface, virtual CompilerType AddRestrictModifier(lldb::opaque_compiler_type_t type); + // TODO: are we allowed to insert virtual functions in the middle of the class + // interface and break ABI? + virtual CompilerType AddPtrAuthModifier(lldb::opaque_compiler_type_t type, + unsigned key, + bool isAddressDiscriminated, + unsigned extraDiscriminator); + /// \param opaque_payload The m_payload field of Type, which may /// carry TypeSystem-specific extra information. virtual CompilerType CreateTypedef(lldb::opaque_compiler_type_t type, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 37fb16d4e035..21fdeff1a27e 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -504,6 +504,10 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, type_sp = ParsePointerToMemberType(die, attrs); break; } + case DW_TAG_LLVM_ptrauth_type: { + type_sp = ParsePtrAuthQualifiedType(die, attrs); + break; + } default: dwarf->GetObjectFile()->GetModule()->ReportError( "[{0:x16}]: unhandled type tag {1:x4} ({2}), " @@ -1375,6 +1379,33 @@ TypeSP DWARFASTParserClang::ParsePointerToMemberType( return nullptr; } +TypeSP DWARFASTParserClang::ParsePtrAuthQualifiedType( + const DWARFDIE &die, const ParsedDWARFTypeAttributes &attrs) { + SymbolFileDWARF *dwarf = die.GetDWARF(); + Type *pointer_type = dwarf->ResolveTypeUID(attrs.type.Reference(), true); + + if (pointer_type == nullptr) + return nullptr; + + CompilerType pointer_clang_type = pointer_type->GetForwardCompilerType(); + + unsigned key = die.GetAttributeValueAsUnsigned(DW_AT_LLVM_ptrauth_key, 0); + bool has_addr_discr = die.GetAttributeValueAsUnsigned( + DW_AT_LLVM_ptrauth_address_discriminated, false); + unsigned extra_discr = die.GetAttributeValueAsUnsigned( + DW_AT_LLVM_ptrauth_extra_discriminator, 0); + CompilerType clang_type = m_ast.AddPtrAuthModifier( + pointer_clang_type.GetOpaqueQualType(), key, has_addr_discr, extra_discr); + + TypeSP type_sp = dwarf->MakeType( + die.GetID(), attrs.name, pointer_type->GetByteSize(nullptr), nullptr, + attrs.type.Reference().GetID(), Type::eEncodingIsLLVMPtrAuthUID, + &attrs.decl, clang_type, Type::ResolveState::Forward); + + dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); + return type_sp; +} + void DWARFASTParserClang::ParseInheritance( const DWARFDIE &die, const DWARFDIE &parent_die, const CompilerType class_clang_type, const AccessType default_accessibility, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h index 88bfc490e890..15f8d44c2434 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -296,6 +296,10 @@ class DWARFASTParserClang : public DWARFASTParser { lldb::TypeSP ParsePointerToMemberType(const DWARFDIE &die, const ParsedDWARFTypeAttributes &attrs); + lldb::TypeSP + ParsePtrAuthQualifiedType(const DWARFDIE &die, + const ParsedDWARFTypeAttributes &attrs); + /// Parses a DW_TAG_inheritance DIE into a base/super class. /// /// \param die The DW_TAG_inheritance DIE to parse. diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 69cff0f35ae4..df863c9920a8 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -3048,6 +3048,35 @@ bool TypeSystemClang::IsCStringType(lldb::opaque_compiler_type_t type, return false; } +unsigned TypeSystemClang::GetPtrAuthKey(lldb::opaque_compiler_type_t type) { + if (type) { + clang::QualType qual_type(GetCanonicalQualType(type)); + if (auto pointer_auth = qual_type.getPointerAuth()) + return pointer_auth.getKey(); + } + return 0; +} + +unsigned +TypeSystemClang::GetPtrAuthDiscriminator(lldb::opaque_compiler_type_t type) { + if (type) { + clang::QualType qual_type(GetCanonicalQualType(type)); + if (auto pointer_auth = qual_type.getPointerAuth()) + return pointer_auth.getExtraDiscriminator(); + } + return 0; +} + +bool TypeSystemClang::GetPtrAuthAddressDiversity( + lldb::opaque_compiler_type_t type) { + if (type) { + clang::QualType qual_type(GetCanonicalQualType(type)); + if (auto pointer_auth = qual_type.getPointerAuth()) + return pointer_auth.isAddressDiscriminated(); + } + return false; +} + bool TypeSystemClang::IsFunctionType(lldb::opaque_compiler_type_t type) { auto isFunctionType = [&](clang::QualType qual_type) { return qual_type->isFunctionType(); @@ -4622,6 +4651,24 @@ TypeSystemClang::AddConstModifier(lldb::opaque_compiler_type_t type) { return CompilerType(); } +CompilerType +TypeSystemClang::AddPtrAuthModifier(lldb::opaque_compiler_type_t type, + unsigned key, bool isAddressDiscriminated, + unsigned extraDiscriminator) { + if (type) { + clang::ASTContext &clang_ast = getASTContext(); + auto pauth = PointerAuthQualifier::Create( + key, isAddressDiscriminated, extraDiscriminator, + PointerAuthenticationMode::SignAndAuth, + /* isIsaPointer */ false, + /* authenticatesNullValues */ false); + clang::QualType result = + clang_ast.getPointerAuthType(GetQualType(type), pauth); + return GetType(result); + } + return CompilerType(); +} + CompilerType TypeSystemClang::AddVolatileModifier(lldb::opaque_compiler_type_t type) { if (type) { diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 0544de3cd33b..e9f41ff880bc 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -642,6 +642,10 @@ class TypeSystemClang : public TypeSystem { bool IsFloatingPointType(lldb::opaque_compiler_type_t type, uint32_t &count, bool &is_complex) override; + unsigned GetPtrAuthKey(lldb::opaque_compiler_type_t type) override; + unsigned GetPtrAuthDiscriminator(lldb::opaque_compiler_type_t type) override; + bool GetPtrAuthAddressDiversity(lldb::opaque_compiler_type_t type) override; + bool IsFunctionType(lldb::opaque_compiler_type_t type) override; uint32_t IsHomogeneousAggregate(lldb::opaque_compiler_type_t type, @@ -782,6 +786,10 @@ class TypeSystemClang : public TypeSystem { CompilerType AddConstModifier(lldb::opaque_compiler_type_t type) override; + CompilerType AddPtrAuthModifier(lldb::opaque_compiler_type_t type, + unsigned key, bool isAddressDiscriminated, + unsigned extraDiscriminator) override; + CompilerType AddVolatileModifier(lldb::opaque_compiler_type_t type) override; CompilerType AddRestrictModifier(lldb::opaque_compiler_type_t type) override; diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp index 33f7e63d9be4..5cd33a9ed4c1 100644 --- a/lldb/source/Symbol/CompilerType.cpp +++ b/lldb/source/Symbol/CompilerType.cpp @@ -108,6 +108,27 @@ bool CompilerType::IsConst() const { return false; } +unsigned CompilerType::GetPtrAuthKey() const { + if (IsValid()) + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->GetPtrAuthKey(m_type); + return 0; +} + +unsigned CompilerType::GetPtrAuthDiscriminator() const { + if (IsValid()) + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->GetPtrAuthDiscriminator(m_type); + return 0; +} + +bool CompilerType::GetPtrAuthAddressDiversity() const { + if (IsValid()) + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->GetPtrAuthAddressDiversity(m_type); + return false; +} + bool CompilerType::IsCStringType(uint32_t &length) const { if (IsValid()) if (auto type_system_sp = GetTypeSystem()) @@ -485,6 +506,17 @@ CompilerType CompilerType::GetPointerType() const { return CompilerType(); } +CompilerType +CompilerType::AddPtrAuthModifier(unsigned key, bool isAddressDiscriminated, + unsigned extraDiscriminator) const { + if (IsValid()) { + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->AddPtrAuthModifier( + m_type, key, isAddressDiscriminated, extraDiscriminator); + } + return CompilerType(); +} + CompilerType CompilerType::GetLValueReferenceType() const { if (IsValid()) if (auto type_system_sp = GetTypeSystem()) diff --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp index 66284eb73cad..e27642a84157 100644 --- a/lldb/source/Symbol/Type.cpp +++ b/lldb/source/Symbol/Type.cpp @@ -230,6 +230,9 @@ void Type::GetDescription(Stream *s, lldb::DescriptionLevel level, case eEncodingIsSyntheticUID: s->PutCString(" (synthetic type)"); break; + case eEncodingIsLLVMPtrAuthUID: + s->PutCString(" (ptrauth type)"); + break; } } } @@ -291,6 +294,8 @@ void Type::Dump(Stream *s, bool show_context, lldb::DescriptionLevel level) { case eEncodingIsSyntheticUID: s->PutCString(" (synthetic type)"); break; + case eEncodingIsLLVMPtrAuthUID: + s->PutCString(" (ptrauth type)"); } } @@ -376,12 +381,13 @@ std::optional Type::GetByteSize(ExecutionContextScope *exe_scope) { // If we are a pointer or reference, then this is just a pointer size; case eEncodingIsPointerUID: case eEncodingIsLValueReferenceUID: - case eEncodingIsRValueReferenceUID: { - if (ArchSpec arch = m_symbol_file->GetObjectFile()->GetArchitecture()) { - m_byte_size = arch.GetAddressByteSize(); - m_byte_size_has_value = true; - return static_cast(m_byte_size); - } + case eEncodingIsRValueReferenceUID: + case eEncodingIsLLVMPtrAuthUID: { + if (ArchSpec arch = m_symbol_file->GetObjectFile()->GetArchitecture()) { + m_byte_size = arch.GetAddressByteSize(); + m_byte_size_has_value = true; + return static_cast(m_byte_size); + } } break; } return {}; diff --git a/lldb/source/Symbol/TypeSystem.cpp b/lldb/source/Symbol/TypeSystem.cpp index 24f202930565..47949518f7c6 100644 --- a/lldb/source/Symbol/TypeSystem.cpp +++ b/lldb/source/Symbol/TypeSystem.cpp @@ -93,6 +93,13 @@ CompilerType TypeSystem::AddConstModifier(lldb::opaque_compiler_type_t type) { return CompilerType(); } +CompilerType TypeSystem::AddPtrAuthModifier(lldb::opaque_compiler_type_t type, + unsigned key, + bool isAddressDiscriminated, + unsigned extraDiscriminator) { + return CompilerType(); +} + CompilerType TypeSystem::AddVolatileModifier(lldb::opaque_compiler_type_t type) { return CompilerType(); diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp index 20a085741f73..5d9d386430d4 100644 --- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp +++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp @@ -286,6 +286,140 @@ TEST_F(DWARFASTParserClangTests, TestCallingConventionParsing) { ASSERT_EQ(found_function_types, expected_function_types); } +TEST_F(DWARFASTParserClangTests, TestPtrAuthParsing) { + // Tests parsing values with type DW_TAG_LLVM_ptrauth_type corresponding to + // explicitly signed raw function pointers + + // This is Dwarf for the following C code: + // ``` + // void (*__ptrauth(0, 0, 42) a)(); + // ``` + + const char *yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_AARCH64 +DWARF: + debug_str: + - a + debug_abbrev: + - ID: 0 + Table: + - Code: 0x01 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Code: 0x02 + Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_external + Form: DW_FORM_flag_present + - Code: 0x03 + Tag: DW_TAG_LLVM_ptrauth_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_LLVM_ptrauth_key + Form: DW_FORM_data1 + - Attribute: DW_AT_LLVM_ptrauth_extra_discriminator + Form: DW_FORM_data2 + - Code: 0x04 + Tag: DW_TAG_pointer_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Code: 0x05 + Tag: DW_TAG_subroutine_type + Children: DW_CHILDREN_yes + - Code: 0x06 + Tag: DW_TAG_unspecified_parameters + Children: DW_CHILDREN_no + + debug_info: + - Version: 5 + UnitType: DW_UT_compile + AddrSize: 8 + Entries: +# 0x0c: DW_TAG_compile_unit +# DW_AT_language [DW_FORM_data2] (DW_LANG_C99) + - AbbrCode: 0x01 + Values: + - Value: 0x0c + +# 0x0f: DW_TAG_variable +# DW_AT_name [DW_FORM_strp] (\"a\") +# DW_AT_type [DW_FORM_ref4] (0x00000018 \"void (*__ptrauth(0, 0, 0x02a)\") +# DW_AT_external [DW_FORM_flag_present] (true) + - AbbrCode: 0x02 + Values: + - Value: 0x00 + - Value: 0x18 + +# 0x18: DW_TAG_LLVM_ptrauth_type +# DW_AT_type [DW_FORM_ref4] (0x00000020 \"void (*)(...)\") +# DW_AT_LLVM_ptrauth_key [DW_FORM_data1] (0x00) +# DW_AT_LLVM_ptrauth_extra_discriminator [DW_FORM_data2] (0x002a) + - AbbrCode: 0x03 + Values: + - Value: 0x20 + - Value: 0x00 + - Value: 0x2a + +# 0x20: DW_TAG_pointer_type +# DW_AT_type [DW_AT_type [DW_FORM_ref4] (0x00000025 \"void (...)\") + - AbbrCode: 0x04 + Values: + - Value: 0x25 + +# 0x25: DW_TAG_subroutine_type + - AbbrCode: 0x05 + +# 0x26: DW_TAG_unspecified_parameters + - AbbrCode: 0x06 + + - AbbrCode: 0x00 # end of child tags of 0x25 + - AbbrCode: 0x00 # end of child tags of 0x0c +... +)"; + YAMLModuleTester t(yamldata); + + DWARFUnit *unit = t.GetDwarfUnit(); + ASSERT_NE(unit, nullptr); + const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE(); + ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit); + DWARFDIE cu_die(unit, cu_entry); + + auto holder = std::make_unique("ast"); + auto &ast_ctx = *holder->GetAST(); + DWARFASTParserClangStub ast_parser(ast_ctx); + + DWARFDIE ptrauth_variable = cu_die.GetFirstChild(); + ASSERT_EQ(ptrauth_variable.Tag(), DW_TAG_variable); + DWARFDIE ptrauth_type = + ptrauth_variable.GetAttributeValueAsReferenceDIE(DW_AT_type); + ASSERT_EQ(ptrauth_type.Tag(), DW_TAG_LLVM_ptrauth_type); + + SymbolContext sc; + bool new_type = false; + lldb::TypeSP type = + ast_parser.ParseTypeFromDWARF(sc, ptrauth_type, &new_type); + std::string type_as_string = + type->GetForwardCompilerType().GetTypeName().AsCString(); + ASSERT_EQ(type_as_string, "void (*__ptrauth(0,0,42))(...)"); +} + struct ExtractIntFromFormValueTest : public testing::Test { SubsystemRAII subsystems; clang_utils::TypeSystemClangHolder holder; From 8ed84bc100cf28a55f39e5cfe5affc1e223896bb Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Thu, 1 Feb 2024 03:12:38 +0300 Subject: [PATCH 8/9] [AArch64][PAC][lldb][Dwarf] Support `[[clang::ptrauth_vtable_pointer(...)]]` attribute - Encode the attribute in Dwarf as a `DW_TAG_LLVM_ptrauth_type` entry corresponding to vtable pointer. The attribute supports "default" values for address and extra discrimination, but in Dwarf we emit actual values due to `DW_AT_LLVM_ptrauth_*` attributes limitations. See lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp for details. - When parsing dynamic class and structure types from Dwarf, check the vtable pointer type against `DW_TAG_LLVM_ptrauth_type`. If true, apply the `clang::ptrauth_vtable_pointer` attribute with corresponding parameters to the class record decl. "Default" address and extra discrimination values are not restored, the actual ones are, but the result is effectively the same since the signing schema must be preserved unchanged. --- clang/lib/CodeGen/CGDebugInfo.cpp | 41 +++ .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 42 +++ .../DWARF/DWARFASTParserClangTests.cpp | 275 ++++++++++++++++++ 3 files changed, 358 insertions(+) diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 44757b8ebb05..d030167cf55f 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -2432,6 +2432,47 @@ void CGDebugInfo::CollectVTableInfo(const CXXRecordDecl *RD, llvm::DIFile *Unit, if (!VPtrTy) VPtrTy = getOrCreateVTablePtrType(Unit); + if (auto *VptrAuthAttr = RD->getAttr()) { + unsigned TypedDiscriminator = + CGM.getContext().getPointerAuthVTablePointerDiscriminator(RD); + auto &LangOpts = CGM.getContext().getLangOpts(); + + unsigned Key = VptrAuthAttr->getKey(); + + bool HasAddressDiscrimination = + LangOpts.PointerAuthVTPtrAddressDiscrimination; + if (VptrAuthAttr->getAddressDiscrimination() != + VTablePointerAuthenticationAttr::DefaultAddressDiscrimination) { + HasAddressDiscrimination = + (VptrAuthAttr->getAddressDiscrimination() == + VTablePointerAuthenticationAttr::AddressDiscrimination); + } + + unsigned ExtraDiscriminator = 0; + + switch (VptrAuthAttr->getExtraDiscrimination()) { + case VTablePointerAuthenticationAttr::DefaultExtraDiscrimination: + if (LangOpts.PointerAuthVTPtrTypeDiscrimination) + ExtraDiscriminator = TypedDiscriminator; + break; + case VTablePointerAuthenticationAttr::TypeDiscrimination: + ExtraDiscriminator = TypedDiscriminator; + break; + case VTablePointerAuthenticationAttr::CustomDiscrimination: + ExtraDiscriminator = VptrAuthAttr->getCustomDiscriminationValue(); + break; + case VTablePointerAuthenticationAttr::NoExtraDiscrimination: + break; + } + + // See CodeGenModule::computeVTPointerAuthentication: + // isIsaPointer and authenticatesNullValues are always false. + VPtrTy = DBuilder.createPtrAuthQualifiedType( + VPtrTy, Key, HasAddressDiscrimination, ExtraDiscriminator, + /* isIsaPointer */ false, + /* authenticatesNullValues */ false); + } + unsigned Size = CGM.getContext().getTypeSize(CGM.getContext().VoidPtrTy); llvm::DIType *VPtrMember = DBuilder.createMemberType(Unit, getVTableName(RD), Unit, 0, Size, 0, 0, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 21fdeff1a27e..c5b1abff8eb1 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -1842,6 +1842,48 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, attrs.name.GetCString(), tag_decl_kind, attrs.class_language, &metadata, attrs.exports_symbols); } + + if (metadata.GetIsDynamicCXXType()) { + clang::RecordDecl *record_decl = m_ast.GetAsRecordDecl(clang_type); + DWARFDIE vptr_type_die = + die.GetFirstChild().GetAttributeValueAsReferenceDIE(DW_AT_type); + if (vptr_type_die.Tag() == DW_TAG_LLVM_ptrauth_type) { + unsigned key = vptr_type_die.GetAttributeValueAsUnsigned( + DW_AT_LLVM_ptrauth_key, -1); + unsigned discriminator = vptr_type_die.GetAttributeValueAsUnsigned( + DW_AT_LLVM_ptrauth_extra_discriminator, -1); + unsigned has_addr_discr = vptr_type_die.GetAttributeValueAsUnsigned( + DW_AT_LLVM_ptrauth_address_discriminated, -1); + + auto error_missing = [&vptr_type_die](const dw_attr_t attr) { + vptr_type_die.GetDWARF()->GetObjectFile()->GetModule()->ReportError( + "[{0:x16}]: missing attribute {1:x4} ({2}) required for signed " + "vtable pointer", + vptr_type_die.GetOffset(), attr, DW_AT_value_to_name(attr)); + }; + + if (key == unsigned(-1)) + error_missing(DW_AT_LLVM_ptrauth_key); + if (discriminator == unsigned(-1)) + error_missing(DW_AT_LLVM_ptrauth_extra_discriminator); + if (has_addr_discr == unsigned(-1)) + error_missing(DW_AT_LLVM_ptrauth_extra_discriminator); + + record_decl->addAttr( + clang::VTablePointerAuthenticationAttr::CreateImplicit( + m_ast.getASTContext(), + key == 2 + ? clang::VTablePointerAuthenticationAttr::ProcessDependent + : clang::VTablePointerAuthenticationAttr:: + ProcessIndependent, + has_addr_discr ? clang::VTablePointerAuthenticationAttr:: + AddressDiscrimination + : clang::VTablePointerAuthenticationAttr:: + NoAddressDiscrimination, + clang::VTablePointerAuthenticationAttr::CustomDiscrimination, + discriminator)); + } + } } // Store a forward declaration to this class type in case any diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp index 5d9d386430d4..ec0f57276cf3 100644 --- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp +++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp @@ -420,6 +420,281 @@ TEST_F(DWARFASTParserClangTests, TestPtrAuthParsing) { ASSERT_EQ(type_as_string, "void (*__ptrauth(0,0,42))(...)"); } +TEST_F(DWARFASTParserClangTests, TestVTablePtrAuthParsing) { + // Tests parsing dynamic structure types with explicit vtable pointer + // authentication + + // This is Dwarf for the following C++ code: + // ``` + // struct [[clang::ptrauth_vtable_pointer(process_dependent, + // address_discrimination, + // custom_discrimination, 42)]] A { + // virtual void foo() {} + // }; + // A a; + // ``` + + const char *yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_AARCH64 +DWARF: + debug_str: + - a + - A + - _vptr$A + - foo + - __vtbl_ptr_type + - int + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Code: 0x2 + Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_external + Form: DW_FORM_flag_present + - Code: 0x3 + Tag: DW_TAG_structure_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_containing_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Code: 0x4 + Tag: DW_TAG_member + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_artificial + Form: DW_FORM_flag_present + - Code: 0x5 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_virtuality + Form: DW_FORM_data1 + - Attribute: DW_AT_containing_type + Form: DW_FORM_ref4 + - Code: 0x6 + Tag: DW_TAG_formal_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_artificial + Form: DW_FORM_flag_present + - Code: 0x7 + Tag: DW_TAG_LLVM_ptrauth_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_LLVM_ptrauth_key + Form: DW_FORM_data1 + - Attribute: DW_AT_LLVM_ptrauth_extra_discriminator + Form: DW_FORM_data2 + - Attribute: DW_AT_LLVM_ptrauth_address_discriminated + Form: DW_FORM_flag_present + - Code: 0x8 + Tag: DW_TAG_pointer_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Code: 0x9 + Tag: DW_TAG_pointer_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Code: 0xA + Tag: DW_TAG_subroutine_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Code: 0xB + Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_encoding + Form: DW_FORM_data1 + - Attribute: DW_AT_byte_size + Form: DW_FORM_data1 + - Code: 0xC + Tag: DW_TAG_pointer_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + + debug_info: + - Version: 5 + UnitType: DW_UT_compile + AddrSize: 8 + Entries: +# 0x0c: DW_TAG_compile_unit +# DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus_11) + - AbbrCode: 0x1 + Values: + - Value: 0x1A + +# 0x0f: DW_TAG_variable +# DW_AT_name [DW_FORM_strp] (\"a\") +# DW_AT_type [DW_FORM_ref4] (0x00000018 \"A\") +# DW_AT_external [DW_FORM_flag_present] (true) + - AbbrCode: 0x2 + Values: + - Value: 0x00 + - Value: 0x18 + +# 0x18: DW_TAG_structure_type +# DW_AT_containing_type [DW_FORM_ref4] (0x00000018 \"A\") +# DW_AT_name [DW_FORM_strp] (\"A\") + - AbbrCode: 0x3 + Values: + - Value: 0x18 + - Value: 0x02 + +# 0x21: DW_TAG_member +# DW_AT_name [DW_FORM_strp] (\"_vptr$A\") +# DW_AT_type [DW_FORM_ref4] (0x0000002f) +# DW_AT_artificial [DW_FORM_flag_present] (true) + - AbbrCode: 0x4 + Values: + - Value: 0x04 + - Value: 0x3B + +# 0x2a: DW_TAG_subprogram +# DW_AT_name [DW_FORM_strp] (\"foo\") +# DW_AT_virtuality [DW_FORM_data1] (DW_VIRTUALITY_virtual) +# DW_AT_containing_type [DW_FORM_ref4] (0x00000018 \"A\") + - AbbrCode: 0x5 + Values: + - Value: 0x0C + - Value: 0x01 + - Value: 0x18 + +# 0x34: DW_TAG_formal_parameter +# DW_AT_type [DW_FORM_ref4] (0x0000005d \"A *\") +# DW_AT_artificial [DW_FORM_flag_present] (true) + - AbbrCode: 0x6 + Values: + - Value: 0x5D + + - AbbrCode: 0x0 # end of child tags of 0x2a + - AbbrCode: 0x0 # end of child tags of 0x18 + +# 0x3b: DW_TAG_LLVM_ptrauth_type +# DW_AT_type [DW_FORM_ref4] (0x00000043 \"int (**)()\") +# DW_AT_LLVM_ptrauth_key [DW_FORM_data1] (0x02) +# DW_AT_LLVM_ptrauth_extra_discriminator [DW_FORM_data2] (0x002a) +# DW_AT_LLVM_ptrauth_address_discriminated [DW_FORM_flag_present] (true) + - AbbrCode: 0x7 + Values: + - Value: 0x43 + - Value: 0x02 + - Value: 0x2A + +# 0x43: DW_TAG_pointer_type +# DW_AT_type [DW_FORM_ref4] (0x00000048 \"int (*)()\") + - AbbrCode: 0x8 + Values: + - Value: 0x48 + +# 0x48: DW_TAG_pointer_type +# DW_AT_type [DW_FORM_ref4] (0x00000051 \"int ()\") +# DW_AT_name [DW_FORM_strp] (\"__vtbl_ptr_type\") + - AbbrCode: 0x9 + Values: + - Value: 0x51 + - Value: 0x10 + +# 0x51: DW_TAG_subroutine_type +# DW_AT_type [DW_FORM_ref4] (0x00000056 \"int\") + - AbbrCode: 0xA + Values: + - Value: 0x56 + +# 0x56: DW_TAG_base_type +# DW_AT_name [DW_FORM_strp] (\"int\") +# DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed) +# DW_AT_byte_size [DW_FORM_data1] (0x04) + - AbbrCode: 0xB + Values: + - Value: 0x20 + - Value: 0x05 + - Value: 0x04 + +# 0x5d: DW_TAG_pointer_type +# DW_AT_type [DW_FORM_ref4] (0x00000018 \"A\") + - AbbrCode: 0xC + Values: + - Value: 0x18 + + - AbbrCode: 0x0 # end of child tags of 0x0c +... +)"; + YAMLModuleTester t(yamldata); + + DWARFUnit *unit = t.GetDwarfUnit(); + ASSERT_NE(unit, nullptr); + const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE(); + ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit); + DWARFDIE cu_die(unit, cu_entry); + + auto holder = std::make_unique("ast"); + auto &ast_ctx = *holder->GetAST(); + DWARFASTParserClangStub ast_parser(ast_ctx); + + DWARFDIE struct_object = cu_die.GetFirstChild(); + ASSERT_EQ(struct_object.Tag(), DW_TAG_variable); + DWARFDIE structure_type = + struct_object.GetAttributeValueAsReferenceDIE(DW_AT_type); + ASSERT_EQ(structure_type.Tag(), DW_TAG_structure_type); + + SymbolContext sc; + bool new_type = false; + lldb::TypeSP type = + ast_parser.ParseTypeFromDWARF(sc, structure_type, &new_type); + clang::RecordDecl *record_decl = + TypeSystemClang::GetAsRecordDecl(type->GetForwardCompilerType()); + auto *attr = record_decl->getAttr(); + ASSERT_NE(attr, nullptr); + ASSERT_EQ(attr->getKey(), + clang::VTablePointerAuthenticationAttr::ProcessDependent); + ASSERT_EQ(attr->getAddressDiscrimination(), + clang::VTablePointerAuthenticationAttr::AddressDiscrimination); + ASSERT_EQ(attr->getExtraDiscrimination(), + clang::VTablePointerAuthenticationAttr::CustomDiscrimination); + ASSERT_EQ(attr->getCustomDiscriminationValue(), 42); +} + struct ExtractIntFromFormValueTest : public testing::Test { SubsystemRAII subsystems; clang_utils::TypeSystemClangHolder holder; From 3a04a18d72b994910bf3cfba1490b116c798f1b2 Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Thu, 15 Feb 2024 16:38:36 +0300 Subject: [PATCH 9/9] [AArch64][PAC][lldb][Dwarf] Support `[[clang::ptrauth_struct(...)]]` attribute Use IR metadata annotations for storing `ptrauth_struct_key` and `ptrauth_struct_disc` values. Corresponding `DW_TAG_LLVM_annotation` tags are used in Dwarf. --- clang/lib/CodeGen/CGDebugInfo.cpp | 60 +++++++- clang/lib/CodeGen/CGDebugInfo.h | 3 + .../ptrauth-struct-attr-annotations.cpp | 32 ++++ .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 33 +++++ .../DWARF/DWARFASTParserClangTests.cpp | 137 ++++++++++++++++++ 5 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 clang/test/CodeGenCXX/ptrauth-struct-attr-annotations.cpp diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index d030167cf55f..a8a28998f498 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -2304,6 +2304,50 @@ llvm::DINodeArray CGDebugInfo::CollectBTFDeclTagAnnotations(const Decl *D) { return DBuilder.getOrCreateArray(Annotations); } +llvm::DINodeArray +CGDebugInfo::CollectPtrAuthStructAnnotations(const RecordDecl *RD) { + if (!RD->hasAttr()) + return nullptr; + + llvm::LLVMContext &Context = CGM.getLLVMContext(); + + auto [IsKeyValIndependent, Key] = + CGM.getContext().getPointerAuthStructKey(RD); + auto [IsDiscValIndependent, Disc] = + CGM.getContext().getPointerAuthStructDisc(RD); + + // TODO: handle value-dependent keys and discriminators. What does this even + // mean? Using template integer parameters as key/disc or expressions like + // `__builtin_ptrauth_struct_key(...)` result in specific constants, and does + // not make key/disc value dependent. + if (!IsKeyValIndependent || !IsDiscValIndependent) + return nullptr; + + // TODO: 2-bit key and 16-bit disc results in negative values of i2 and i16 + // types in IR for unsigned integers using all the given bit-width (for ex., + // key 3 is i8 -1, disc 65534 is i16 -2). Do we want to use wider types to + // prevent negation? + auto *KeyMD = llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(Context, llvm::APInt(2, Key, false))); + auto *DiscMD = llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(Context, llvm::APInt(16, Disc, false))); + + llvm::Metadata *Annotations[2]; + llvm::Metadata *OpsKey[2] = { + llvm::MDString::get(Context, StringRef("ptrauth_struct_key")), KeyMD}; + Annotations[0] = llvm::MDNode::get(CGM.getLLVMContext(), OpsKey); + llvm::Metadata *OpsDisc[2] = { + llvm::MDString::get(Context, StringRef("ptrauth_struct_disc")), DiscMD}; + Annotations[1] = llvm::MDNode::get(CGM.getLLVMContext(), OpsDisc); + + // TODO: is it OK to return DINodeArray instead of MDNodeArray from + // CollectPtrAuthStructAnnotations and CollectBTFDeclTagAnnotations? The + // elements are not actually DINodes, and trying to iterate over the returned + // DINodeArray results in an assertion failure. This does not seem to happen + // though - it looks like that there are no places where such loops are used. + return DBuilder.getOrCreateArray(Annotations); +} + llvm::DIType *CGDebugInfo::getOrCreateVTablePtrType(llvm::DIFile *Unit) { if (VTablePtrType) return VTablePtrType; @@ -3790,7 +3834,21 @@ llvm::DICompositeType *CGDebugInfo::CreateLimitedType(const RecordType *Ty) { dyn_cast(CXXRD->getDeclContext())); } - llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(D); + llvm::DINodeArray AnnotationsBTFTag = CollectBTFDeclTagAnnotations(D); + llvm::DINodeArray AnnotationsPAuthStruct = CollectPtrAuthStructAnnotations(D); + + llvm::SmallVector AnnotationsVec; + if (AnnotationsBTFTag.get() != nullptr) + for (const llvm::MDOperand &Op : AnnotationsBTFTag.get()->operands()) + AnnotationsVec.push_back(Op.get()); + if (AnnotationsPAuthStruct.get() != nullptr) + for (const llvm::MDOperand &Op : AnnotationsPAuthStruct.get()->operands()) + AnnotationsVec.push_back(Op.get()); + + llvm::DINodeArray Annotations = + AnnotationsVec.empty() ? nullptr + : DBuilder.getOrCreateArray(AnnotationsVec); + llvm::DICompositeType *RealDecl = DBuilder.createReplaceableCompositeType( getTagForRecord(RD), RDName, RDContext, DefUnit, Line, 0, Size, Align, Flags, Identifier, Annotations); diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index ae12485850ca..ffcede3a4f90 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -316,6 +316,9 @@ class CGDebugInfo { /// A helper function to collect debug info for btf_decl_tag annotations. llvm::DINodeArray CollectBTFDeclTagAnnotations(const Decl *D); + /// A helper function to collect debug info for ptrauth_struct attribute. + llvm::DINodeArray CollectPtrAuthStructAnnotations(const RecordDecl *RD); + llvm::DIType *createFieldType(StringRef name, QualType type, SourceLocation loc, AccessSpecifier AS, uint64_t offsetInBits, uint32_t AlignInBits, diff --git a/clang/test/CodeGenCXX/ptrauth-struct-attr-annotations.cpp b/clang/test/CodeGenCXX/ptrauth-struct-attr-annotations.cpp new file mode 100644 index 000000000000..3f75f1ea5729 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-struct-attr-annotations.cpp @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -triple aarch64 -emit-llvm -debug-info-kind=limited -o - %s | FileCheck %s + +#include + +struct __attribute__((ptrauth_struct(1, 42))) S0 {}; + +template +class [[clang::ptrauth_struct(k, 65534)]] S1 {}; + +union __attribute__((ptrauth_struct(__builtin_ptrauth_struct_key(S0) + 1, __builtin_ptrauth_struct_disc(S0) + 1))) S2 {}; + +S0 s0; +S1<3> s1; +S2 s2; + +// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "S1<3>", +// CHECK-SAME: annotations: ![[A1:.*]]) +// CHECK: ![[A1]] = !{![[A1K:.*]], ![[A1D:.*]]} +// CHECK: ![[A1K]] = !{!"ptrauth_struct_key", i2 -1} +// CHECK: ![[A1D]] = !{!"ptrauth_struct_disc", i16 -2} + +// CHECK: !DICompositeType(tag: DW_TAG_union_type, name: "S2", +// CHECK-SAME: annotations: ![[A2:.*]]) +// CHECK: ![[A2]] = !{![[A2K:.*]], ![[A2D:.*]]} +// CHECK: ![[A2K]] = !{!"ptrauth_struct_key", i2 -2} +// CHECK: ![[A2D]] = !{!"ptrauth_struct_disc", i16 43} + +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "S0", +// CHECK-SAME: annotations: ![[A0:.*]]) +// CHECK: ![[A0]] = !{![[A0K:.*]], ![[A0D:.*]]} +// CHECK: ![[A0K]] = !{!"ptrauth_struct_key", i2 1} +// CHECK: ![[A0D]] = !{!"ptrauth_struct_disc", i16 42} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index c5b1abff8eb1..5bee3618372f 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -1884,6 +1884,39 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, discriminator)); } } + + // TODO: check that each annotation does not appear more than once + std::optional pauth_key, pauth_disc; + for (DWARFDIE die_child = die.GetFirstChild(); die_child; + die_child = die_child.GetSibling()) { + if (die_child.Tag() != DW_TAG_LLVM_annotation) + continue; + if (die_child.GetAttributeValueAsString(DW_AT_name, nullptr) == + llvm::StringRef("ptrauth_struct_key")) + pauth_key = + die_child.GetAttributeValueAsOptionalUnsigned(DW_AT_const_value); + else if (die_child.GetAttributeValueAsString(DW_AT_name, nullptr) == + llvm::StringRef("ptrauth_struct_disc")) + pauth_disc = + die_child.GetAttributeValueAsOptionalUnsigned(DW_AT_const_value); + } + + // TODO: do we need some handling for one std::nullopt out of two? + if (pauth_key != std::nullopt && pauth_disc != std::nullopt) { + clang::ASTContext &ctx = m_ast.getASTContext(); + llvm::APInt key_int = llvm::APInt(ctx.getTypeSize(ctx.UnsignedIntTy), + pauth_key.value(), false); + auto *key = clang::IntegerLiteral::Create(ctx, key_int, ctx.UnsignedIntTy, + clang::SourceLocation()); + llvm::APInt disc_int = llvm::APInt(ctx.getTypeSize(ctx.UnsignedIntTy), + pauth_disc.value(), false); + auto *disc = clang::IntegerLiteral::Create( + ctx, disc_int, ctx.UnsignedIntTy, clang::SourceLocation()); + + clang::RecordDecl *record_decl = m_ast.GetAsRecordDecl(clang_type); + record_decl->addAttr( + clang::PointerAuthStructAttr::CreateImplicit(ctx, key, disc)); + } } // Store a forward declaration to this class type in case any diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp index ec0f57276cf3..48e09abf4472 100644 --- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp +++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp @@ -695,6 +695,143 @@ TEST_F(DWARFASTParserClangTests, TestVTablePtrAuthParsing) { ASSERT_EQ(attr->getCustomDiscriminationValue(), 42); } +TEST_F(DWARFASTParserClangTests, TestPtrAuthStructAttr) { + // Tests parsing types with ptrauth_struct attribute + // authentication + + // This is Dwarf for the following C code: + // ``` + // struct [[clang::ptrauth_struct(2, 42)]] A {}; + // struct A a; + // ``` + + const char *yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_AARCH64 +DWARF: + debug_str: + - a + - A + - ptrauth_struct_key + - ptrauth_struct_disc + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Code: 0x2 + Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_external + Form: DW_FORM_flag_present + - Code: 0x3 + Tag: DW_TAG_structure_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Code: 0x4 + Tag: DW_TAG_LLVM_annotation + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_const_value + Form: DW_FORM_udata + + debug_info: + - Version: 5 + UnitType: DW_UT_compile + AddrSize: 8 + Entries: +# 0x0c: DW_TAG_compile_unit +# DW_AT_language [DW_FORM_data2] (DW_LANG_C99) + - AbbrCode: 0x1 + Values: + - Value: 0x0c + +# 0x0f: DW_TAG_variable +# DW_AT_name [DW_FORM_strp] (\"a\") +# DW_AT_type [DW_FORM_ref4] (0x00000018 \"A\") +# DW_AT_external [DW_FORM_flag_present] (true) + - AbbrCode: 0x2 + Values: + - Value: 0x00 + - Value: 0x18 + +# 0x18: DW_TAG_structure_type +# DW_AT_name [DW_FORM_strp] (\"A\") + - AbbrCode: 0x3 + Values: + - Value: 0x02 + +# 0x1d: DW_TAG_LLVM_annotation +# DW_AT_name [DW_FORM_strp] (\"ptrauth_struct_key\") +# DW_AT_const_value [DW_FORM_udata] (2) + - AbbrCode: 0x4 + Values: + - Value: 0x04 + - Value: 0x02 + +# 0x23: DW_TAG_LLVM_annotation +# DW_AT_name [DW_FORM_strp] (\"ptrauth_struct_disc\") +# DW_AT_const_value [DW_FORM_udata] (42) + - AbbrCode: 0x4 + Values: + - Value: 0x17 + - Value: 0x2a + + - AbbrCode: 0x0 # end of child tags of 0x18 + - AbbrCode: 0x0 # end of child tags of 0x0c +... +)"; + YAMLModuleTester t(yamldata); + + DWARFUnit *unit = t.GetDwarfUnit(); + ASSERT_NE(unit, nullptr); + const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE(); + ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit); + DWARFDIE cu_die(unit, cu_entry); + + auto holder = std::make_unique("ast"); + auto &ast_ctx = *holder->GetAST(); + DWARFASTParserClangStub ast_parser(ast_ctx); + + DWARFDIE struct_object = cu_die.GetFirstChild(); + ASSERT_EQ(struct_object.Tag(), DW_TAG_variable); + DWARFDIE structure_type = + struct_object.GetAttributeValueAsReferenceDIE(DW_AT_type); + ASSERT_EQ(structure_type.Tag(), DW_TAG_structure_type); + + SymbolContext sc; + bool new_type = false; + lldb::TypeSP type = + ast_parser.ParseTypeFromDWARF(sc, structure_type, &new_type); + clang::RecordDecl *record_decl = + TypeSystemClang::GetAsRecordDecl(type->GetForwardCompilerType()); + auto [is_key_val_independent, key] = + ast_ctx.getASTContext().getPointerAuthStructKey(record_decl); + auto [is_disc_val_independent, disc] = + ast_ctx.getASTContext().getPointerAuthStructDisc(record_decl); + ASSERT_TRUE(is_key_val_independent); + ASSERT_TRUE(is_disc_val_independent); + ASSERT_EQ(key, 2); + ASSERT_EQ(disc, 42); +} + struct ExtractIntFromFormValueTest : public testing::Test { SubsystemRAII subsystems; clang_utils::TypeSystemClangHolder holder;