Skip to content

Commit 272e4f0

Browse files
kennytmpietroalbini
authored andcommitted
Do not allow LLVM to increase a TLS's alignment on macOS.
1 parent 84607c8 commit 272e4f0

File tree

4 files changed

+96
-3
lines changed

4 files changed

+96
-3
lines changed

src/librustc_codegen_llvm/consts.rs

+39-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ pub fn codegen_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
250250
unsafe {
251251
let g = get_static(cx, def_id);
252252

253-
let v = match ::mir::codegen_static_initializer(cx, def_id) {
253+
let (v, alloc) = match ::mir::codegen_static_initializer(cx, def_id) {
254254
Ok(v) => v,
255255
// Error has already been reported
256256
Err(_) => return,
@@ -309,6 +309,44 @@ pub fn codegen_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
309309

310310
if attr::contains_name(attrs, "thread_local") {
311311
llvm::set_thread_local_mode(g, cx.tls_model);
312+
313+
// Do not allow LLVM to change the alignment of a TLS on macOS.
314+
//
315+
// By default a global's alignment can be freely increased.
316+
// This allows LLVM to generate more performant instructions
317+
// e.g. using load-aligned into a SIMD register.
318+
//
319+
// However, on macOS 10.10 or below, the dynamic linker does not
320+
// respect any alignment given on the TLS (radar 24221680).
321+
// This will violate the alignment assumption, and causing segfault at runtime.
322+
//
323+
// This bug is very easy to trigger. In `println!` and `panic!`,
324+
// the `LOCAL_STDOUT`/`LOCAL_STDERR` handles are stored in a TLS,
325+
// which the values would be `mem::replace`d on initialization.
326+
// The implementation of `mem::replace` will use SIMD
327+
// whenever the size is 32 bytes or higher. LLVM notices SIMD is used
328+
// and tries to align `LOCAL_STDOUT`/`LOCAL_STDERR` to a 32-byte boundary,
329+
// which macOS's dyld disregarded and causing crashes
330+
// (see issues #51794, #51758, #50867, #48866 and #44056).
331+
//
332+
// To workaround the bug, we trick LLVM into not increasing
333+
// the global's alignment by explicitly assigning a section to it
334+
// (equivalent to automatically generating a `#[link_section]` attribute).
335+
// See the comment in the `GlobalValue::canIncreaseAlignment()` function
336+
// of `lib/IR/Globals.cpp` for why this works.
337+
//
338+
// When the alignment is not increased, the optimized `mem::replace`
339+
// will use load-unaligned instructions instead, and thus avoiding the crash.
340+
//
341+
// We could remove this hack whenever we decide to drop macOS 10.10 support.
342+
if cx.tcx.sess.target.target.options.is_like_osx {
343+
let sect_name = if alloc.bytes.iter().all(|b| *b == 0) {
344+
CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_bss\0")
345+
} else {
346+
CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_data\0")
347+
};
348+
llvm::LLVMSetSection(g, sect_name.as_ptr());
349+
}
312350
}
313351

314352
base::set_link_section(cx, g, attrs);

src/librustc_codegen_llvm/mir/constant.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx, alloc: &Allocation) -> ValueRef {
117117
pub fn codegen_static_initializer<'a, 'tcx>(
118118
cx: &CodegenCx<'a, 'tcx>,
119119
def_id: DefId)
120-
-> Result<ValueRef, ConstEvalErr<'tcx>>
120+
-> Result<(ValueRef, &'tcx Allocation), ConstEvalErr<'tcx>>
121121
{
122122
let instance = ty::Instance::mono(cx.tcx, def_id);
123123
let cid = GlobalId {
@@ -131,7 +131,7 @@ pub fn codegen_static_initializer<'a, 'tcx>(
131131
ConstVal::Value(ConstValue::ByRef(alloc, n)) if n.bytes() == 0 => alloc,
132132
_ => bug!("static const eval returned {:#?}", static_),
133133
};
134-
Ok(const_alloc_to_llvm(cx, alloc))
134+
Ok((const_alloc_to_llvm(cx, alloc), alloc))
135135
}
136136

137137
impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-tidy-linelength
12+
// only-macos
13+
// no-system-llvm
14+
// min-llvm-version 6.0
15+
// compile-flags: -O
16+
17+
#![crate_type = "rlib"]
18+
#![feature(thread_local)]
19+
20+
// CHECK: @STATIC_VAR_1 = internal thread_local unnamed_addr global <{ [32 x i8] }> zeroinitializer, section "__DATA,__thread_bss", align 4
21+
#[no_mangle]
22+
#[allow(private_no_mangle_statics)]
23+
#[thread_local]
24+
static mut STATIC_VAR_1: [u32; 8] = [0; 8];
25+
26+
// CHECK: @STATIC_VAR_2 = internal thread_local unnamed_addr global <{ [32 x i8] }> <{{[^>]*}}>, section "__DATA,__thread_data", align 4
27+
#[no_mangle]
28+
#[allow(private_no_mangle_statics)]
29+
#[thread_local]
30+
static mut STATIC_VAR_2: [u32; 8] = [4; 8];
31+
32+
#[no_mangle]
33+
pub unsafe fn f(x: &mut [u32; 8]) {
34+
std::mem::swap(x, &mut STATIC_VAR_1)
35+
}
36+
37+
#[no_mangle]
38+
pub unsafe fn g(x: &mut [u32; 8]) {
39+
std::mem::swap(x, &mut STATIC_VAR_2)
40+
}

src/test/run-pass/issue-44056.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// only-x86_64
12+
// no-prefer-dynamic
13+
// compile-flags: -Ctarget-feature=+avx -Clto
14+
15+
fn main() {}

0 commit comments

Comments
 (0)