Change:
This appears to be a MinGW-w64 linker bug.
Triage: I am not sure if there has been any change, nor steps to reproduce.
If this was a mingw-specific bug, we may be able to reactivate it on the msvc builds.
According to so we've already been effectively using ASLR with targets.
Can someone comment on whether thread locals are broken on Windows MinGW-w64 today? What are the reproduction steps?
I've done a cursory check of TLS with 64 bit binaries compiled with msvc and mingw32-g++ and both are working correctly. msvc output: Rage counter for a: 2 Rage counter for b: 2 Rage counter for main: 1 mingw32-g++ output: Rage counter for main: 1 Rage counter for a: 2 Rage counter for b: 2 64 bit binaries on Windows use ASLR by default unless explicitly disabled with a linker option. Both binaries in this test had the NX Compatible flag set in the executable image header. Happy to test further if anyone has a pointer to the problem code.
The key thing is statics in Rust. The NX compatible bit does not indicate ASLR, rather it needs to have the Dynamic base bit.
Does anybody have reproducer? %5D%0Afn%20main()%20%7B%0Ause%20std%3A%3Acell%3A%3ARefCell%3B%0Ause%20std%3A%3Athread%3B%0A%0Athreadlocal!(static%20FOO%3A%20RefCell%3Cu32%3E%20%3D%20RefCell%3A%3Anew(1))(%7Cf%7C%20%7B%0A%20%20%20%20asserteq!(f.borrow()%2C%201)%3B%0A%20%20%20%20f.borrowmut()%20%3D%202%3B%0A%7D)%3B%0A%0A%2F%2F%20each%20thread%20starts%20out%20with%20the%20initial%20value%20of%201%0Alet%20t%20%3D%20thread%3A%3Aspawn((%7Cf%7C%20%7B%0A%20%20%20%20%20%20%20%20asserteq!(f.borrow()%2C%201)%3B%0A%20%20%20%20%20%20%20%20f.borrowmut()%20%3D%203%3B%0A%20%20%20%20%7D)%3B%0A%7D)().unwrap()(%7Cf%7C%20%7B%0A%20%20%20%20asserteq!(*f.borrow()%2C%202)%3B%0A%7D)%3B%0A%7D&edition=2018) still works for me after enabling ASLR.

Discussion:
"-fno-use-linker-plugin".to_string(), // Always enable DEP (NX bit) when it is available "-Wl,--nxcompat".to_string(), <ins> // Enable ASLR "-Wl,--dynamicbase".to_string(), // ASLR will rebase it anyway so leaving that option enabled only leads to confusion "-Wl,--disable-auto-image-base".to_string(), </ins> ], );

pub fn target() -> TargetResult { let mut base = super::windows_gnu_base::opts(); base.cpu = "x86-64".to_string(); <del> base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); </del> <ins> let gcc_pre_link_args = base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); gcc_pre_link_args.push("-m64".to_string()); // Use high-entropy 64 bit address space for ASLR gcc_pre_link_args.push("-Wl,--high-entropy-va".to_string()); </ins> base.pre_link_args .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".to_string(), "i386pep".to_string()]); base.max_atomic_width = Some(64);

pub fn target() -> TargetResult { let mut base = super::windows_uwp_gnu_base::opts(); base.cpu = "x86-64".to_string(); <del> base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); </del> <ins> let gcc_pre_link_args = base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); gcc_pre_link_args.push("-m64".to_string()); // Use high-entropy 64 bit address space for ASLR gcc_pre_link_args.push("-Wl,--high-entropy-va".to_string()); </ins> base.pre_link_args .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".to_string(), "i386pep".to_string()]); base.max_atomic_width = Some(64);