Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. 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...

like image 382
Sherm Pendley Avatar asked Mar 29 '11 17:03

Sherm Pendley


1 Answers

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.

like image 163
Laurent Etiemble Avatar answered Oct 20 '22 15:10

Laurent Etiemble