Skip to content

std: eargerly run TLS destructors to catch stack overflows - #164917

Open
joboet wants to merge 3 commits into
rust-lang:mainfrom
joboet:eager_tls_dtors
Open

joboet wants to merge 3 commits into
rust-lang:mainfrom
joboet:eager_tls_dtors

Conversation

@joboet

@joboet joboet commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

This addresses #111272 on UNIX platforms with native TLS by eagerly running the destructors of TLS variables inside the thread main function while the stack overflow detection logic is still active. I've previously tried this strategy in #109858, but that failed on the Apple platforms because I removed the _tlv_atexit implementation of guard::enable in favour of the pthread-TLS-based implementation which doesn't work on Apple (see the updated documentation in the corresponding file for why that is).

This obviously requires us to always manage the list of destructors manually, even on platforms like Linux that allow registering a per-object destructor. While that makes it slightly harder to support the case where a library referencing an instance of std defined in a separate DSO is unloaded, we currently don't support that anyway – __cxa_thread_atexit_impl prevents the unloading of the DSO referenced by the dso_symbol argument, but we always reference the dso_handle living alongside std instead of the one of the DSO where the variable is defined.

I've also added a drive-by commit updating the documentation of the Apple thread guard with some interesting caveats I've recently come across.

@rustbot rustbot added O-hermit Operating System: Hermit 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. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Oct 25, 2026
@rustbot

rustbot commented Oct 25, 2026

Copy link
Copy Markdown
Collaborator

r? @jhpratt

rustbot has assigned @jhpratt.
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: @ChrisDenton, libs
  • @ChrisDenton, libs expanded to 37 candidates
  • Random selection from 6 candidates

@joboet joboet changed the title Eager tls dtors std: eargerly run TLS destructors to catch stack overflows Oct 25, 2026
@rust-log-analyzer

This comment has been minimized.

@joboet

joboet commented Oct 25, 2026

Copy link
Copy Markdown
Member Author

@bors try jobs=test-aarch64-apple-,test-x86_64-msvc-

@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Oct 25, 2026
std: eargerly run TLS destructors to catch stack overflows 


try-job: test-aarch64-apple-*
try-job: test-x86_64-msvc-*
@rust-log-analyzer

Copy link
Copy Markdown
Collaborator

The job test-aarch64-apple-1 failed! Check out the build log: (web) (plain enhanced) (plain)

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

---- [codegen] tests/codegen-llvm/optimize-closure-shim.rs stdout ----
------FileCheck stdout------------------------------

------FileCheck stderr------------------------------
/Users/runner/work/rust/rust/tests/codegen-llvm/optimize-closure-shim.rs:11:15: error: CHECK-DAG: expected string not found in input
// CHECK-DAG: define{{.*}}void {{.*}}shim{{.*}}test_passed_directly{{.*}}(){{.*}}#[[ATTR_SIZE]]
              ^
/Users/runner/work/rust/rust/build/aarch64-apple-darwin/test/codegen-llvm/optimize-closure-shim/optimize-closure-shim.ll:8:171: note: scanning from here
define internal void @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim20test_passed_directly0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_() unnamed_addr #0 personality ptr @rust_eh_personality !guid !4 {
                                                                                                                                                                          ^
/Users/runner/work/rust/rust/build/aarch64-apple-darwin/test/codegen-llvm/optimize-closure-shim/optimize-closure-shim.ll:8:171: note: with "ATTR_SIZE" equal to "0"
define internal void @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim20test_passed_directly0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_() unnamed_addr #0 personality ptr @rust_eh_personality !guid !4 {
                                                                                                                                                                          ^
/Users/runner/work/rust/rust/build/aarch64-apple-darwin/test/codegen-llvm/optimize-closure-shim/optimize-closure-shim.ll:58:68: note: possible intended match here
 tail call void @f(ptr noundef nonnull @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim20test_passed_directly0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_)
                                                                   ^

