Skip to content

std: Run TLS destructors in a statically linked binary #28130

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

Merged
merged 1 commit into from
Sep 1, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions src/libstd/sys/windows/thread_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,24 @@ unsafe fn unregister_dtor(key: Key) -> bool {
//
// # The article mentions crazy stuff about "/INCLUDE"?
//
// It sure does! We include it below for MSVC targets, but it look like for GNU
// targets we don't require it.
// It sure does! Specifically we're talking about this quote:
//
// The Microsoft run-time library facilitates this process by defining a
// memory image of the TLS Directory and giving it the special name
// “__tls_used” (Intel x86 platforms) or “_tls_used” (other platforms). The
// linker looks for this memory image and uses the data there to create the
// TLS Directory. Other compilers that support TLS and work with the
// Microsoft linker must use this same technique.
//
// Basically what this means is that if we want support for our TLS
// destructors/our hook being called then we need to make sure the linker does
// not omit this symbol. Otherwise it will omit it and our callback won't be
// wired up.
//
// We don't actually use the `/INCLUDE` linker flag here like the article
// mentions because the Rust compiler doesn't propagate linker flags, but
// instead we use a shim function which performs a volatile 1-byte load from
// the address of the symbol to ensure it sticks around.

#[link_section = ".CRT$XLB"]
#[linkage = "external"]
Expand All @@ -231,13 +247,6 @@ pub static p_thread_callback: unsafe extern "system" fn(LPVOID, DWORD,
LPVOID) =
on_tls_callback;

#[cfg(all(target_env = "msvc", target_pointer_width = "64"))]
#[link_args = "/INCLUDE:_tls_used"]
extern {}
#[cfg(all(target_env = "msvc", target_pointer_width = "32"))]
#[link_args = "/INCLUDE:__tls_used"]
extern {}

#[allow(warnings)]
unsafe extern "system" fn on_tls_callback(h: LPVOID,
dwReason: DWORD,
Expand All @@ -247,6 +256,17 @@ unsafe extern "system" fn on_tls_callback(h: LPVOID,
if dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH {
run_dtors();
}

// See comments above for what this is doing. Note that we don't need this
// trickery on GNU windows, just on MSVC.
reference_tls_used();
#[cfg(target_env = "msvc")]
unsafe fn reference_tls_used() {
extern { static _tls_used: u8; }
::intrinsics::volatile_load(&_tls_used);
}
#[cfg(not(target_env = "msvc"))]
unsafe fn reference_tls_used() {}
}

#[allow(dead_code)] // actually called above
Expand Down
9 changes: 9 additions & 0 deletions src/test/run-pass/down-with-thread-dtors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ thread_local!(static FOO: Foo = Foo);
thread_local!(static BAR: Bar = Bar(1));
thread_local!(static BAZ: Baz = Baz);

static mut HIT: bool = false;

struct Foo;
struct Bar(i32);
struct Baz;
Expand All @@ -31,8 +33,15 @@ impl Drop for Bar {
}
}

impl Drop for Baz {
fn drop(&mut self) {
unsafe { HIT = true; }
}
}

fn main() {
std::thread::spawn(|| {
FOO.with(|_| {});
}).join().unwrap();
assert!(unsafe { HIT });
}
30 changes: 30 additions & 0 deletions src/test/run-pass/tls-dtors-are-run-in-a-static-binary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// no-prefer-dynamic

static mut HIT: bool = false;

struct Foo;

impl Drop for Foo {
fn drop(&mut self) {
unsafe { HIT = true; }
}
}

thread_local!(static FOO: Foo = Foo);

fn main() {
std::thread::spawn(|| {
FOO.with(|_| {});
}).join().unwrap();
assert!(unsafe { HIT });
}