gnustep-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Understanding the non-fragile ABI


From: David Chisnall
Subject: Understanding the non-fragile ABI
Date: Sat, 20 Mar 2010 17:41:54 +0000

A few people have asked me questions about the non-fragile ABI recently, which 
lead me to believe that I haven't properly explained it in the past, so here is 
a more full explanation:

How it Works (with some simplification):

With the fragile ABI, ivars are turned into fixed offsets.  Imagine that you 
have a class like this:

@interface AClass : ASuperClass
{
        int foo;
        id bar;
}
@end

An access to foo becomes something like this:

*(int*)((char*)self + sizeof(ASuperClass))

An access to bar becomes something like this:

*(id*)((char*)self + sizeof(ASuperClass)+sizeof(int))

If sizeof(ASuperClass) changes - which happens when you add or remove ivars - 
then all of these offsets are incorrect.

With the non-fragile ABI, these are implemented differently.  For code compiled 
with the non-fragile ABI, the accesses become:

*(int*)((char*)self + **__objc_ivar_offset_AClass.foo)

An access to bar becomes something like this:

*(id*)((char*)self +  **__objc_ivar_offset_AClass.bar)

The __objc_ivar_offset_AClass.foo is created as a weak symbol in each 
compilation unit that references the instance variable but doesn't contain the 
class definition.  This is initialised to point to a guess variable, which 
contains the offset that the compiler would have hard-coded with the old ABI.

In compilation units containing the class definition, this variable points 
instead to a canonical definition.  There is also a pointer to this definition 
in the class structure.  At load time, if the canonical definition exists (i.e. 
if the class in which the ivar was declared was compiled with the clang), the 
loader will use it.  If not, it will fall back to the guess.  The runtime will 
then fix up the ivar offsets for any class compiled with the non-fragile ABI.  

The runtime also now does some sanity checking to make sure that every loaded 
class's ivars start at the end of its superclass's ones.  This will make it 
abort in a number of cases where you're using binary-incompatible classes, 
where previously it would have just corrupted your ivars.

There is an optimisation pass in the libobjc2/opts directory that will turn the 
double indirection into single indirection or hard-coding when possible.  If 
all of the superclasses are in the same compilation unit (framework or 
application, if you run it as a link-time optimisation), then you will get 
hard-coded offsets because the superclass layout can't change without the 
subclasses being recompiled.

If the canonical definition exists in the same compilation unit, then you only 
get single indirection after running this pass.  This means that any class can 
access its own instance variables with only single indirection, but it may need 
to use double indirection for its superclass.  

If all code is compiled with the non-fragile ABI, then you never need to use 
the double indirection.  In the next version of the ABI, I will always use 
single-indirection and abort when you try loading a class that uses the GCC ABI 
as a superclass of one that uses the incompatible ABI.

What This Means:

If you compile a class with the non-fragile ABI, it can access its instance 
variables with the correct offsets.  It can access its superclass's instance 
variables at the correct offsets if one or more of the following holds:

- The superclass's instance variables are where they the @interface says they 
should be.
- The superclass is compiled with the non-fragile ABI.
- The superclass is compiled with clang.

Clang always emits the non-fragile ABI offsets variables, even when it does not 
use these offsets.  This means that clang-compiled code that does not use 
-fobjc-nonfragile-abi will work with both the GCC and GNUstep runtimes.  If you 
use the GCC runtime, it will behave like GCC-compiled code. 

If you compile a class with the non-fragile ABI then you may subclass it with a 
class compiled with the fragile ABI, but the normal conditions for the fragile 
ABI apply, specifically that the ivar layout that the compiler sees in the 
@interface must be correct.  Changes to the superclass ivar layout will break 
this subclass, but will not break any subclasses compiled with the non-fragile 
ABI.

David

-- Send from my Jacquard Loom



reply via email to

[Prev in Thread] Current Thread [Next in Thread]