Input file: /Users/runner/work/rust/rust/build/aarch64-apple-darwin/test/codegen-llvm/optimize-closure-shim/optimize-closure-shim.ll
Check file: /Users/runner/work/rust/rust/tests/codegen-llvm/optimize-closure-shim.rs

-dump-input=help explains the following input dump.

Input was:
<<<<<<
          1: ; ModuleID = 'optimize_closure_shim.4274e599c06735a1-cgu.0' 
          2: source_filename = "optimize_closure_shim.4274e599c06735a1-cgu.0" 
          3: target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" 
          4: target triple = "arm64-apple-macosx11.0.0" 
          5:  
          6: ; <optimize_closure_shim::test_passed_directly::{closure#0} as core::ops::function::FnOnce<()>>::call_once 
          7: ; Function Attrs: inlinehint minsize nounwind optsize uwtable 
          8: define internal void @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim20test_passed_directly0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_() unnamed_addr #0 personality ptr @rust_eh_personality !guid !4 { 
dag:11'0                                                                                                                                                                              {                                                   search range start (exclusive)
dag:11'1                                                                                                                                                                                                                                  error: no match found in search range
dag:11'2                                                                                                                                                                                                                                  with "ATTR_SIZE" equal to "0"
          9: start: 
         10:  tail call void @side_effect_3() #5 
         11:  ret void 
         12: } 
         13:  
         14: ; <optimize_closure_shim::test_none::{closure#0} as core::ops::function::FnOnce<()>>::call_once 
         15: ; Function Attrs: noinline optnone uwtable 
         16: define internal void @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim9test_none0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_() unnamed_addr #1 personality ptr @rust_eh_personality !guid !5 { 
         17: start: 
         18:  %_2 = alloca [0 x i8], align 1 
         19: ; call optimize_closure_shim::test_none::{closure#0} 
         20:  call fastcc void @_RNCNvCs5HKjKAbbNcr_21optimize_closure_shim9test_none0B3_() 
         21:  br label %bb1 
         22:  
         23: bb1: ; preds = %start 
         24:  ret void 
         25: } 
         26:  
         27: ; <optimize_closure_shim::test_size::{closure#0} as core::ops::function::FnOnce<()>>::call_once 
         28: ; Function Attrs: inlinehint minsize nounwind optsize uwtable 
         29: define internal void @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim9test_size0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_() unnamed_addr #0 personality ptr @rust_eh_personality !guid !6 { 
         30: start: 
         31:  tail call void @side_effect_2() #5 
         32:  ret void 
         33: } 
         34:  
         35: ; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable 
         36: define noundef nonnull ptr @test_none() unnamed_addr #2 !guid !7 { 
         37: start: 
         38:  ret ptr @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim9test_none0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_ 
         39: } 
         40:  
         41: ; optimize_closure_shim::test_none::{closure#0} 
         42: ; Function Attrs: noinline optnone uwtable 
         43: define internal fastcc void @_RNCNvCs5HKjKAbbNcr_21optimize_closure_shim9test_none0B3_() unnamed_addr #1 !guid !8 { 
         44: start: 
         45:  call void @side_effect_1() #5 
         46:  ret void 
         47: } 
         48:  
         49: ; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable 
         50: define noundef nonnull ptr @test_size() unnamed_addr #2 !guid !9 { 
         51: start: 
         52:  ret ptr @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim9test_size0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_ 
         53: } 
         54:  
         55: ; Function Attrs: noinline uwtable 
         56: define void @test_passed_directly() unnamed_addr #3 !guid !10 { 
         57: start: 
         58:  tail call void @f(ptr noundef nonnull @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim20test_passed_directly0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_) 
