This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [libobjc] fix to nil_method


> >>Hi,
> >>on the libobjc the nil_method is declared as varardic function, however
> >>the calls to it (appears to be autmatically generated by objc) are not.
> >>x86_64 requires all calls to varardic functions to eighter have varardic
> >>prototype or no prototype at all, so objc breaks.  WOuld be the attached patch OK?
> >>
> > nil_method is supposed to be of type IMP, which is variadic, so
> > I'm pretty sure everything is correct as-is.  How exactly is ObjC
> > breaking on x86_64?
> 
> The general problem is code like the following:
> 
> int a (...) {implementation}
> 
> different file:
> extern int a (void);
> 
> b (void)
> { a();}
> 
> We pass in one register the number of variadic arguments - but only if
> the function is variadic.  a is called here as a non-variadic function
> but is implemented as a variadic one.  This will break.
> 

> But at first glance, it appears that libobjc does it right:
> libobjc/objc/objc.h: typedef id (*IMP)(id, SEL, ...);

Huh - I think this is not relevant, but I can understand why everyone
seems confused.

Let me sum up what I remember of how the ObjC compiler/runtime library
performs the method invocation.

Say that the source code contains the call

 [object doSomething: argument];

<which means, call the method 'doSomething:' of the object 'object' with
a single argument 'argumnet'>
The compiler converts that into a sequence of operations.

First, a call to the lookup function, which for the GNU runtime is
objc_msg_lookup(), giving to it the receiver (==the object who method is
being called) and selector (==the method name), which are the information
required to dynamically lookup at runtime what the implementation of the
method is.

This lookup function is supposed to return the function to call to execute
the method ('doSomething:' for the object 'object' in the example).  In
the declaration of the lookup function, the value returned is of type IMP
- the idea of the definition of IMP is to return a 'general function
taking an id, a SEL, followed by other unspecified arguments'.

The value returned is casted by the compiler to a pointer to a function
taking the appropriate number of arguments for that *specific* method ...
extracted by the declaration of the method ('doSomething:') as seen by the
compiler in the class @interface; in the example, probably the function is
casted to be something like void doSomething_impl (id receiver, SEL
selector, id argument); assuming the method doSomething: returns void, and
takes an id argument.

The function pointed to by the pointer is executed, passing the receiver,
selector, and arguments of the method, in this order.

As you see, the 'IMP' declaration is never actually used to call a
function.  The lookup function returns an IMP, which is just a generic
placeholder for a function pointer, it just means it returns a pointer to
a function implementing a method - this function takes a receiver, a
selector, and other unspecified arguments; but it should be casted to the
actual appropriate form with the appropriate number of arguments before
being called.

This is what user-code manually looking up methods should also do
(background - in some cases you can optimize your end-user code by
manually looking up method implementation, and caching the method
implementations, then calling them manually and directly shortcutting the
method lookup - useful in loops) - before invoking a method implementation
as returned by the lookup function, it should cast it to a pointer to a
function taking the appropriate arguments.


Now, the nil_method thing is a trick built on top of all this to
implement the behaviour required by ObjC for nil receivers.

When the receiver of a method call is 'nil' (the null object), the
semantics is supposed to be that nothing happens and the method call
returns (id)nil ['id' means 'an object', 'nil' means 'the null object'].  
A huge amount of user code relies on this behaviour.  It is normally used
for methods returning void (then calling them with a nil receiver simply
does nothing), or returning an object (then calling them with a nil
receiver returns a nil result).

The way in which this is implemented in the GNU runtime is - the lookup
function objc_msg_lookup () checks if the receiver is nil; and if it is,
it returns a pointer to the special function 'nil_method ()'.  
'nil_method ()' is a function which does nothing, and returns nil.

Then, the normal method invocation flow goes on as normal - the nil_method
() function will be casted to whatever function was supposed to be
executed to execute that method (that function will take an id, followed
by a SEL, followed by who knows what arguments, depends on the method),
and executed.

Apparently, the usual definition of nil_method () can support this
behaviour on usual machines, and the problem you get is because the usual
definition doesn't work on your machine. :-(

So, it looks to me that the fix should consist in implementing the
'nil_method ()' function so that it can be called in place of any function
taking an 'id' argument followed by a 'SEL' argument, followed by zero, or
one, or any number of arguments (both a fixed number, or a variable number
!).

If declaring it to take just an 'id' then a 'SEL' argument is enough to
get this result (I suppose it depends on how function calls are
implemented/compiled, about which I don't know much at the moment) - as in
Honza's patch, then it's fine for me and the patch looks good.  If not,
then the patch is not good.

Anyway I hope I explained the ObjC machine enough to let the
stack/function calls/etc hackers find a way to fix it ... :-)

Please note again that the 'declaration' of the 'nil_method()' function
will *never* be used at runtime in the running program.  It is only
checked by the compiler when compiling libobjc, in objc_msg_lookup() so
that nil_method() can be returned without warnings, it must be an IMP, but
it's irrelevant - we can cast it to an IMP inside objc_msg_lookup().

Sorry for being long, but I hope it helps.

I might have got something incorrect as I didn't have time to actually
examine the compiler and the runtime library, and was talking by heart and
memories, so if something doesn't match my description, the description
might need a slight amending.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]