Skip to content

prevent unloading of all GNU/Linux objects by setting DF_1_NODELETE - #158402

Open
joboet wants to merge 1 commit into
rust-lang:mainfrom
joboet:gnu_linux_nodelete
Open

prevent unloading of all GNU/Linux objects by setting DF_1_NODELETE#158402
joboet wants to merge 1 commit into
rust-lang:mainfrom
joboet:gnu_linux_nodelete

Conversation

@joboet

@joboet joboet commented Jun 25, 2026

Copy link
Copy Markdown
Member

glibc supports unloading dynamic libraries through dlclose. However, unloading dynamic libraries is fundamentally incompatible with assumptions made by std and much of the Rust ecosystem. Notably, the 'static lifetime is commonly expected to last until the termination of the abstract machine, which is required for the soundness of e.g. thread-local destructors. Hence, this PR disables unloading for all GNU/Linux Rust objects by having the linker set the DF_1_NODELETE flag (LD docs/LLD docs). Note that this flag does not make dlclose fail, it will just not have any effect.

I suppose this requires a MCP? Not sure how this is handled...

@rustbot

rustbot commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

These commits modify compiler targets.
(See the Target Tier Policy.)

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jun 25, 2026
@rustbot

rustbot commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

r? @mejrs

rustbot has assigned @mejrs.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler
  • compiler expanded to 73 candidates
  • Random selection from 19 candidates

@rust-log-analyzer

This comment has been minimized.

@joboet
joboet force-pushed the gnu_linux_nodelete branch from bc193bb to 842b8c5 Compare June 25, 2026 18:20
@rust-log-analyzer

Copy link
Copy Markdown
Collaborator

The job aarch64-gnu-llvm-21-2 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
failures:

---- spec::tests::aarch64_be_unknown_linux_gnu_ilp32 stdout ----

thread 'spec::tests::aarch64_be_unknown_linux_gnu_ilp32' (50994) panicked at compiler/rustc_target/src/spec/mod.rs:3658:35:
Target consistency check failed:
 link args for cc and non-cc versions of flavors are not consistent