dag:11'3                                                                        ?                                                                                                          possible intended match
         59:  ret void 
         60: } 
         61:  
         62: ; Function Attrs: noinline uwtable 
         63: define void @f(ptr nofree noundef nonnull readonly captures(none) %x) unnamed_addr #3 !guid !11 { 
         64: start: 
         65:  tail call void %x() 
         66:  ret void 
         67: } 
         68:  
         69: ; Function Attrs: nounwind uwtable 
         70: declare noundef range(i32 0, 10) i32 @rust_eh_personality(i32 noundef, i32 noundef, i64 noundef, ptr noundef, ptr noundef) unnamed_addr #4 
         71:  
         72: ; Function Attrs: nounwind uwtable 
         73: declare void @side_effect_1() unnamed_addr #4 
         74:  
         75: ; Function Attrs: nounwind uwtable 
         76: declare void @side_effect_2() unnamed_addr #4 
         77:  
         78: ; Function Attrs: nounwind uwtable 
         79: declare void @side_effect_3() unnamed_addr #4 
         80:  
         81: attributes #0 = { inlinehint minsize nounwind optsize uwtable "frame-pointer"="non-leaf" "probe-stack"="inline-asm" "target-cpu"="apple-m1" } 
         82: attributes #1 = { noinline optnone uwtable "frame-pointer"="non-leaf" "probe-stack"="inline-asm" "target-cpu"="apple-m1" } 
         83: attributes #2 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable "frame-pointer"="non-leaf" "probe-stack"="inline-asm" "target-cpu"="apple-m1" } 
         84: attributes #3 = { noinline uwtable "frame-pointer"="non-leaf" "probe-stack"="inline-asm" "target-cpu"="apple-m1" } 
         85: attributes #4 = { nounwind uwtable "frame-pointer"="non-leaf" "probe-stack"="inline-asm" "target-cpu"="apple-m1" } 
         86: attributes #5 = { nounwind } 
         87:  
         88: !llvm.module.flags = !{!0, !1, !2} 
         89: !llvm.ident = !{!3} 
         90:  
         91: !0 = !{i32 8, !"PIC Level", i32 2} 
         92: !1 = !{i32 7, !"uwtable", i32 2} 
         93: !2 = !{i32 7, !"frame-pointer", i32 1} 
         94: !3 = !{!"rustc version 1.101.0-nightly (0f4573baa 2026-10-25)"} 
         95: !4 = !{i64 7745449567613243355} 
         96: !5 = !{i64 101661454626649503} 
         97: !6 = !{i64 -167988436488191286} 
         98: !7 = !{i64 -7716599440928436234} 
         99: !8 = !{i64 -2467921899225040083} 
        100: !9 = !{i64 -1344477224990069929} 
        101: !10 = !{i64 7698178979281266674} 
        102: !11 = !{i64 -3706093650706652785} 
dag:11'4                                       } search range end (exclusive)
>>>>>>

------------------------------------------

error: verification with 'FileCheck' failed
status: exit status: 1
command: "/Users/runner/work/rust/rust/build/aarch64-apple-darwin/llvm/bin/FileCheck" "--input-file" "/Users/runner/work/rust/rust/build/aarch64-apple-darwin/test/codegen-llvm/optimize-closure-shim/optimize-closure-shim.ll" "/Users/runner/work/rust/rust/tests/codegen-llvm/optimize-closure-shim.rs" "--check-prefix=CHECK" "--allow-unused-prefixes" "--dump-input-context" "100"
stdout: none
--- stderr -------------------------------
/Users/runner/work/rust/rust/tests/codegen-llvm/optimize-closure-shim.rs:11:15: error: CHECK-DAG: expected string not found in input
// CHECK-DAG: define{{.*}}void {{.*}}shim{{.*}}test_passed_directly{{.*}}(){{.*}}#[[ATTR_SIZE]]
              ^
/Users/runner/work/rust/rust/build/aarch64-apple-darwin/test/codegen-llvm/optimize-closure-shim/optimize-closure-shim.ll:8:171: note: scanning from here
define internal void @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim20test_passed_directly0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_() unnamed_addr #0 personality ptr @rust_eh_personality !guid !4 {
                                                                                                                                                                          ^
