1
Fork 0

Add emulated TLS support

Currently LLVM uses emutls by default
for some targets (such as android, openbsd),
but rust does not use it, because `has_thread_local` is false.

This commit has some changes to allow users to enable emutls:

1. add `-Zhas-thread-local` flag to specify
    that std uses `#[thread_local]` instead of pthread key.
2. when using emutls, decorate symbol names
    to find thread local symbol correctly.
3. change `-Zforce-emulated-tls` to `-Ztls-model=emulated`
    to explicitly specify whether to generate emutls.
This commit is contained in:
quininer 2023-11-13 20:48:23 +08:00
parent 8a7b2035f8
commit e5b76892cc
17 changed files with 71 additions and 28 deletions

View file

@ -569,5 +569,6 @@ fn to_gcc_tls_mode(tls_model: TlsModel) -> gccjit::TlsModel {
TlsModel::LocalDynamic => gccjit::TlsModel::LocalDynamic, TlsModel::LocalDynamic => gccjit::TlsModel::LocalDynamic,
TlsModel::InitialExec => gccjit::TlsModel::InitialExec, TlsModel::InitialExec => gccjit::TlsModel::InitialExec,
TlsModel::LocalExec => gccjit::TlsModel::LocalExec, TlsModel::LocalExec => gccjit::TlsModel::LocalExec,
TlsModel::Emulated => gccjit::TlsModel::GlobalDynamic,
} }
} }

View file

@ -39,7 +39,7 @@ impl OwnedTargetMachine {
split_dwarf_file: &CStr, split_dwarf_file: &CStr,
output_obj_file: &CStr, output_obj_file: &CStr,
debug_info_compression: &CStr, debug_info_compression: &CStr,
force_emulated_tls: bool, use_emulated_tls: bool,
args_cstr_buff: &[u8], args_cstr_buff: &[u8],
) -> Result<Self, LlvmError<'static>> { ) -> Result<Self, LlvmError<'static>> {
assert!(args_cstr_buff.len() > 0); assert!(args_cstr_buff.len() > 0);
@ -71,7 +71,7 @@ impl OwnedTargetMachine {
split_dwarf_file.as_ptr(), split_dwarf_file.as_ptr(),
output_obj_file.as_ptr(), output_obj_file.as_ptr(),
debug_info_compression.as_ptr(), debug_info_compression.as_ptr(),
force_emulated_tls, use_emulated_tls,
args_cstr_buff.as_ptr() as *const c_char, args_cstr_buff.as_ptr() as *const c_char,
args_cstr_buff.len(), args_cstr_buff.len(),
) )

View file

@ -33,7 +33,7 @@ use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, Switc
use rustc_session::Session; use rustc_session::Session;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::InnerSpan; use rustc_span::InnerSpan;
use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo}; use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel};
use crate::llvm::diagnostic::OptimizationDiagnosticKind; use crate::llvm::diagnostic::OptimizationDiagnosticKind;
use libc::{c_char, c_int, c_uint, c_void, size_t}; use libc::{c_char, c_int, c_uint, c_void, size_t};
@ -223,7 +223,7 @@ pub fn target_machine_factory(
let path_mapping = sess.source_map().path_mapping().clone(); let path_mapping = sess.source_map().path_mapping().clone();
let force_emulated_tls = sess.target.force_emulated_tls; let use_emulated_tls = matches!(sess.tls_model(), TlsModel::Emulated);
// copy the exe path, followed by path all into one buffer // copy the exe path, followed by path all into one buffer
// null terminating them so we can use them as null terminated strings // null terminating them so we can use them as null terminated strings
@ -297,7 +297,7 @@ pub fn target_machine_factory(
&split_dwarf_file, &split_dwarf_file,
&output_obj_file, &output_obj_file,
&debuginfo_compression, &debuginfo_compression,
force_emulated_tls, use_emulated_tls,
&args_cstr_buff, &args_cstr_buff,
) )
}) })

View file

@ -120,6 +120,7 @@ fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode {
TlsModel::LocalDynamic => llvm::ThreadLocalMode::LocalDynamic, TlsModel::LocalDynamic => llvm::ThreadLocalMode::LocalDynamic,
TlsModel::InitialExec => llvm::ThreadLocalMode::InitialExec, TlsModel::InitialExec => llvm::ThreadLocalMode::InitialExec,
TlsModel::LocalExec => llvm::ThreadLocalMode::LocalExec, TlsModel::LocalExec => llvm::ThreadLocalMode::LocalExec,
TlsModel::Emulated => llvm::ThreadLocalMode::GeneralDynamic,
} }
} }

View file

