C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Standard support for different ABI's for class vtables

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Tue, 3 Jun 2025 12:15:01 +0100
On Tue, Jun 3, 2025 at 11:37 AM Oliver Hunt wrote:
>
> Here's what you need:
>
> https://godbolt.org/z/aaMjfvbro
>
> No. That does not work.
>
> I do not know why you are arguing with me. What you are trying to do does _not_ work.
>
> I gave you multiple godbolt links showing you the required, type specific, codegen required to load the vtable pointer.



Specifically, you provided me with assembler that loads the vtable
pointer in such a way that it does pointer authentication checks
because you used compiler options such as
"-fptrauth-vtable-pointer-address-discrimination", meaning that the
machine code emitted from the compiler used instructions such as
'autd'. It is the introduction of these instructions (such as 'autd')
into the machine code that crashed the program.

Your code did way more stuff than it was supposed to do. All it had to do was:

   (1) Get the vtable pointer from the object, conveniently always
located at [base + 0x00]
   (2) Dereference the vtable pointer and substract 8 to yield the
address of the type_info

and:

   (1) Get the vtable pointer from the object, conveniently always
located at [base + 0x00]
   (2) Dereference the vtable pointer and substract 16 to yield the
address of the "distance to most derived" offset.
   (3) Add the offset to the address of the sub-object.


Your code did way more than that. Sabotage is a strong word but it's
borderline applicable here.



> After correcting your code to include the required underscores, here is the outcome:
>
> ./a.out
>
> Address of stringstream: 0x16bd3eef8
>
> Address of ostream: 0x16bd3ef08
>
> [1] 66169 segmentation fault ./a.out



You told me about something called pointer authentication, which I
hadn't known about before. Thank you for that. I see that pointer
authentication is achieved by the use of aarch64 instructions such as
'autd'.

But I gave you the following assembler in that GodBolt:

    asm (
        ".global GetTypeInfo \n" // Make the symbol globally accessible
        "GetTypeInfo: \n"
        " ldr x8, [x0] \n" // Load vtable pointer
        " ldur x0, [x8, #-8] \n" // Load type_info pointer
        " ret"
    );

    asm (
        ".global GetMostDerived \n" // Make function globally visible
        "GetMostDerived: \n"
        " ldr x8, [x0] \n" // Load vtable pointer
        " ldur x8, [x8, #-16] \n" // Load offset from vtable metadata
        " add x0, x0, x8 \n" // Adjust pointer to most-derived base
        " ret"
    );

You see that I have reduced 'GetTypeInfo' to just 3 instructions, and
'GetMostDerived' to just 4 instructions. None of those 7 instructions
are 'autd', so I don't see why your program's crashing. You mentioned
something about missing underscores but I don't see where in my
GodBolt I could possibly need extra underscores.

How about you provide me with the verbatim code that you fed into your
compiler in order to produce that crashy executable? Also I'd like to
know the compiler version and exactly what string of options you gave
to it at the command line. I don't have an Apple ARM computer here but
I'll use Qemu and a cross-compiler inside a chroot jail to try
replicate. Oh actually I might get Github Actions to do it for me, as
they give you access to the 'macos-15' runner which is 64-Bit ARM. (By
the way the 'macos-13' runner is x86_64).

Your argument is not making sense to me right now. You're telling me
that the program crashes because of instructions like 'autd', but I
have implemented the two functions in assembler without using those
instructions. So what exactly is crashing it? Are you saying that the
return value from 'GetTypeInfo' or the return value from
'GetMostDerived', when it's used in 'main', causes a crash? If this is
the case, then this is an issue of configuration, because the C++ code
shouldn't be making any assumptions about the pointer it receives from
a function implemented in assembler. I mean if I work with a 3rd party
proprietary library -- even one which was written in C++ -- and it has
a function such as:

    extern void *MysteriousFunction(void) noexcept;

then there's no way my executable can do any kind of validation on
that pointer if it has no idea what it's supposed to be. Most it could
do is check what page of memory it's in and then check the permissions
for that page (e.g. read / write / execute), but nothing more than
that -- especially if the mysterious library didn't use any of those
'autd'-like instructions.

Oliver you still haven't convinced me that why I'm proposing is
impossible on your 64-Bit ARM Apple. I am willing to listen to you and
figure this out though. And if you win, you win. I don't think you
will though. Please give me your _verbatim_ source code file along
with your compiler options, and I'll make a new repo on Github and get
the Github Actions to compile it and run it.

P.S. As I was writing this email, I realised that the function
"GetMostDerived' should really return a 'polyhandle' instead of a
'void*', and so you would do:

    std::stringstream ss; // most derived object
    std::ostream &os = ss; // sub-object
    std::polyhandle p(os);
    // Now let's get the most derived:
    void *pv = p.GetMostDerived().get_pointer_to_object();

Received on 2025-06-03 11:15:19