How to implement an IMP function that returns a large struct type determined at run-time?
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.
开发者_Go百科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...
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.
精彩评论