]> git.lizzy.rs Git - rust.git/commitdiff
Do not allow LLVM to increase a TLS's alignment on macOS.
authorkennytm <kennytm@gmail.com>
Wed, 27 Jun 2018 22:24:09 +0000 (06:24 +0800)
committerkennytm <kennytm@gmail.com>
Sat, 30 Jun 2018 13:36:03 +0000 (21:36 +0800)
src/librustc_codegen_llvm/consts.rs
src/librustc_codegen_llvm/mir/constant.rs
src/test/codegen/issue-44056-macos-tls-align.rs [new file with mode: 0644]
src/test/run-pass/issue-44056.rs [new file with mode: 0644]

index afa81465ea2d1b78c362c09a55eb4a81203c74e3..199c40bb704ea3d62ec2e10e469b5a5a4bae5a94 100644 (file)
@@ -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);
index bbe0e34b48f2642bb1754c9b801b7c17de0250d2..d7939bd2ab224d547f35705e0957de4a9d80c696 100644 (file)
@@ -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<ValueRef, Lrc<ConstEvalErr<'tcx>>>
+    -> Result<(ValueRef, &'tcx Allocation), Lrc<ConstEvalErr<'tcx>>>
 {
     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 (file)
index 0000000..3235ef0
--- /dev/null
@@ -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 <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.
+
+// 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 (file)
index 0000000..dcaa0bf
--- /dev/null
@@ -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 <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.
+
+// only-x86_64
+// no-prefer-dynamic
+// compile-flags: -Ctarget-feature=+avx -Clto
+
+fn main() {}