[cxx-abi-dev] RTTI symbol uniqueness
John McCall
rjmccall at apple.com
Thu Nov 21 05:43:29 UTC 2013
The ABI often requires symbols to be emitted with vague linkage: inline functions, static data members of class temploids, RTTI objects and names for types that aren’t polymorphic classes with a key function, etc.
Vague linkage is fine during static linking*. However, it can be really expensive if the program needs dynamic linking***, and it’s expensive even if only one shared object actually defines the symbol.
* Well, no, it causes terrible build-time regressions, but that’s hard to avoid in the C++ translation model.**
** Unless you are EDG.
*** Which is to say, always, unless you’re on a very special platform.
When the symbol is a function, a lot of this cost is avoidable, or at least postponable:
- Direct calls and v-table references don’t care about the exact address and can potentially be resolved within the shared object.* **
- Direct calls can use a lazy-binding stub to postpone symbol resolution until the code actually executes.
* At least, this is legal by the language standard. IIRC, GCC has historically not done this on the grounds that it violates the ELF standard.
** Although this can hurt code locality by causing multiple copies of a function to be executed.
When the symbol is a variable, including RTTI objects and names, that resolution has to occur immediately upon load.* That’s a lot of work, but more importantly, it’s a lot of memory that has to be loaded from disk before you can even start running global constructors.
* References from code could also use lazy binding in theory, but I don’t think anybody does this for various reasons. References from data, like the name pointer from an RTTI object, have to be resolved immediately.
Some of that work is absolutely necessary: the language requires variables to have a single, unique address. But the language has no such requirement on RTTI objects and names; that’s purely our requirement, done to make it more efficient to compare type_infos.
In practice, users take measures to reduce these costs, e.g. using broad-spectrum visibility switches like GCC/Clang’s -fvisibility=hidden. If they use dynamic libraries, they then usually have to manually undo that for specific symbols, e.g. with attributes like GCC/Clang’s __attribute__((visibility(“default”))). Symbol visibility is its own, glorious thing that some of us probably ought to standardize the computation of one of these days. All that’s really important is that it’s not uncommon for RTTI symbols to get hidden despite actually needing to be uniqued across library boundaries.
I believe GCC and libstdc++ have recently(-ish) taken measures against that problem. If the RTTI name starts with ‘*’, it is known to have a unique address; otherwise, equality/comparison must happen with strcmp. I don’t know exactly when the library and compile use that ABI, however, or what causes the compiler to emit a name starting with ‘*'.
On ARM64, Apple/clang/libc++ does something very similar. When an RTTI object would otherwise have default visibility and vague linkage, we instead give it hidden visibility and set a high bit in the RTTI object’s name pointer.* If that bit is set on both pointers, we fall back on using string comparison. Using a high bit means that, in the fast path, the name data never needs to be loaded at all.
* On ARM64, the top eight bits of all pointers are reserved for shenanigans.**
** Not Objective-C object pointers.
The chief difference* is that Apple’s approach still requires the type to be formally given the right visibility.** It's primarily a targeted optimization to eliminate a common need for vague linkage in the dynamic linker; it's also ARM64-specific, and on all other platforms, Apple’s clang still hews exactly to the ABI. In contrast, I believe GCC’s approach is intended to Do The Right Thing even when types are completely mis-decorated (or at least, left undecorated under -fvisibility=hidden).
* Ignoring the minor representation difference.
** Clang provides a type_visibility attribute which only controls the visibility only of a type, not its (non-type) members. This makes it easy to formally export a type without changing the visibility rules for any of its member functions. This is also essentially what happens with -fvisibility-ms-compat.
This seems to be a growing area of disagreement between vendors, and I wanted to bring it to the list’s attention for discussion.
John.
More information about the cxx-abi-dev
mailing list