/Users/runner/work/rust/rust/build/aarch64-apple-darwin/test/codegen-llvm/optimize-closure-shim/optimize-closure-shim.ll:8:171: note: with "ATTR_SIZE" equal to "0"
define internal void @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim20test_passed_directly0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_() unnamed_addr #0 personality ptr @rust_eh_personality !guid !4 {
                                                                                                                                                                          ^
/Users/runner/work/rust/rust/build/aarch64-apple-darwin/test/codegen-llvm/optimize-closure-shim/optimize-closure-shim.ll:58:68: note: possible intended match here
 tail call void @f(ptr noundef nonnull @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim20test_passed_directly0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_)
                                                                   ^

Input file: /Users/runner/work/rust/rust/build/aarch64-apple-darwin/test/codegen-llvm/optimize-closure-shim/optimize-closure-shim.ll
Check file: /Users/runner/work/rust/rust/tests/codegen-llvm/optimize-closure-shim.rs

-dump-input=help explains the following input dump.

Input was:
<<<<<<
          1: ; ModuleID = 'optimize_closure_shim.4274e599c06735a1-cgu.0' 
          2: source_filename = "optimize_closure_shim.4274e599c06735a1-cgu.0" 
          3: target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" 
          4: target triple = "arm64-apple-macosx11.0.0" 
          5:  
          6: ; <optimize_closure_shim::test_passed_directly::{closure#0} as core::ops::function::FnOnce<()>>::call_once 
          7: ; Function Attrs: inlinehint minsize nounwind optsize uwtable 
          8: define internal void @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim20test_passed_directly0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_() unnamed_addr #0 personality ptr @rust_eh_personality !guid !4 { 
dag:11'0                                                                                                                                                                              {                                                   search range start (exclusive)
dag:11'1                                                                                                                                                                                                                                  error: no match found in search range
dag:11'2                                                                                                                                                                                                                                  with "ATTR_SIZE" equal to "0"
          9: start: 
         10:  tail call void @side_effect_3() #5 
         11:  ret void 
         12: } 
         13:  
         14: ; <optimize_closure_shim::test_none::{closure#0} as core::ops::function::FnOnce<()>>::call_once 
         15: ; Function Attrs: noinline optnone uwtable 
         16: define internal void @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim9test_none0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_() unnamed_addr #1 personality ptr @rust_eh_personality !guid !5 { 
         17: start: 
         18:  %_2 = alloca [0 x i8], align 1 
         19: ; call optimize_closure_shim::test_none::{closure#0} 
         20:  call fastcc void @_RNCNvCs5HKjKAbbNcr_21optimize_closure_shim9test_none0B3_() 
         21:  br label %bb1 
         22:  
         23: bb1: ; preds = %start 
         24:  ret void 
         25: } 
         26:  
         27: ; <optimize_closure_shim::test_size::{closure#0} as core::ops::function::FnOnce<()>>::call_once 
         28: ; Function Attrs: inlinehint minsize nounwind optsize uwtable 
         29: define internal void @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim9test_size0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_() unnamed_addr #0 personality ptr @rust_eh_personality !guid !6 { 
         30: start: 
         31:  tail call void @side_effect_2() #5 
         32:  ret void 
         33: } 
         34:  
         35: ; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable 
         36: define noundef nonnull ptr @test_none() unnamed_addr #2 !guid !7 { 
         37: start: 
         38:  ret ptr @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim9test_none0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_ 
         39: } 
         40:  
         41: ; optimize_closure_shim::test_none::{closure#0} 
         42: ; Function Attrs: noinline optnone uwtable 
         43: define internal fastcc void @_RNCNvCs5HKjKAbbNcr_21optimize_closure_shim9test_none0B3_() unnamed_addr #1 !guid !8 { 
         44: start: 
         45:  call void @side_effect_1() #5 
         46:  ret void 
         47: } 
         48:  
         49: ; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable 
         50: define noundef nonnull ptr @test_size() unnamed_addr #2 !guid !9 { 
         51: start: 
         52:  ret ptr @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim9test_size0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_ 
         53: } 
         54:  
         55: ; Function Attrs: noinline uwtable 
         56: define void @test_passed_directly() unnamed_addr #3 !guid !10 { 
         57: start: 
         58:  tail call void @f(ptr noundef nonnull @_RNvYNCNvCs5HKjKAbbNcr_21optimize_closure_shim20test_passed_directly0INtNtNtCsizeEtVvVzo7_4core3ops8function6FnOnceuE9call_onceB6_) 