@ -306,7 +306,9 @@ impl CodegenBackend for LlvmCodegenBackend {
} }
PrintKind::TlsModels => { PrintKind::TlsModels => {
writeln!(out, "Available TLS models:"); writeln!(out, "Available TLS models:");
for name in &["global-dynamic", "local-dynamic", "initial-exec", "local-exec"] { for name in
&["global-dynamic", "local-dynamic", "initial-exec", "local-exec", "emulated"]
{
writeln!(out, " {name}"); writeln!(out, " {name}");
} }
writeln!(out); writeln!(out);

View file

@ -2159,7 +2159,7 @@ extern "C" {
SplitDwarfFile: *const c_char, SplitDwarfFile: *const c_char,
OutputObjFile: *const c_char, OutputObjFile: *const c_char,
DebugInfoCompression: *const c_char, DebugInfoCompression: *const c_char,
ForceEmulatedTls: bool, UseEmulatedTls: bool,
ArgsCstrBuff: *const c_char, ArgsCstrBuff: *const c_char,
ArgsCstrBuffLen: usize, ArgsCstrBuffLen: usize,
) -> *mut TargetMachine; ) -> *mut TargetMachine;

View file

@ -1748,7 +1748,9 @@ fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
if info.level.is_below_threshold(export_threshold) { if info.level.is_below_threshold(export_threshold) {
symbols.push(symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum)); symbols.push(symbol_export::exporting_symbol_name_for_instance_in_crate(
tcx, symbol, cnum,
));
} }
}); });

View file

@ -16,7 +16,7 @@ use rustc_middle::ty::{self, SymbolName, TyCtxt};
use rustc_middle::ty::{GenericArgKind, GenericArgsRef}; use rustc_middle::ty::{GenericArgKind, GenericArgsRef};
use rustc_middle::util::Providers; use rustc_middle::util::Providers;
use rustc_session::config::{CrateType, OomStrategy}; use rustc_session::config::{CrateType, OomStrategy};
use rustc_target::spec::SanitizerSet; use rustc_target::spec::{SanitizerSet, TlsModel};
pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel { pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
crates_export_threshold(tcx.crate_types()) crates_export_threshold(tcx.crate_types())
@ -552,6 +552,12 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
let mut undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate); let mut undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate);
// thread local will not be a function call,
// so it is safe to return before windows symbol decoration check.
if let Some(name) = maybe_emutls_symbol_name(tcx, symbol, &undecorated) {
return name;
}
let target = &tcx.sess.target; let target = &tcx.sess.target;
if !target.is_like_windows { if !target.is_like_windows {
// Mach-O has a global "_" suffix and `object` crate will handle it. // Mach-O has a global "_" suffix and `object` crate will handle it.
@ -612,6 +618,32 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
format!("{prefix}{undecorated}{suffix}{args_in_bytes}") format!("{prefix}{undecorated}{suffix}{args_in_bytes}")
} }
pub fn exporting_symbol_name_for_instance_in_crate<'tcx>(
tcx: TyCtxt<'tcx>,
symbol: ExportedSymbol<'tcx>,
cnum: CrateNum,
) -> String {
let undecorated = symbol_name_for_instance_in_crate(tcx, symbol, cnum);
maybe_emutls_symbol_name(tcx, symbol, &undecorated).unwrap_or(undecorated)
}
fn maybe_emutls_symbol_name<'tcx>(
tcx: TyCtxt<'tcx>,
symbol: ExportedSymbol<'tcx>,
undecorated: &str,
) -> Option<String> {
if matches!(tcx.sess.tls_model(), TlsModel::Emulated)
&& let ExportedSymbol::NonGeneric(def_id) = symbol
&& tcx.is_thread_local_static(def_id)
{
// When using emutls, LLVM will add the `__emutls_v.` prefix to thread local symbols,
// and exported symbol name need to match this.
Some(format!("__emutls_v.{undecorated}"))
} else {
None
}
}
fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> FxHashMap<DefId, String> { fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> FxHashMap<DefId, String> {
// Build up a map from DefId to a `NativeLib` structure, where // Build up a map from DefId to a `NativeLib` structure, where
// `NativeLib` internally contains information about // `NativeLib` internally contains information about

View file

@ -410,7 +410,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
const char *SplitDwarfFile, const char *SplitDwarfFile,
const char *OutputObjFile, const char *OutputObjFile,
const char *DebugInfoCompression, const char *DebugInfoCompression,
bool ForceEmulatedTls, bool UseEmulatedTls,
const char *ArgsCstrBuff, size_t ArgsCstrBuffLen) { const char *ArgsCstrBuff, size_t ArgsCstrBuffLen) {
auto OptLevel = fromRust(RustOptLevel); auto OptLevel = fromRust(RustOptLevel);
@ -456,13 +456,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
Options.UseInitArray = UseInitArray; Options.UseInitArray = UseInitArray;
#if LLVM_VERSION_LT(17, 0) #if LLVM_VERSION_LT(17, 0)
if (ForceEmulatedTls) { Options.ExplicitEmulatedTLS = true;
Options.ExplicitEmulatedTLS = true;
Options.EmulatedTLS = true;
}
#else
Options.EmulatedTLS = ForceEmulatedTls || Trip.hasDefaultEmulatedTLS();
#endif #endif
Options.EmulatedTLS = UseEmulatedTls;
if (TrapUnreachable) { if (TrapUnreachable) {
// Tell LLVM to codegen `unreachable` into an explicit trap instruction. // Tell LLVM to codegen `unreachable` into an explicit trap instruction.

View file

@ -1283,7 +1283,7 @@ fn default_configuration(sess: &Session) -> Cfg {
ret.insert((sym::relocation_model, Some(relocation_model))); ret.insert((sym::relocation_model, Some(relocation_model)));
} }
ret.insert((sym::target_vendor, Some(Symbol::intern(vendor)))); ret.insert((sym::target_vendor, Some(Symbol::intern(vendor))));
if sess.target.has_thread_local { if sess.opts.unstable_opts.has_thread_local.unwrap_or(sess.target.has_thread_local) {
ret.insert((sym::target_thread_local, None)); ret.insert((sym::target_thread_local, None));
} }
let mut has_atomic = false; let mut has_atomic = false;

View file

@ -1624,6 +1624,8 @@ options! {
graphviz_font: String = ("Courier, monospace".to_string(), parse_string, [UNTRACKED], graphviz_font: String = ("Courier, monospace".to_string(), parse_string, [UNTRACKED],
"use the given `fontname` in graphviz output; can be overridden by setting \ "use the given `fontname` in graphviz output; can be overridden by setting \
environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"), environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"),
has_thread_local: Option<bool> = (None, parse_opt_bool, [TRACKED],
"explicitly enable the `cfg(target_thread_local)` directive"),
hir_stats: bool = (false, parse_bool, [UNTRACKED], hir_stats: bool = (false, parse_bool, [UNTRACKED],
"print some statistics about AST and HIR (default: no)"), "print some statistics about AST and HIR (default: no)"),
human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], human_readable_cgu_names: bool = (false, parse_bool, [TRACKED],

View file

@ -1,10 +1,11 @@
use crate::spec::{base, SanitizerSet, TargetOptions}; use crate::spec::{base, SanitizerSet, TargetOptions, TlsModel};
pub fn opts() -> TargetOptions { pub fn opts() -> TargetOptions {
let mut base = base::linux::opts(); let mut base = base::linux::opts();
base.os = "android".into(); base.os = "android".into();
base.is_like_android = true; base.is_like_android = true;
base.default_dwarf_version = 2; base.default_dwarf_version = 2;
base.tls_model = TlsModel::Emulated;
base.has_thread_local = false; base.has_thread_local = false;
base.supported_sanitizers = SanitizerSet::ADDRESS; base.supported_sanitizers = SanitizerSet::ADDRESS;
// This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867 // This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867

View file

@ -1,11 +1,11 @@
use crate::spec::{base, TargetOptions}; use crate::spec::{base, TargetOptions, TlsModel};
pub fn opts() -> TargetOptions { pub fn opts() -> TargetOptions {
let mut base = base::linux::opts(); let mut base = base::linux::opts();
base.env = "ohos".into(); base.env = "ohos".into();
base.crt_static_default = false; base.crt_static_default = false;
base.force_emulated_tls = true; base.tls_model = TlsModel::Emulated;
base.has_thread_local = false; base.has_thread_local = false;
base base

View file

@ -1,4 +1,4 @@
use crate::spec::{cvs, FramePointer, RelroLevel, TargetOptions}; use crate::spec::{cvs, FramePointer, RelroLevel, TargetOptions, TlsModel};
pub fn opts() -> TargetOptions { pub fn opts() -> TargetOptions {
TargetOptions { TargetOptions {
@ -11,6 +11,7 @@ pub fn opts() -> TargetOptions {
frame_pointer: FramePointer::Always, // FIXME 43575: should be MayOmit... frame_pointer: FramePointer::Always, // FIXME 43575: should be MayOmit...
relro_level: RelroLevel::Full, relro_level: RelroLevel::Full,
default_dwarf_version: 2, default_dwarf_version: 2,
tls_model: TlsModel::Emulated,
..Default::default() ..Default::default()
} }
} }

View file

@ -929,6 +929,7 @@ pub enum TlsModel {
LocalDynamic, LocalDynamic,
InitialExec, InitialExec,
LocalExec, LocalExec,
Emulated,
} }
impl FromStr for TlsModel { impl FromStr for TlsModel {
@ -942,6 +943,7 @@ impl FromStr for TlsModel {
"local-dynamic" => TlsModel::LocalDynamic, "local-dynamic" => TlsModel::LocalDynamic,
"initial-exec" => TlsModel::InitialExec, "initial-exec" => TlsModel::InitialExec,
"local-exec" => TlsModel::LocalExec, "local-exec" => TlsModel::LocalExec,
"emulated" => TlsModel::Emulated,
_ => return Err(()), _ => return Err(()),
}) })
} }
@ -954,6 +956,7 @@ impl ToJson for TlsModel {
TlsModel::LocalDynamic => "local-dynamic", TlsModel::LocalDynamic => "local-dynamic",
TlsModel::InitialExec => "initial-exec", TlsModel::InitialExec => "initial-exec",
TlsModel::LocalExec => "local-exec", TlsModel::LocalExec => "local-exec",
TlsModel::Emulated => "emulated",
} }
.to_json() .to_json()
} }
@ -2190,9 +2193,6 @@ pub struct TargetOptions {
/// Whether the target supports XRay instrumentation. /// Whether the target supports XRay instrumentation.
pub supports_xray: bool, pub supports_xray: bool,
/// Forces the use of emulated TLS (__emutls_get_address)
pub force_emulated_tls: bool,
} }
/// Add arguments for the given flavor and also for its "twin" flavors /// Add arguments for the given flavor and also for its "twin" flavors
@ -2408,7 +2408,6 @@ impl Default for TargetOptions {
entry_name: "main".into(), entry_name: "main".into(),
entry_abi: Conv::C, entry_abi: Conv::C,
supports_xray: false, supports_xray: false,
force_emulated_tls: false,
} }
} }
} }
@ -3112,7 +3111,6 @@ impl Target {
key!(entry_name); key!(entry_name);
key!(entry_abi, Conv)?; key!(entry_abi, Conv)?;
key!(supports_xray, bool); key!(supports_xray, bool);
key!(force_emulated_tls, bool);
if base.is_builtin { if base.is_builtin {
// This can cause unfortunate ICEs later down the line. // This can cause unfortunate ICEs later down the line.
@ -3368,7 +3366,6 @@ impl ToJson for Target {
target_option_val!(entry_name); target_option_val!(entry_name);
target_option_val!(entry_abi); target_option_val!(entry_abi);
target_option_val!(supports_xray); target_option_val!(supports_xray);
target_option_val!(force_emulated_tls);
if let Some(abi) = self.default_adjusted_cabi { if let Some(abi) = self.default_adjusted_cabi {
d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json()); d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json());

View file

@ -12,7 +12,13 @@
// compiling from a newer linux to an older linux, so we also have a // compiling from a newer linux to an older linux, so we also have a
// fallback implementation to use as well. // fallback implementation to use as well.
#[allow(unexpected_cfgs)] #[allow(unexpected_cfgs)]
#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "redox", target_os = "hurd"))] #[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "fuchsia",
target_os = "redox",
target_os = "hurd"
))]
// FIXME: The Rust compiler currently omits weakly function definitions (i.e., // FIXME: The Rust compiler currently omits weakly function definitions (i.e.,
// __cxa_thread_atexit_impl) and its metadata from LLVM IR. // __cxa_thread_atexit_impl) and its metadata from LLVM IR.
#[no_sanitize(cfi, kcfi)] #[no_sanitize(cfi, kcfi)]

View file

@ -20,6 +20,8 @@ loaded at program startup.
The TLS data must not be in a library loaded after startup (via `dlopen`). The TLS data must not be in a library loaded after startup (via `dlopen`).
- `local-exec` - model usable only if the TLS data is defined directly in the executable, - `local-exec` - model usable only if the TLS data is defined directly in the executable,
but not in a shared library, and is accessed only from that executable. but not in a shared library, and is accessed only from that executable.
- `emulated` - Uses thread-specific data keys to implement emulated TLS.
It is like using a general-dynamic TLS model for all modes.
`rustc` and LLVM may use a more optimized model than specified if they know that we are producing `rustc` and LLVM may use a more optimized model than specified if they know that we are producing
an executable rather than a library, or that the `static` item is private enough. an executable rather than a library, or that the `static` item is private enough.