Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question regarding loading libraries on Unix systems #8

Closed
tsuza opened this issue Dec 4, 2023 · 3 comments
Closed

Question regarding loading libraries on Unix systems #8

tsuza opened this issue Dec 4, 2023 · 3 comments

Comments

@tsuza
Copy link

tsuza commented Dec 4, 2023

Understandably, dlopen is used to retrieve the the starting address of a library. However, according to the documentation of the function and many other programs using it, using dlopen on an already loaded library will increment the reference count, which means that unrelated programs will have to use dlclose as many times as it was opened by this library to successfully close it. This is the reason why, for such cases, a dlopen is always paired with dlclose ( here is an example ). Maybe RTLD_NOLOAD could do the job, but I'm unsure if increments the reference count regardless.

So I'm just wondering why it's not being done here, unless I've completely missed something obvious or the purpose of this library, in which case I'm sorry!

@MaulingMonkey
Copy link
Owner

unrelated programs
To be clear, the process unloading will effectively reduce the process's refcount of all shared libraries to zero.

That said, minidl does effectively leak handles to all shared libraries loaded through it for the duration of the program by not supporting dlclose. This is hinted at by the Readme.md:

  • No unloading (would be unsound if the library spawned threads anyways)

Although I can't find a full rant / elaboration on why in these docs... so here goes!

The simple act of loading a dynamic library may, via e.g. constructors:

  • spawn threads running library code
  • register references/pointers to supposedly 'static globals

Unloading out with any of this active is immediate undefined behavior (dangling pointers), limitations on DllMain / OS loader locks mean this typically cannot be sanely fixed via destructors, and the codepath is usually quite poorly tested - if tested at all.

dlclose is a straight up noop on some platforms:

I'm of the opinion that the proper thing to do is to simply leak the shared library.

  • If that causes resource exhaustion issues, restart the process as necessary.
  • If you don't like the file locks for hot reloadable plugins, create a copy of the shared library and load that to leave the original unlocked.
  • Even an unsafe fn would be "bad": it's a footgun that baits people into attempting (and failing) to make a "sound" abstraction around a fundamentally unsound operation.
  • By banning unloading, I allow Library : Copy (which wouldn't be sane if impl Drop for Library { ...dlclose... } existed)

@tsuza
Copy link
Author

tsuza commented Dec 5, 2023

I'm not a fan of leaving the hassle to the developer by forcing them to manually restarting the process or copy-pasting the library, but at least there is a reason behind it. I didn't know about dlclose causing such issues, I assumed that it was thread-safe. And it also makes sense that the programs using it to were mainly single-threaded anyway.

I wonder if there might be another way to tackle it without causing issues.

Anyway, thank you for taking your time to make such an exhaustive explanation! I really appreciate it.

@MaulingMonkey
Copy link
Owner

MaulingMonkey commented Dec 8, 2023

minidl = "0.1.6" / 8090709 introduces unsafe fn Library::close_unsafe_unsound_possible_noop_do_not_use_in_production. This mostly exists to document that you shouldn't use it, but it exists now.

Although I can't find a full rant / elaboration on why in these docs

Apparently my previous ranting on the subject was in denying PR#5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants