Skip to content

Commit

Permalink
Do not allow LLVM to increase a TLS's alignment on macOS.
Browse files Browse the repository at this point in the history
  • Loading branch information
kennytm authored and pietroalbini committed Jul 5, 2018
1 parent 84607c8 commit 272e4f0
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 3 deletions.
40 changes: 39 additions & 1 deletion src/librustc_codegen_llvm/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_codegen_llvm/mir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ValueRef, ConstEvalErr<'tcx>>
-> Result<(ValueRef, &'tcx Allocation), ConstEvalErr<'tcx>>
{
let instance = ty::Instance::mono(cx.tcx, def_id);
let cid = GlobalId {
Expand All @@ -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> {
Expand Down
40 changes: 40 additions & 0 deletions src/test/codegen/issue-44056-macos-tls-align.rs
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)
}
15 changes: 15 additions & 0 deletions src/test/run-pass/issue-44056.rs
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() {}

0 comments on commit 272e4f0

Please sign in to comment.