stack backtrace:
   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: <rustc_target::spec::Target>::test_target::{closure#1}
   3: <rustc_target::spec::Target>::test_target
---
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
---- spec::tests::aarch64_be_unknown_linux_gnu stdout end ----
---- spec::tests::aarch64_unknown_linux_gnu_ilp32 stdout ----

thread 'spec::tests::aarch64_unknown_linux_gnu_ilp32' (51009) panicked at compiler/rustc_target/src/spec/mod.rs:3658:35:
Target consistency check failed:
 link args for cc and non-cc versions of flavors are not consistent
stack backtrace:
   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: <rustc_target::spec::Target>::test_target::{closure#1}
   3: <rustc_target::spec::Target>::test_target
---
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
---- spec::tests::armv7_unknown_linux_gnueabihf stdout end ----
---- spec::tests::csky_unknown_linux_gnuabiv2 stdout ----

thread 'spec::tests::csky_unknown_linux_gnuabiv2' (51084) panicked at compiler/rustc_target/src/spec/mod.rs:3658:35:
Target consistency check failed:
 link args for cc and non-cc versions of flavors are not consistent
stack backtrace:
   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: <rustc_target::spec::Target>::test_target::{closure#1}
   3: <rustc_target::spec::Target>::test_target
   4: <rustc_target::spec::tests::csky_unknown_linux_gnuabiv2::{closure#0} as core::ops::function::FnOnce<()>>::call_once
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
---- spec::tests::csky_unknown_linux_gnuabiv2 stdout end ----
---- spec::tests::csky_unknown_linux_gnuabiv2hf stdout ----

thread 'spec::tests::csky_unknown_linux_gnuabiv2hf' (51085) panicked at compiler/rustc_target/src/spec/mod.rs:3658:35:
Target consistency check failed:
 link args for cc and non-cc versions of flavors are not consistent
stack backtrace:
   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: <rustc_target::spec::Target>::test_target::{closure#1}
   3: <rustc_target::spec::Target>::test_target
---
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
---- spec::tests::m68k_unknown_linux_gnu stdout end ----
---- spec::tests::mips64_unknown_linux_gnuabi64 stdout ----

thread 'spec::tests::mips64_unknown_linux_gnuabi64' (51124) panicked at compiler/rustc_target/src/spec/mod.rs:3658:35:
Target consistency check failed:
 link args for cc and non-cc versions of flavors are not consistent
stack backtrace:
   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: <rustc_target::spec::Target>::test_target::{closure#1}
   3: <rustc_target::spec::Target>::test_target
   4: <rustc_target::spec::tests::mips64_unknown_linux_gnuabi64::{closure#0} as core::ops::function::FnOnce<()>>::call_once
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
---- spec::tests::mips64_unknown_linux_gnuabi64 stdout end ----
---- spec::tests::mips64el_unknown_linux_gnuabi64 stdout ----

thread 'spec::tests::mips64el_unknown_linux_gnuabi64' (51126) panicked at compiler/rustc_target/src/spec/mod.rs:3658:35:
Target consistency check failed:
 link args for cc and non-cc versions of flavors are not consistent
stack backtrace:
   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: <rustc_target::spec::Target>::test_target::{closure#1}
   3: <rustc_target::spec::Target>::test_target
---
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
---- spec::tests::mipsisa32r6_unknown_linux_gnu stdout end ----
---- spec::tests::mipsisa64r6el_unknown_linux_gnuabi64 stdout ----

thread 'spec::tests::mipsisa64r6el_unknown_linux_gnuabi64' (51143) panicked at compiler/rustc_target/src/spec/mod.rs:3658:35:
Target consistency check failed:
 link args for cc and non-cc versions of flavors are not consistent
stack backtrace:
   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: <rustc_target::spec::Target>::test_target::{closure#1}
   3: <rustc_target::spec::Target>::test_target
   4: <rustc_target::spec::tests::mipsisa64r6el_unknown_linux_gnuabi64::{closure#0} as core::ops::function::FnOnce<()>>::call_once
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
---- spec::tests::mipsisa64r6el_unknown_linux_gnuabi64 stdout end ----
---- spec::tests::mipsisa64r6_unknown_linux_gnuabi64 stdout ----

thread 'spec::tests::mipsisa64r6_unknown_linux_gnuabi64' (51142) panicked at compiler/rustc_target/src/spec/mod.rs:3658:35:
Target consistency check failed:
 link args for cc and non-cc versions of flavors are not consistent
stack backtrace:
   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: <rustc_target::spec::Target>::test_target::{closure#1}
   3: <rustc_target::spec::Target>::test_target
---
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
---- spec::tests::powerpc64_unknown_linux_gnu stdout end ----
---- spec::tests::powerpc64_unknown_linux_gnuelfv2 stdout ----

thread 'spec::tests::powerpc64_unknown_linux_gnuelfv2' (51149) panicked at compiler/rustc_target/src/spec/mod.rs:3658:35:
Target consistency check failed:
 link args for cc and non-cc versions of flavors are not consistent
stack backtrace:
   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: <rustc_target::spec::Target>::test_target::{closure#1}
   3: <rustc_target::spec::Target>::test_target

@bjorn3

bjorn3 commented Jun 26, 2026

Copy link
Copy Markdown
Member

This would break hot code reloading, wouldn't it? While hot code reloading isn't sound, there are use cases where it is the only approach to get reasonable iteration times as the program takes some time to load and more importantly has a lot of non-serialized state like for example the entire world in the case of a game.

@joboet

joboet commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

Not really, it just takes more memory – dlclose will obviously not unload anything but dlopen will still correctly open the modified binary, just as a new library. And I don't think that the increased memory usage is a problem as long as the kernel swaps the now probably unused pages to disk.

@bjorn3

bjorn3 commented Jun 26, 2026

Copy link
Copy Markdown
Member

AFAIK dlopen will reuse an existing loaded library if the path matches. Also a memory leak can easily cause memory exhaustion if you hot reload many times without restarting the program. And on Windows it will prevent cargo from replacing the DLL entirely as opened files can't be deleted on Windows by default. (I know this PR doesn't apply to Windows, but if you do it for glibc, why wouldn't you do it for Windows too?)

And I don't think that the increased memory usage is a problem as long as the kernel swaps the now probably unused pages to disk.

That only works for read-only pages, not writable pages (including those that are read-only at runtime but contain relocations that are applied at load time like those in .data.rel.ro) Writable pages end up in the swap, which the user may not have set up, but even if they did probably don't have it set up to be very big.

In any case dlclose itself has the safety obligation of checking that the dylib supports unloading. It is not the responsibility of the dylib to prevent unloading if it isn't safe to unload.

@mejrs

mejrs commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

r? bjorn3

@rustbot rustbot assigned bjorn3 and unassigned mejrs Jun 26, 2026
@Mark-Simulacrum

Copy link
Copy Markdown
Member

I think it would be unfortunate to break the ability to write a library that can be unloaded; that may be needed for parity with an existing C library for example. Is there a concrete list of things that are making the assumption this doesn't happen to which we could perhaps attach this? For example, maybe if you use a thread_local! with a significant Drop then we apply this restriction?

until the termination of the abstract machine

What parts of the Rust abstract machine are still running after a dlclose?

@bjorn3

bjorn3 commented Jun 27, 2026

Copy link
Copy Markdown
Member

Thread locals already prevent glibc from unloading a dylib. It is more background threads, atexit handlers or references to data inside the dylib.

@bjorn3 bjorn3 added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants