diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs index afa81465ea2d1..199c40bb704ea 100644 --- a/src/librustc_codegen_llvm/consts.rs +++ b/src/librustc_codegen_llvm/consts.rs @@ -250,7 +250,7 @@ pub fn codegen_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, unsafe { let g = get_static(cx, def_id); - let v = match ::mir::codegen_static_initializer(cx, def_id) { + let (v, alloc) = match ::mir::codegen_static_initializer(cx, def_id) { Ok(v) => v, // Error has already been reported Err(_) => return, @@ -309,6 +309,44 @@ pub fn codegen_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, if attr::contains_name(attrs, "thread_local") { llvm::set_thread_local_mode(g, cx.tls_model); + + // Do not allow LLVM to change the alignment of a TLS on macOS. + // + // By default a global's alignment can be freely increased. + // This allows LLVM to generate more performant instructions + // e.g. using load-aligned into a SIMD register. + // + // However, on macOS 10.10 or below, the dynamic linker does not + // respect any alignment given on the TLS (radar 24221680). + // This will violate the alignment assumption, and causing segfault at runtime. + // + // This bug is very easy to trigger. In `println!` and `panic!`, + // the `LOCAL_STDOUT`/`LOCAL_STDERR` handles are stored in a TLS, + // which the values would be `mem::replace`d on initialization. + // The implementation of `mem::replace` will use SIMD + // whenever the size is 32 bytes or higher. LLVM notices SIMD is used + // and tries to align `LOCAL_STDOUT`/`LOCAL_STDERR` to a 32-byte boundary, + // which macOS's dyld disregarded and causing crashes + // (see issues #51794, #51758, #50867, #48866 and #44056). + // + // To workaround the bug, we trick LLVM into not increasing + // the global's alignment by explicitly assigning a section to it + // (equivalent to automatically generating a `#[link_section]` attribute). + // See the comment in the `GlobalValue::canIncreaseAlignment()` function + // of `lib/IR/Globals.cpp` for why this works. + // + // When the alignment is not increased, the optimized `mem::replace` + // will use load-unaligned instructions instead, and thus avoiding the crash. + // + // We could remove this hack whenever we decide to drop macOS 10.10 support. + if cx.tcx.sess.target.target.options.is_like_osx { + let sect_name = if alloc.bytes.iter().all(|b| *b == 0) { + CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_bss\0") + } else { + CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_data\0") + }; + llvm::LLVMSetSection(g, sect_name.as_ptr()); + } } base::set_link_section(cx, g, attrs); diff --git a/src/librustc_codegen_llvm/mir/constant.rs b/src/librustc_codegen_llvm/mir/constant.rs index bbe0e34b48f26..d7939bd2ab224 100644 --- a/src/librustc_codegen_llvm/mir/constant.rs +++ b/src/librustc_codegen_llvm/mir/constant.rs @@ -118,7 +118,7 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx, alloc: &Allocation) -> ValueRef { pub fn codegen_static_initializer<'a, 'tcx>( cx: &CodegenCx<'a, 'tcx>, def_id: DefId) - -> Result>> + -> Result<(ValueRef, &'tcx Allocation), Lrc>> { let instance = ty::Instance::mono(cx.tcx, def_id); let cid = GlobalId { @@ -132,7 +132,7 @@ pub fn codegen_static_initializer<'a, 'tcx>( ConstValue::ByRef(alloc, n) if n.bytes() == 0 => alloc, _ => bug!("static const eval returned {:#?}", static_), }; - Ok(const_alloc_to_llvm(cx, alloc)) + Ok((const_alloc_to_llvm(cx, alloc), alloc)) } impl<'a, 'tcx> FunctionCx<'a, 'tcx> { diff --git a/src/test/codegen/issue-44056-macos-tls-align.rs b/src/test/codegen/issue-44056-macos-tls-align.rs new file mode 100644 index 0000000000000..3235ef0bb3335 --- /dev/null +++ b/src/test/codegen/issue-44056-macos-tls-align.rs @@ -0,0 +1,40 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// only-macos +// no-system-llvm +// min-llvm-version 6.0 +// compile-flags: -O + +#![crate_type = "rlib"] +#![feature(thread_local)] + +// CHECK: @STATIC_VAR_1 = internal thread_local unnamed_addr global <{ [32 x i8] }> zeroinitializer, section "__DATA,__thread_bss", align 4 +#[no_mangle] +#[allow(private_no_mangle_statics)] +#[thread_local] +static mut STATIC_VAR_1: [u32; 8] = [0; 8]; + +// CHECK: @STATIC_VAR_2 = internal thread_local unnamed_addr global <{ [32 x i8] }> <{{[^>]*}}>, section "__DATA,__thread_data", align 4 +#[no_mangle] +#[allow(private_no_mangle_statics)] +#[thread_local] +static mut STATIC_VAR_2: [u32; 8] = [4; 8]; + +#[no_mangle] +pub unsafe fn f(x: &mut [u32; 8]) { + std::mem::swap(x, &mut STATIC_VAR_1) +} + +#[no_mangle] +pub unsafe fn g(x: &mut [u32; 8]) { + std::mem::swap(x, &mut STATIC_VAR_2) +} diff --git a/src/test/run-pass/issue-44056.rs b/src/test/run-pass/issue-44056.rs new file mode 100644 index 0000000000000..dcaa0bf86294a --- /dev/null +++ b/src/test/run-pass/issue-44056.rs @@ -0,0 +1,15 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// only-x86_64 +// no-prefer-dynamic +// compile-flags: -Ctarget-feature=+avx -Clto + +fn main() {}