Mangled symbol loading from Rust
I'm writing a mangled symbol loader for Rust.
I'm actually soliciting feedback from experienced Rust programmers here, which would be greatly appreciated đź’ś.
General design
I use a design similar to Boost.DLL's mangled import.
I scan the object file for exported symbols. On Mach-O (macOS and friends), I scan the relevant architecture.
While scanning the relevant exported symbol table, I use rustc-demangle to demangle the symbols. I create a map of mangled symbols to demangled symbols (Note: on Mach-O, I also strip the leading underscore, because macOS and friends prepend an underscore to all symbols in the file).
Once I demangle the symbols, I can then use libc::dlsym
or whatever to load a symbol from a string, after looking it up in the map.
Drawbacks
Obviously there are caveats I am aware of.
- Rust has no stable ABI; this approach is not guaranteed to work between compiler versions, although I trust
rustc-demangle
will be updated promptly. Nonetheless, everything must be built with the same compiler. I think this would be a problem with just linking with a Rust library in general,unless some sort of backwards compat stuff is in there that I'm not aware of. I should use said mechanism if it exists; let me know if it does!Update: I found out there is in fact metadata I can use. - A lot of symbols are exported in a Rust dylib; I don't know which ones are relevant to the functionality of the shared object, or if it's not as simple as just “demangle, map to a mangled symbol,
dlsym
and you're off to the races.” But so far it all seems to work, based on limited testing. - Due to the above issues, I worry this system is brittle. I also question if Boost.DLL isn't also brittle, because ABI has been broken before by GCC and Clang in C++ (they're not even 100% compatible with each other afaik).
- I don't do parameter checking during the mapping process; in fact, I strip off the hash, which could be used (I think) in theory to do such checks. I haven't implemented a scheme to actually generate this hash, and I don't want to maintain something like this (it would involve parsing Rust code, and I don't want to write a Rust parser). This makes mangled imports unsafe. But this is a problem with importing unmangled functions too, and there's not much we can do about it.
- The loading process on every platform I'm aware of involves a cast from
void*
to anextern “Rust”
function pointer. Guaranteed to work on Unix by POSIX, and I think Windows (but I'm not 100% sure on that). But I don't know the applicability on things like WASM or AVR. Then again... are shared/runtime loadable objects even a thing on such platforms???
Comments welcome
Let me know what you think on Fedi! I welcome all constructive feedback. I'm sure I haven't thought of everything, and I really want to know what you think. đź’ś
I do ask that you please bring well-thought out arguments, and not just ad hominems and parroted, cargo culted nonsense.
— Elizabeth Ashford (Elizafox) Fedi (elsewhere): @Elizafox@social.treehouse.systems Tip jar: PayPal || CashApp || LiberaPay