From 8a7e831809f33fd0b890be1450872734222b63a6 Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Tue, 8 Oct 2024 01:19:40 +0000 Subject: [PATCH] Allow specifying the TLS ABI TLSDESC is an alternate method of TLS access. It is part of the ELF TLS ABI, and is already commonly used in other toolchains. LLVM supports this for most ELF backends (X86_64, Aarch64, RISC-V). It has always been the default for Aarch64, but support for RISC-V and X86_64 were added before the last LLVM release. More information on TLSDESC can be found in: https://android.googlesource.com/platform/bionic/+/HEAD/docs/elf-tls.md or the original RFC: https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-ARM.txt This patch adds a new unstable option `-Z tls-dialect`, which matches the common `-mtls-dialect=` option used in Clang, GCC, and other toolchains. This option only needs to be passed through to LLVM to update the code generation. For targets like Aarch64 that always use TLSDESC, this has no effect. Eventually this flag should probably be stabilized since it is an important part of a program's ABI. In the future, if LLVM changes its defaults for TLS to enable TLSDESC for a particular platform or architecture, users who do not wish to change can use the new option to select their desired TLS ABI. --- .../src/back/owned_target_machine.rs | 2 ++ compiler/rustc_codegen_llvm/src/back/write.rs | 5 ++- compiler/rustc_codegen_llvm/src/lib.rs | 11 ++++++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + compiler/rustc_driver_impl/src/lib.rs | 1 + .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 3 +- compiler/rustc_session/src/config.rs | 5 ++- compiler/rustc_session/src/options.rs | 14 +++++++- compiler/rustc_session/src/session.rs | 6 +++- compiler/rustc_target/src/spec/mod.rs | 34 +++++++++++++++++++ tests/assembly/auxiliary/tlsdesc_aux.rs | 8 +++++ tests/assembly/tlsdesc.rs | 29 ++++++++++++++++ tests/ui/invalid-compile-flags/print.stderr | 2 +- 13 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 tests/assembly/auxiliary/tlsdesc_aux.rs create mode 100644 tests/assembly/tlsdesc.rs diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 44c30d22a9e9c..44765a15dc1d8 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -39,6 +39,7 @@ impl OwnedTargetMachine { output_obj_file: &CStr, debug_info_compression: &CStr, use_emulated_tls: bool, + enable_tlsdesc: bool, args_cstr_buff: &[u8], ) -> Result> { assert!(args_cstr_buff.len() > 0); @@ -71,6 +72,7 @@ impl OwnedTargetMachine { output_obj_file.as_ptr(), debug_info_compression.as_ptr(), use_emulated_tls, + enable_tlsdesc, args_cstr_buff.as_ptr() as *const c_char, args_cstr_buff.len(), ) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index bfa9e8b82a033..c4254935797e2 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -26,7 +26,7 @@ use rustc_session::config::{ }; use rustc_span::InnerSpan; use rustc_span::symbol::sym; -use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; +use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo, TlsDialect, TlsModel}; use tracing::debug; use crate::back::lto::ThinBuffer; @@ -230,6 +230,8 @@ pub(crate) fn target_machine_factory( let use_emulated_tls = matches!(sess.tls_model(), TlsModel::Emulated); + let enable_tlsdesc = matches!(sess.tls_dialect(), TlsDialect::Desc); + // copy the exe path, followed by path all into one buffer // null terminating them so we can use them as null terminated strings let args_cstr_buff = { @@ -302,6 +304,7 @@ pub(crate) fn target_machine_factory( &output_obj_file, &debuginfo_compression, use_emulated_tls, + enable_tlsdesc, &args_cstr_buff, ) }) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index b85d28a2f1f71..d828bf9d5bc5a 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -310,6 +310,17 @@ impl CodegenBackend for LlvmCodegenBackend { } writeln!(out).unwrap(); } + PrintKind::TlsDialect => { + writeln!(out, "Available TLS dialects:").unwrap(); + for name in + &["trad", "desc"] + { + writeln!(out, " {name}").unwrap(); + } + writeln!(out).unwrap(); + } + + PrintKind::StackProtectorStrategies => { writeln!( out, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 8fc586d2c8ffc..a052253e74209 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2230,6 +2230,7 @@ unsafe extern "C" { OutputObjFile: *const c_char, DebugInfoCompression: *const c_char, UseEmulatedTls: bool, + EnableTlsDesc: bool, ArgsCstrBuff: *const c_char, ArgsCstrBuffLen: usize, ) -> *mut TargetMachine; diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index e2585c023883a..ba19a45c4b18a 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -837,6 +837,7 @@ fn print_crate_info( RelocationModels | CodeModels | TlsModels + | TlsDialect | TargetCPUs | StackProtectorStrategies | TargetFeatures => { diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 3e906f89c15c3..d93ac3fcf33ea 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -397,7 +397,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( bool TrapUnreachable, bool Singlethread, bool VerboseAsm, bool EmitStackSizeSection, bool RelaxELFRelocations, bool UseInitArray, const char *SplitDwarfFile, const char *OutputObjFile, - const char *DebugInfoCompression, bool UseEmulatedTls, + const char *DebugInfoCompression, bool UseEmulatedTls, bool EnableTLSDESC, const char *ArgsCstrBuff, size_t ArgsCstrBuffLen) { auto OptLevel = fromRust(RustOptLevel); @@ -456,6 +456,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( #if LLVM_VERSION_GE(19, 0) Options.MCOptions.X86RelaxRelocations = RelaxELFRelocations; + Options.EnableTLSDESC = EnableTLSDESC; #else Options.RelaxELFRelocations = RelaxELFRelocations; #endif diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index d733e32f209db..52713756574b6 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -825,6 +825,7 @@ pub enum PrintKind { RelocationModels, CodeModels, TlsModels, + TlsDialect, TargetSpec, AllTargetSpecs, NativeStaticLibs, @@ -1956,6 +1957,7 @@ fn collect_print_requests( ("target-libdir", PrintKind::TargetLibdir), ("target-list", PrintKind::TargetList), ("target-spec-json", PrintKind::TargetSpec), + ("tls-dialect", PrintKind::TlsDialect), ("tls-models", PrintKind::TlsModels), // tidy-alphabetical-end ]; @@ -3018,7 +3020,7 @@ pub(crate) mod dep_tracking { use rustc_target::spec::{ CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTriple, - TlsModel, WasmCAbi, + TlsDialect, TlsModel, WasmCAbi, }; use super::{ @@ -3083,6 +3085,7 @@ pub(crate) mod dep_tracking { FramePointer, RelocModel, CodeModel, + TlsDialect, TlsModel, InstrumentCoverage, CoverageOptions, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 54a4621db2462..7c2889e4498ce 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -14,7 +14,7 @@ use rustc_span::{RealFileName, SourceFileHashAlgorithm}; use rustc_target::spec::{ CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, - TargetTriple, TlsModel, WasmCAbi, + TargetTriple, TlsDialect, TlsModel, WasmCAbi, }; use crate::config::*; @@ -427,6 +427,8 @@ mod desc { "one of supported code models (`rustc --print code-models`)"; pub(crate) const parse_tls_model: &str = "one of supported TLS models (`rustc --print tls-models`)"; + pub(crate) const parse_tls_dialect: &str = + "one of supported TLS dialects (`rustc --print tls-dialect`)"; pub(crate) const parse_target_feature: &str = parse_string; pub(crate) const parse_terminal_url: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or `auto`"; @@ -1244,6 +1246,13 @@ mod parse { } true } + pub(crate) fn parse_tls_dialect(slot: &mut Option, v: Option<&str>) -> bool { + match v.and_then(|s| TlsDialect::from_str(s).ok()) { + Some(tls_dialect) => *slot = Some(tls_dialect), + _ => return false, + } + true + } pub(crate) fn parse_terminal_url(slot: &mut TerminalUrl, v: Option<&str>) -> bool { *slot = match v { @@ -2114,6 +2123,9 @@ written to standard error output)"), #[rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field")] tls_model: Option = (None, parse_tls_model, [TRACKED], "choose the TLS model to use (`rustc --print tls-models` for details)"), + #[rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field")] + tls_dialect: Option = (None, parse_tls_dialect, [TRACKED], + "choose the TLS dialect to use (`rustc --print tls-dialect` for details)"), trace_macros: bool = (false, parse_bool, [UNTRACKED], "for every macro invocation, print its name and arguments (default: no)"), track_diagnostics: bool = (false, parse_bool, [UNTRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 45434534c75ea..feae0e4ab556a 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -32,7 +32,7 @@ use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{ CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, Target, - TargetTriple, TlsModel, + TargetTriple, TlsDialect, TlsModel, }; use crate::code_stats::CodeStats; @@ -767,6 +767,10 @@ impl Session { self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model) } + pub fn tls_dialect(&self) -> TlsDialect { + self.opts.unstable_opts.tls_dialect.unwrap_or(self.target.tls_dialect) + } + pub fn direct_access_external_data(&self) -> Option { self.opts .unstable_opts diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 3e7b42d3d1c13..40e72fddc5aab 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1125,6 +1125,36 @@ impl ToJson for TlsModel { } } +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum TlsDialect { + Trad, // Use traditional TLS (e.g. through tls_get_addr()). + Desc, // Use TLS descriptors (TLSDESC). +} + +impl FromStr for TlsDialect { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + // There are only two variants, but the options spelling has been inconsistent across + // architectures in other compilers. + "trad" | "traditional" | "gnu" => TlsDialect::Trad, + "desc" | "descriptor" | "gnu2" => TlsDialect::Desc, + _ => return Err(()), + }) + } +} + +impl ToJson for TlsDialect { + fn to_json(&self) -> Json { + match *self { + TlsDialect::Trad => "trad", + TlsDialect::Desc => "desc", + } + .to_json() + } +} + /// Everything is flattened to a single enum to make the json encoding/decoding less annoying. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub enum LinkOutputKind { @@ -2226,6 +2256,9 @@ pub struct TargetOptions { /// TLS model to use. Options are "global-dynamic" (default), "local-dynamic", "initial-exec" /// and "local-exec". This is similar to the -ftls-model option in GCC/Clang. pub tls_model: TlsModel, + /// TLS dialect to use. Options are "trad" (default) for traditional TLS access and "desc" + /// for TLSDESC. This is similar to the -mtls-dialect option in GCC/Clang. + pub tls_dialect: TlsDialect, /// Do not emit code that uses the "red zone", if the ABI has one. Defaults to false. pub disable_redzone: bool, /// Frame pointer mode for this target. Defaults to `MayOmit`. @@ -2629,6 +2662,7 @@ impl Default for TargetOptions { relocation_model: RelocModel::Pic, code_model: None, tls_model: TlsModel::GeneralDynamic, + tls_dialect: TlsDialect::Trad, disable_redzone: false, frame_pointer: FramePointer::MayOmit, function_sections: true, diff --git a/tests/assembly/auxiliary/tlsdesc_aux.rs b/tests/assembly/auxiliary/tlsdesc_aux.rs new file mode 100644 index 0000000000000..3a590f4c0844d --- /dev/null +++ b/tests/assembly/auxiliary/tlsdesc_aux.rs @@ -0,0 +1,8 @@ +#![crate_type = "lib"] + +use std::cell::Cell; + +thread_local! { +#[no_mangle] +pub static A: Cell = const { Cell::new(0) }; +} diff --git a/tests/assembly/tlsdesc.rs b/tests/assembly/tlsdesc.rs new file mode 100644 index 0000000000000..65588f69a89e8 --- /dev/null +++ b/tests/assembly/tlsdesc.rs @@ -0,0 +1,29 @@ +// Verifies that setting the tls-dialect result sin the correct set of assembly +// instructions. +// +// Note: tls-dialect flags have no changes to LLVM IR, and only affect which +// instruction sequences are emitted by the LLVM backend. Checking the assembly +// output is how we test the lowering in LLVM, and is the only way a frontend +// can determine if its code generation flags are set correctly. +// +//@ revisions: x64 x64-trad x64-desc +// +//@[x64] compile-flags: --target=x86_64-unknown-linux-gnu +//@[x64-trad] compile-flags: --target=x86_64-unknown-linux-gnu -Z tls-dialect=trad +//@[x64-desc] compile-flags: --target=x86_64-unknown-linux-gnu -Z tls-dialect=desc +// +//@ assembly-output: emit-asm +//@ aux-build:tlsdesc_aux.rs + +#![crate_type = "lib"] + +extern crate tlsdesc_aux as aux; + +#[no_mangle] +fn get_aux() -> u64 { + // x64: __tls_get_addr + // x64-trad: __tls_get_addr + // x64-desc: tlsdesc + // x64-desc: tlscall + aux::A.with(|a| a.get()) +} diff --git a/tests/ui/invalid-compile-flags/print.stderr b/tests/ui/invalid-compile-flags/print.stderr index 70b4a394dd022..2d54727656735 100644 --- a/tests/ui/invalid-compile-flags/print.stderr +++ b/tests/ui/invalid-compile-flags/print.stderr @@ -1,4 +1,4 @@ error: unknown print request: `yyyy` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-dialect`, `tls-models`