Skip to content

Conversation

@xdoardo
Copy link
Collaborator

@xdoardo xdoardo commented Nov 28, 2025

The current code generating debuginfo for vtables assumes that sizeof(usize) == pointer_size && alignof(usize) == pointer_align. Of course, this does not work for CHERIoT, and this patch tries to rewrite the generation of this data to be compatible with CHERIoT (and other platforms with non-integral pointers).

Without this patch, the debuginfo generated for vtables on CHERIoT looks like this:

@vtable.0 = private addrspace(200) constant <{ ptr addrspace(200), [4 x i8], [4 x i8], [4 x i8], [4 x i8], ptr addrspace(200), ptr addrspace(200), ptr addrspace(200) }> <{ ptr addrspace(200) @"..", [4 x i8] c"..", [4 x i8] undef, [4 x i8] c"..", [4 x i8] undef, ptr addrspace(200) @"..", ptr addrspace(200) @"..", ptr addrspace(200) @.. }>, align 8, !dbg !7
...
!7 = !DIGlobalVariableExpression(var: !8, expr: !DIExpression())
!8 = distinct !DIGlobalVariable(name: "<..>::{vtable}", scope: null, file: !5, type: !9, isLocal: true, isDefinition: true)
!9 = !DICompositeType(tag: DW_TAG_structure_type, name: "<..>::{vtable_type}", file: !5, size: 384, align: 64, flags: DIFlagArtificial, elements: !10, vtableHolder: !20, templateParams: !6, identifier: "..")
!10 = !{!11, !14, !16, !17, !18, !19} ; notice here: 6 members for a total size of ----------^ (384 bits)
!11 = !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: !9, file: !5, baseType: !12, size: 64, align: 64)
!12 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "*const ()", baseType: !13, size: 64, align: 64, dwarfAddressSpace: 0)
!13 = !DIBasicType(name: "()", encoding: DW_ATE_unsigned)
!14 = !DIDerivedType(tag: DW_TAG_member, name: "size", scope: !9, file: !5, baseType: !15, size: 64, align: 64, offset: 64) ; but "size" is 32 bits...
!15 = !DIBasicType(name: "usize", size: 32, encoding: DW_ATE_unsigned)
!16 = !DIDerivedType(tag: DW_TAG_member, name: "align", scope: !9, file: !5, baseType: !15, size: 64, align: 64, offset: 128) ; ...and "align" as well. Notice that they are also using the size and alignment of pointers.
!17 = !DIDerivedType(tag: DW_TAG_member, name: "__method3", scope: !9, file: !5, baseType: !12, size: 64, align: 64, offset: 192)
!18 = !DIDerivedType(tag: DW_TAG_member, name: "__method4", scope: !9, file: !5, baseType: !12, size: 64, align: 64, offset: 256)
!19 = !DIDerivedType(tag: DW_TAG_member, name: "__method5", scope: !9, file: !5, baseType: !12, size: 64, align: 64, offset: 320)

The generated data change with this patch (only when targeting CHERIoT, it stays the same for "usual" targets) in:

@vtable.0 = private addrspace(200) constant <{ ptr addrspace(200), [4 x i8], [4 x i8], [4 x i8], [4 x i8], ptr addrspace(200), ptr addrspace(200), ptr addrspace(200) }> <{ ptr addrspace(200) @"..", [4 x i8] c"..", [4 x i8] undef, [4 x i8] c"..", [4 x i8] undef, ptr addrspace(200) @"..", ptr addrspace(200) @"..", ptr addrspace(200) @.. }>, align 8, !dbg !7
...
!7 = !DIGlobalVariableExpression(var: !8, expr: !DIExpression())
!8 = distinct !DIGlobalVariable(name: "<..>::{vtable}", scope: null, file: !5, type: !9, isLocal: true, isDefinition: true)
!9 = !DICompositeType(tag: DW_TAG_structure_type, name: "<..>::{vtable_type}", file: !5, size: 320, align: 64, flags: DIFlagArtificial, elements: !10, vtableHolder: !20, templateParams: !6, identifier: "..")
!10 = !{!11, !14, !16, !17, !18, !19} ; the size of the vtable is now correct: 320 bits vs. 384, i.e. 64 bits less
!11 = !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: !9, file: !5, baseType: !12, size: 64, align: 64)
!12 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "*const ()", baseType: !13, size: 64, align: 64, dwarfAddressSpace: 0)
!13 = !DIBasicType(name: "()", encoding: DW_ATE_unsigned)
!14 = !DIDerivedType(tag: DW_TAG_member, name: "size", scope: !9, file: !5, baseType: !15, size: 32, align: 32, offset: 64)
!15 = !DIBasicType(name: "usize", size: 32, encoding: DW_ATE_unsigned)
!16 = !DIDerivedType(tag: DW_TAG_member, name: "align", scope: !9, file: !5, baseType: !15, size: 32, align: 32, offset: 96)
!17 = !DIDerivedType(tag: DW_TAG_member, name: "__method3", scope: !9, file: !5, baseType: !12, size: 64, align: 64, offset: 128)
!18 = !DIDerivedType(tag: DW_TAG_member, name: "__method4", scope: !9, file: !5, baseType: !12, size: 64, align: 64, offset: 192)
!19 = !DIDerivedType(tag: DW_TAG_member, name: "__method5", scope: !9, file: !5, baseType: !12, size: 64, align: 64, offset: 256)

Of course, the new way to generate the data should produce exactly the same output it produced for every other target in rustc, that is, exactly the same code should be generated if sizeof(usize) == pointer_size && alignof(usize) == pointer_align is true. This is, of course, a strict requirement to merge this PR.

Marking this as a draft because I have to figure out how to test this code that looks plausible but could break catastrophically, and having CI is useful.

@xdoardo
Copy link
Collaborator Author

xdoardo commented Nov 28, 2025

Just noticed a bug:

@vtable.0 = private addrspace(200) constant <{ ptr addrspace(200), [4 x i8], [4 x i8], [4 x i8], [4 x i8], ptr addrspace(200), ptr addrspace(200), ptr addrspace(200) }> <{ ptr addrspace(200) @"..", [4 x i8] c"..", [4 x i8] undef, [4 x i8] c"..", [4 x i8] undef, ptr addrspace(200) @"..", ptr addrspace(200) @"..", ptr addrspace(200) @.. }>, align 8, !dbg !7

is still wrong, it counts again size and alignment as [4 x i8], [4 x i8], [4 x i8], [4 x i8], that is 64 bits each. I suspect this comes from a different function than the one I patched, but I might be wrong. Ideally the two should fit in 64 bits together, I think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant