mirror of https://github.com/rust-lang/rust.git
Auto merge of #117873 - quininer:android-emutls, r=Amanieu
Add emulated TLS support This is a reopen of https://github.com/rust-lang/rust/pull/96317 . many android devices still only use 128 pthread keys, so using emutls can be helpful. 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. r? `@Amanieu`
This commit is contained in:
commit
608f32435a
|
@ -569,5 +569,6 @@ fn to_gcc_tls_mode(tls_model: TlsModel) -> gccjit::TlsModel {
|
|||
TlsModel::LocalDynamic => gccjit::TlsModel::LocalDynamic,
|
||||
TlsModel::InitialExec => gccjit::TlsModel::InitialExec,
|
||||
TlsModel::LocalExec => gccjit::TlsModel::LocalExec,
|
||||
TlsModel::Emulated => gccjit::TlsModel::GlobalDynamic,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ impl OwnedTargetMachine {
|
|||
split_dwarf_file: &CStr,
|
||||
output_obj_file: &CStr,
|
||||
debug_info_compression: &CStr,
|
||||
force_emulated_tls: bool,
|
||||
use_emulated_tls: bool,
|
||||
args_cstr_buff: &[u8],
|
||||
) -> Result<Self, LlvmError<'static>> {
|
||||
assert!(args_cstr_buff.len() > 0);
|
||||
|
@ -71,7 +71,7 @@ impl OwnedTargetMachine {
|
|||
split_dwarf_file.as_ptr(),
|
||||
output_obj_file.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.len(),
|
||||
)
|
||||
|
|
|
@ -33,7 +33,7 @@ use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, Switc
|
|||
use rustc_session::Session;
|
||||
use rustc_span::symbol::sym;
|
||||
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 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 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
|
||||
// null terminating them so we can use them as null terminated strings
|
||||
|
@ -297,7 +297,7 @@ pub fn target_machine_factory(
|
|||
&split_dwarf_file,
|
||||
&output_obj_file,
|
||||
&debuginfo_compression,
|
||||
force_emulated_tls,
|
||||
use_emulated_tls,
|
||||
&args_cstr_buff,
|
||||
)
|
||||
})
|
||||
|
|
|
@ -120,6 +120,7 @@ fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode {
|
|||
TlsModel::LocalDynamic => llvm::ThreadLocalMode::LocalDynamic,
|
||||
TlsModel::InitialExec => llvm::ThreadLocalMode::InitialExec,
|
||||
TlsModel::LocalExec => llvm::ThreadLocalMode::LocalExec,
|
||||
TlsModel::Emulated => llvm::ThreadLocalMode::GeneralDynamic,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -306,7 +306,9 @@ impl CodegenBackend for LlvmCodegenBackend {
|
|||
}
|
||||
PrintKind::TlsModels => {
|
||||
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);
|
||||
|
|
|
@ -2159,7 +2159,7 @@ extern "C" {
|
|||
SplitDwarfFile: *const c_char,
|
||||
OutputObjFile: *const c_char,
|
||||
DebugInfoCompression: *const c_char,
|
||||
ForceEmulatedTls: bool,
|
||||
UseEmulatedTls: bool,
|
||||
ArgsCstrBuff: *const c_char,
|
||||
ArgsCstrBuffLen: usize,
|
||||
) -> *mut TargetMachine;
|
||||
|
|
|
@ -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]);
|
||||
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
|
||||
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,
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ use rustc_middle::ty::{self, SymbolName, TyCtxt};
|
|||
use rustc_middle::ty::{GenericArgKind, GenericArgsRef};
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_session::config::{CrateType, OomStrategy};
|
||||
use rustc_target::spec::SanitizerSet;
|
||||
use rustc_target::spec::{SanitizerSet, TlsModel};
|
||||
|
||||
pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
|
||||
crates_export_threshold(tcx.crate_types())
|
||||
|
@ -564,6 +564,12 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
|
|||
|
||||
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;
|
||||
if !target.is_like_windows {
|
||||
// Mach-O has a global "_" suffix and `object` crate will handle it.
|
||||
|
@ -624,6 +630,32 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
|
|||
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> {
|
||||
// Build up a map from DefId to a `NativeLib` structure, where
|
||||
// `NativeLib` internally contains information about
|
||||
|
|
|
@ -410,7 +410,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
|
|||
const char *SplitDwarfFile,
|
||||
const char *OutputObjFile,
|
||||
const char *DebugInfoCompression,
|
||||
bool ForceEmulatedTls,
|
||||
bool UseEmulatedTls,
|
||||
const char *ArgsCstrBuff, size_t ArgsCstrBuffLen) {
|
||||
|
||||
auto OptLevel = fromRust(RustOptLevel);
|
||||
|
@ -456,13 +456,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
|
|||
Options.UseInitArray = UseInitArray;
|
||||
|
||||
#if LLVM_VERSION_LT(17, 0)
|
||||
if (ForceEmulatedTls) {
|
||||
Options.ExplicitEmulatedTLS = true;
|
||||
Options.EmulatedTLS = true;
|
||||
}
|
||||
#else
|
||||
Options.EmulatedTLS = ForceEmulatedTls || Trip.hasDefaultEmulatedTLS();
|
||||
Options.ExplicitEmulatedTLS = true;
|
||||
#endif
|
||||
Options.EmulatedTLS = UseEmulatedTls;
|
||||
|
||||
if (TrapUnreachable) {
|
||||
// Tell LLVM to codegen `unreachable` into an explicit trap instruction.
|
||||
|
|
|
@ -1283,7 +1283,7 @@ fn default_configuration(sess: &Session) -> Cfg {
|
|||
ret.insert((sym::relocation_model, Some(relocation_model)));
|
||||
}
|
||||
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));
|
||||
}
|
||||
let mut has_atomic = false;
|
||||
|
|
|
@ -1624,6 +1624,8 @@ options! {
|
|||
graphviz_font: String = ("Courier, monospace".to_string(), parse_string, [UNTRACKED],
|
||||
"use the given `fontname` in graphviz output; can be overridden by setting \
|
||||
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],
|
||||
"print some statistics about AST and HIR (default: no)"),
|
||||
human_readable_cgu_names: bool = (false, parse_bool, [TRACKED],
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::spec::{base, SanitizerSet, TargetOptions};
|
||||
use crate::spec::{base, SanitizerSet, TargetOptions, TlsModel};
|
||||
|
||||
pub fn opts() -> TargetOptions {
|
||||
let mut base = base::linux::opts();
|
||||
base.os = "android".into();
|
||||
base.is_like_android = true;
|
||||
base.default_dwarf_version = 2;
|
||||
base.tls_model = TlsModel::Emulated;
|
||||
base.has_thread_local = false;
|
||||
base.supported_sanitizers = SanitizerSet::ADDRESS;
|
||||
// This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::spec::{base, TargetOptions};
|
||||
use crate::spec::{base, TargetOptions, TlsModel};
|
||||
|
||||
pub fn opts() -> TargetOptions {
|
||||
let mut base = base::linux::opts();
|
||||
|
||||
base.env = "ohos".into();
|
||||
base.crt_static_default = false;
|
||||
base.force_emulated_tls = true;
|
||||
base.tls_model = TlsModel::Emulated;
|
||||
base.has_thread_local = false;
|
||||
|
||||
base
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::spec::{cvs, FramePointer, RelroLevel, TargetOptions};
|
||||
use crate::spec::{cvs, FramePointer, RelroLevel, TargetOptions, TlsModel};
|
||||
|
||||
pub fn opts() -> TargetOptions {
|
||||
TargetOptions {
|
||||
|
@ -11,6 +11,7 @@ pub fn opts() -> TargetOptions {
|
|||
frame_pointer: FramePointer::Always, // FIXME 43575: should be MayOmit...
|
||||
relro_level: RelroLevel::Full,
|
||||
default_dwarf_version: 2,
|
||||
tls_model: TlsModel::Emulated,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -929,6 +929,7 @@ pub enum TlsModel {
|
|||
LocalDynamic,
|
||||
InitialExec,
|
||||
LocalExec,
|
||||
Emulated,
|
||||
}
|
||||
|
||||
impl FromStr for TlsModel {
|
||||
|
@ -942,6 +943,7 @@ impl FromStr for TlsModel {
|
|||
"local-dynamic" => TlsModel::LocalDynamic,
|
||||
"initial-exec" => TlsModel::InitialExec,
|
||||
"local-exec" => TlsModel::LocalExec,
|
||||
"emulated" => TlsModel::Emulated,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
|
@ -954,6 +956,7 @@ impl ToJson for TlsModel {
|
|||
TlsModel::LocalDynamic => "local-dynamic",
|
||||
TlsModel::InitialExec => "initial-exec",
|
||||
TlsModel::LocalExec => "local-exec",
|
||||
TlsModel::Emulated => "emulated",
|
||||
}
|
||||
.to_json()
|
||||
}
|
||||
|
@ -2191,9 +2194,6 @@ pub struct TargetOptions {
|
|||
|
||||
/// Whether the target supports XRay instrumentation.
|
||||
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
|
||||
|
@ -2409,7 +2409,6 @@ impl Default for TargetOptions {
|
|||
entry_name: "main".into(),
|
||||
entry_abi: Conv::C,
|
||||
supports_xray: false,
|
||||
force_emulated_tls: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3113,7 +3112,6 @@ impl Target {
|
|||
key!(entry_name);
|
||||
key!(entry_abi, Conv)?;
|
||||
key!(supports_xray, bool);
|
||||
key!(force_emulated_tls, bool);
|
||||
|
||||
if base.is_builtin {
|
||||
// This can cause unfortunate ICEs later down the line.
|
||||
|
@ -3369,7 +3367,6 @@ impl ToJson for Target {
|
|||
target_option_val!(entry_name);
|
||||
target_option_val!(entry_abi);
|
||||
target_option_val!(supports_xray);
|
||||
target_option_val!(force_emulated_tls);
|
||||
|
||||
if let Some(abi) = self.default_adjusted_cabi {
|
||||
d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json());
|
||||
|
|
|
@ -12,7 +12,13 @@
|
|||
// compiling from a newer linux to an older linux, so we also have a
|
||||
// fallback implementation to use as well.
|
||||
#[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.,
|
||||
// __cxa_thread_atexit_impl) and its metadata from LLVM IR.
|
||||
#[no_sanitize(cfi, kcfi)]
|
||||
|
|
|
@ -20,6 +20,8 @@ loaded at program startup.
|
|||
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,
|
||||
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
|
||||
an executable rather than a library, or that the `static` item is private enough.
|
||||
|
|
Loading…
Reference in New Issue