nagisa / rust_libloading

Bindings around the platform's dynamic library loading primitives with greatly improved memory safety.

Home Page:https://docs.rs/libloading

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Dropping and reloading a dylib on macOS returns the old version, but only if the dylib uses the stdlib

mistodon opened this issue · comments

commented

I'm attempting to get some sort of code hotswapping working by dynamically re-loading a crate at runtime when it changes.

I've noticed weirdly that it works if the dylib isn't using stdlib (no print!, no Strings, etc.) - but as soon as you add any reference to something from std in the dylib, it breaks the reloading until the program quits. Or rather, the Library reloads, but returns the outdated version (same handle as before).

I pushed a crate with a repo in it: https://github.com/mistodon/dlopen_issue - it's as concise as I could make a working example. But basically:

To reproduce, on macOS:

  1. Load a dylib
  2. Drop it
  3. Recompile it
  4. Load it again
  5. Observe that:
    a. If the dylib uses std, the new version has not been loaded
    b. If the dylib does not use std, the new version has been loaded

I'm not sure if this is a bug in libloading, or dylib, or if I'm doing something wrong. Or possibly this is intended behaviour and I'm not understanding something? But anyway I'm baffled and not sure where to look next.

Thanks for reading, and sorry it's a bit long-winded!

This is a macOS peculiarity which happened in order to fix/work-around bugs related to dynamic library handling in macOS, like #5.

Standard library uses thread-local variables extensively. As per this presentation unloading shared libraries that use thread-local variables is a no-op. Therefore loading a new dylib will just fetch you the old one that’s already loaded.

As per the presentation, please fill a radar against macOS.

For now closing this as there is nothing actionable to be done here on the side of libloading. Feel free to continue the discussion or ask further questions, though.

commented

Ah, that's interesting. It makes a lot more sense now.

I have one question, if you (or anyone else) happen to know. For my specific use case, I don't care too much about leaking resources. Can I load a second version of the same library under a different name and handle? I did try this (copying the lib to a new path and loading that) but I'm still getting the same handle. Any idea how dyld is resolving them to the same handle?

There might be an optimisation in macOS to not load identical libraries at different paths, but if the libraries are actually different and at different locations, reloading should work fine. There are a couple of examples around the ecosystem of people having successes with that kind of approach.

commented

Nice, I must be missing something on my end. I'll do some more experimentation. Also, you've been really helpful and quick to respond, thank you 🙂

commented

Just in case it's useful for anyone else - it wasn't enough to change the file name of the dylib. To avoid dyld caching it regardless, I had to also change the install name with install_name_tool -id '' libfoo.dylib.

I presume any argument to -id would work? But an empty string does for me.

Thanks heaps @mistodon your install_name_tool trick worked a charm.