How to implement an IMP function that returns a large struct type determined at run-time?
Objective CPerlObjective C-RuntimeObjective C Problem Overview
Background: CamelBones registers Perl classes with the Objective-C runtime.
To do this, every Perl method is registered with the same IMP
function; that function examines its self
& _cmd
arguments to find
which Perl method to call.
This has worked well enough for several years, for messages that were
dispatched with objc_msgSend
. But now I want to add support for
returning floating-point and large struct types from Perl methods.
Floating-point isn't hard; I'll simply write another IMP that returns
double, to handle messages dispatched with objc_msgSend_fpret
.
The question is what to do about objc_msgSend_stret
. Writing a
separate IMP
for every possible struct return type is impractical, for
two reasons: First, because even if I did so only for struct types
that are known at compile-time, that's an absurd number of functions.
And second, because we're talking about a framework that can be linked against any arbitrary Objective-C & Perl code, we don't know all the potential struct types when the framework is being compiled.
What I hope to do is write a single IMP
that can handle any return
type that's dispatched via objc_msgSend_stret
. Could I write it as
returning void
, and taking a pointer argument to a return buffer, like
the old objc_msgSend_stret
was declared? Even if that happened to
work for now, could I rely on it continuing to work in the future?
Thanks for any advice - I've been racking my brain on this one. :-)
Update:
Here's the advice I received from one of Apple's runtime engineers, on their objc-language mailing list:
> You must write assembly code to handle
> this case.
>
> Your suggestion fails on some
> architectures, where ABI for "function
> returning void with a pointer to a
> struct as the first argument" differs
> from "function returning a struct".
> (On i386, the struct address is popped
> from the stack by the caller in one
> case and by the callee in the other
> case.) That's why the prototype for
> objc_msgSend_stret
was changed.
>
> The assembly code would capture the
> struct return address, smuggle it into
> non-struct-return C function call
> without disturbing the rest of the
> parameters, and then do the right
> ABI-specific cleanup on exit (ret $4
> on i386). Alternatively, the assembly
> code can capture all of the
> parameters. The forwarding machinery
> does something like this. That code
> might be in open-source CoreFoundation
> if you want to see what the techniques
> look like.
I'll leave this question open, in case someone brainstorms a better idea, but with this coming directly from Apple's own "runtime wrangler," I figure it's probably as authoritative an answer as I'm likely to get. Time to dust off the x86 reference manuals and knock the rust off my assembler-fu, I guess...
Objective C Solutions
Solution 1 - Objective C
It seems that the Apple engineer is right: the only to way to go is assembly code. Here are some usefull pointers to getting started:
- From the Objective-C runtime code: The i386 and x86_64 hand-crafted messenger assmbly stubs for the various messaging methods.
- An SO answer that provides an overview of the dispatching.
- A in-depth review of the dispatching mecanism with a line-by-line analysis of the assembly code
Hope it helps.