fruity
fruity copied to clipboard
Make `selector!` macro zero-cost
Currently, the selector!
macro calls Sel::register
, which in turn calls sel_registerName
. This is inefficient compared to @selector
in Objective-C.
On my machine, I measure:
Initial | Subsequent | |
---|---|---|
Low | 2.15µs | 100ns |
High | 2.45µs | 210ns |
This is relatively small compared to objc_msgSend
. However, one of the goals of this crate is to be zero-cost compared to the same code written directly in Objective-C.
Related issues:
- https://github.com/rust-lang/rust/issues/80019
- https://github.com/SSheldon/rust-objc/issues/49
- https://github.com/rust-lang/rust/issues/51608
I made an attempt in nvzqz/rust-selector-example, however it fails to build at link time. See that repo for details.
The culprit seems to be the literal_pointers
link option. It builds fine with just __DATA,__objc_selrefs
, but I get "unrecognized selector" when trying to use the selector.
A couple guesses as to why this fails (the first being the likely culprit, but I'd be curious to hear if you know otherwise):
-
In a binary, all internal pointers use an arbitrary virtual memory base address. When the binary is loaded, all the internal metadata pointers will be adjusted using the real load address that dyld chose at runtime (this is called a 'rebase'). dyld relies on metadata in the
__LINKEDIT
segment to find the pointers to rebase. Since you're not producing any metadata informing dyld about your pointer to the selector literal in__objc_methname
, the system might see it as an invalid value and skip over it when processing selrefs. I haven't checked the source or verified this behaviour, though. Note that the data format in__LINKEDIT
changed significantly in iOS 15. The details are a bit gruesome, but I made a longer writeup here -
You might also need a type encoding for the selector in
__objc_methtype
.
For the same reason I would have expected difficulties using the CFConstantStrings
you’re generating, since they contain rebased pointers to __cstring
(though I’d be very curious to hear if it works in practice!)
In my fork of objc
I am having issues with CFConstantStrings
- fruity exhibits this as well, try:
fn main() {
let s = || fruity::ns_string!("abc");
println!("{}", s());
}
Linkers and dynamic linkers are very much opaque pieces of software to me, are there some resources you can recommend to get a better understanding of such details (I'm asking you because you seem very knowledgable in this area)? The writeup you provided is very cool, but sadly I feel I lack the necessary foundation to properly understand it.
(Also, I've implemented a static selector macro that somewhat works (but is still very brittle), search for unstable-static-sel
in this file).
It's really neat to have the hunch confirmed, thank you for letting me know!
Unfortunately I don't know of a good resource to point to for a '10,000 foot' view. For me, I look at and parse many binaries for work, so I've gotten familiar with the structure that way. I personally think that parsing binaries is probably the best way to gain familiarity, as you're 'forced' to understand all the pieces. I would just throw in: while they may be opaque to you now, there's absolutely no magic and it's all approachable. That said, everybody here is deep into Rust/Darwin land so that might go without saying =).
If you're not already doing this, I would strongly recommend compiling a binary containing some simple structure (like the ObjC equivalent of the fruity example you gave above) with the first-party toolchain, then crack open the resulting binary with Hopper/IDA and click around to see how the internal Mach-O structures reference each other, and have a look at all the resulting Mach-O sections. Any time you're having a bug with your tooling, look at the diff in the resulting binaries.
Learning the __LINKEDIT
structures is a little bit trickier, particularly because Hopper (at least) will hide them from you and parse them upfront, for user convenience. For me, I learned how these structures are laid out by parsing them.
For work, I regularly make internal educational content describing various bits of esoterica. A couple months ago, I happened to make a ~10 minute video covering some conceptual introductions to dynamic linking, as well as the __LINKEDIT
change I mentioned above, which might be useful for you? The video isn't available publicly, but I can send you a copy of it if you tell me a good way to reach you.
Lastly, I'm happy to answer other questions that you might have now or in the future! Feel free to ping in-thread or elsewhere.