dag:11'3                                                                        ?                                                                                                          possible intended match
         59:  ret void 
         60: } 
         61:  
         62: ; Function Attrs: noinline uwtable 
         63: define void @f(ptr nofree noundef nonnull readonly captures(none) %x) unnamed_addr #3 !guid !11 { 
         64: start: 
         65:  tail call void %x() 
         66:  ret void 
         67: } 
         68:  
         69: ; Function Attrs: nounwind uwtable 
         70: declare noundef range(i32 0, 10) i32 @rust_eh_personality(i32 noundef, i32 noundef, i64 noundef, ptr noundef, ptr noundef) unnamed_addr #4 
         71:  
         72: ; Function Attrs: nounwind uwtable 
         73: declare void @side_effect_1() unnamed_addr #4 
         74:  
         75: ; Function Attrs: nounwind uwtable 
         76: declare void @side_effect_2() unnamed_addr #4 
         77:  
         78: ; Function Attrs: nounwind uwtable 
         79: declare void @side_effect_3() unnamed_addr #4 
         80:  
         81: attributes #0 = { inlinehint minsize nounwind optsize uwtable "frame-pointer"="non-leaf" "probe-stack"="inline-asm" "target-cpu"="apple-m1" } 
         82: attributes #1 = { noinline optnone uwtable "frame-pointer"="non-leaf" "probe-stack"="inline-asm" "target-cpu"="apple-m1" } 
         83: attributes #2 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable "frame-pointer"="non-leaf" "probe-stack"="inline-asm" "target-cpu"="apple-m1" } 
         84: attributes #3 = { noinline uwtable "frame-pointer"="non-leaf" "probe-stack"="inline-asm" "target-cpu"="apple-m1" } 
         85: attributes #4 = { nounwind uwtable "frame-pointer"="non-leaf" "probe-stack"="inline-asm" "target-cpu"="apple-m1" } 
         86: attributes #5 = { nounwind } 
         87:  
         88: !llvm.module.flags = !{!0, !1, !2} 
         89: !llvm.ident = !{!3} 
         90:  
         91: !0 = !{i32 8, !"PIC Level", i32 2} 
         92: !1 = !{i32 7, !"uwtable", i32 2} 
         93: !2 = !{i32 7, !"frame-pointer", i32 1} 
         94: !3 = !{!"rustc version 1.101.0-nightly (0f4573baa 2026-10-25)"} 
         95: !4 = !{i64 7745449567613243355} 
         96: !5 = !{i64 101661454626649503} 
         97: !6 = !{i64 -167988436488191286} 
         98: !7 = !{i64 -7716599440928436234} 
         99: !8 = !{i64 -2467921899225040083} 
        100: !9 = !{i64 -1344477224990069929} 
        101: !10 = !{i64 7698178979281266674} 
        102: !11 = !{i64 -3706093650706652785} 
dag:11'4                                       } search range end (exclusive)
>>>>>>
------------------------------------------

---- [codegen] tests/codegen-llvm/optimize-closure-shim.rs stdout end ----

@rust-bors rust-bors Bot 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 Oct 25, 2026
@rust-bors

rust-bors Bot commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

💔 Test for 0f4573b failed: CI. Failed jobs:

@joboet

joboet commented Oct 28, 2026 •

Copy link
Copy Markdown
Member Author

I don't get why that test is failing – it's completely unrelated, and doesn't even fail when I run it locally. Is that known to be spurious?

Anyway, I'll mark this as ready for review.
@rustbot ready

@rust-lang rust-lang deleted a comment from rust-bors Bot Oct 28, 2026
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Oct 28, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

O-hermit Operating System: Hermit 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. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants