-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) { | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
eddyb
Member
|
||
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); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() {} |
FWIW, this introduced an extremely subtle bug: even if all the "bytes" are 0, there might still be pointers in this allocation and then the 0 just means the offset is 0, but a relocation needs to be emitted.
See #62655 (comment).
Cc @oli-obk @eddyb @kennytm @alexcrichton