Contributions to libc++abi for different ABIs

Hi all,

We’re attempting to implement a PC-relative vtable ABI for C++ in Fuchsia and I wanted to ask about the etiquette for contributions to libc++abi for different ABIs.

The purpose of this ABI is to replace the 64 bit absolute address of virtual functions in a vtable with 32 bit relative offsets between the vtable and the virtual function. This would effectively make the vtable readonly and help save on memory by reducing copy on write pages. The same technique is applied to the typeinfo pointer in the vtable, replacing it with an offset to the typeinfo object.

We ran into an issue though where changing the size and value of this typeinfo component breaks dynamic_cast. More specifically, in this snippet from __dynamic_cast in libc++abi:

// Get (dynamic_ptr, dynamic_type) from static_ptr
void **vtable = *static_cast<void ** const *>(static_ptr);
ptrdiff_t offset_to_derived = reinterpret_cast<ptrdiff_t>(vtable[-2]);
const void* dynamic_ptr = static_cast<const char*>(static_ptr) + offset_to_derived;
const __class_type_info* dynamic_type = static_cast<const __class_type_info*>(vtable[-1]);

there is an assumption that the offset_to_derived is 16 bytes behind the first function in the vtable, where for us it would take 12 bytes (8 for the offset to top + 4 for the relative rtti component). The same thing applies to __class_type_info which instead should be 4 bytes behind the vtable pointer and would require an additional dereference and add since it’s an offset for us.

Off the top of my head, it seems like a simple solution to this is to create another __dynamic_cast function (like __dynamic_cast_fuchsia_relative or something) that would calculate these variables correctly for our ABI.

So, finally to my question, is the proper etiquette for making ABI changes this way to contribute them to libc++abi directly, or is there another recommended way? Or is there a better way to address this problem that I haven’t thought of yet? So far, this seems to be the only RTTI breakage I could find, but I’m still actively looking.

Thanks,
Leonard

Hi all,

We’re attempting to implement a PC-relative vtable ABI for C++ in Fuchsia and I wanted to ask about the etiquette for contributions to libc++abi for different ABIs.

The purpose of this ABI is to replace the 64 bit absolute address of virtual functions in a vtable with 32 bit relative offsets between the vtable and the virtual function. This would effectively make the vtable readonly and help save on memory by reducing copy on write pages. The same technique is applied to the typeinfo pointer in the vtable, replacing it with an offset to the typeinfo object.

We ran into an issue though where changing the size and value of this typeinfo component breaks dynamic_cast. More specifically, in this snippet from __dynamic_cast in libc++abi:

// Get (dynamic_ptr, dynamic_type) from static_ptr
void **vtable = *static_cast<void ** const *>(static_ptr);
ptrdiff_t offset_to_derived = reinterpret_cast<ptrdiff_t>(vtable[-2]);
const void* dynamic_ptr = static_cast<const char*>(static_ptr) + offset_to_derived;
const __class_type_info* dynamic_type = static_cast<const __class_type_info*>(vtable[-1]);

there is an assumption that the offset_to_derived is 16 bytes behind the first function in the vtable, where for us it would take 12 bytes (8 for the offset to top + 4 for the relative rtti component). The same thing applies to __class_type_info which instead should be 4 bytes behind the vtable pointer and would require an additional dereference and add since it’s an offset for us.

Off the top of my head, it seems like a simple solution to this is to create another __dynamic_cast function (like __dynamic_cast_fuchsia_relative or something) that would calculate these variables correctly for our ABI.

Could this be a build configuration (e.g. CMake option that changes the behavior or __dynamic_cast) that we would set when compiling libc++abi for Fuchsia? I don’t think we want to change the existing code to use a different function when compiling for Fuchsia, rather we want the new ABI to be the default on Fuchsia and be completely transparent to the code and developers.

I can see that as a viable option. My main concern is if this is the “standard” way of doing things. A quick glance at libcxxabi/CMakeLists.txt doesn’t seem to show any previous examples of people doing this, but I guess this could be the first time this happens.

It seems likely there is no precedent in existing libc++abi code for handling different ABIs aside from per-machine issues that are otherwise uniform in the canonical ABI. I think it’s definitely the right thing to use the same symbol names for the new ABI and just have libc++abi be built for one ABI or another. The libc++abi ABI is already specific to a particular (compiler, machine, OS) tuple whether it’s explicitly clear in the source code that that’s so. It’s up to the maintainers responsible for a particular machine/OS pair to say how to handle ABI versioning issues. For Fuchsia we’re happy to just break the ABI at will and either change SONAME or not as we deem convenient. For other systems it’s likely they will not want to diverge from their existing libc++abi ABI for a long time yet exactly because of the stickiness of the ABI versioning and compatibility story.

How exactly to specify that between the cmake stuff and #ifdef bits in the code is up to the libc++abi maintainers to choose. I think whatever seems clean to you and Petr is liable to be a good starting point for maintainers’ review feedback unless they want to offer specific direction now.