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.
_______________________________________________
cxx-abi-dev mailing list
[email protected]
http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev

Reply via email to