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 7c1035e2fcb88..a640a02ece2fc 100644 --- a/src/librustc_codegen_llvm/mir/constant.rs +++ b/src/librustc_codegen_llvm/mir/constant.rs @@ -117,7 +117,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), ConstEvalErr<'tcx>> { let instance = ty::Instance::mono(cx.tcx, def_id); let cid = GlobalId { @@ -131,7 +131,7 @@ pub fn codegen_static_initializer<'a, 'tcx>( ConstVal::Value(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..362d0a3c1071b --- /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 8 +#[no_mangle] +#[allow(private_no_mangle_statics)] +#[thread_local] +static mut STATIC_VAR_1: [u64; 4] = [0; 4]; + +// CHECK: @STATIC_VAR_2 = internal thread_local unnamed_addr global <{ [32 x i8] }> <{{[^>]*}}>, section "__DATA,__thread_data", align 8 +#[no_mangle] +#[allow(private_no_mangle_statics)] +#[thread_local] +static mut STATIC_VAR_2: [u64; 4] = [4; 4]; + +#[no_mangle] +pub unsafe fn f(x: &mut [u64; 4]) { + std::mem::swap(x, &mut STATIC_VAR_1) +} + +#[no_mangle] +pub unsafe fn g(x: &mut [u64; 4]) { + std::mem::swap(x, &mut STATIC_VAR_2) +}