1- //! Thread-local storage data structures
1+ //! Thread-local storage data structures.
22//!
3- //! See _ELF Handling For Thread-Local Storage_ [[tls]] .
3+ //! See _ELF Handling For Thread-Local Storage_ [\ [tls\]] for details .
44//!
5- //! - For `riscv`-specifics, see _Thread Local Storage - RISC-V ELF Specification - RISC-V ELF psABI Document_ [[riscv]].
5+ //! - For ARM specifics, see
6+ //! - _4 ADDENDUM: Thread Local Storage - Addenda to, and Errata in, the ABI for the Arm Architecture_ [\[riscv\]] and
7+ //! - _Speeding Up Thread-Local Storage Access in Dynamic Libraries in the ARM platform_ [\[paper-lk2006\]].
8+ //! - For RISC-V specifics, see
9+ //! - _Thread Local Storage - RISC-V ELF Specification - RISC-V ELF psABI Document_ [\[riscv\]].
10+ //! - For x86-64 specifics, see
11+ //! - _ELF x86-64-ABI psABI_ [\[x86-64 psABI\]] and
12+ //! - _Thread-Local Storage Descriptors for IA32 and AMD64/EM64T_ [\[RFC-TLSDESC-x86\]]
613//!
7- //! The dynamic thread vector (dtv) is currently unused.
8- //!
9- //! [[tls]]: https://akkadia.org/drepper/tls.pdf
10- //! [[riscv]]: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/v1.0/riscv-elf.adoc#thread-local-storage
14+ //! [\[tls\]]: https://akkadia.org/drepper/tls.pdf
15+ //! [\[arm\]]: https://github.com/ARM-software/abi-aa/blob/2025Q1/addenda32/addenda32.rst#addendum-thread-local-storage
16+ //! [\[paper-lk2006\]]: https://www.fsfla.org/~lxoliva/writeups/TLS/paper-lk2006.pdf
17+ //! [\[riscv\]]: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/v1.0/riscv-elf.adoc#thread-local-storage
18+ //! [\[x86-64 psABI\]]: https://gitlab.com/x86-psABIs/x86-64-ABI/-/jobs/artifacts/master/raw/x86-64-ABI/abi.pdf?job=build
19+ //! [\[RFC-TLSDESC-x86\]]: https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt
1120
1221use core:: alloc:: Layout ;
13- use core:: mem:: MaybeUninit ;
22+ use core:: mem:: { self , MaybeUninit } ;
1423use core:: { ptr, slice} ;
1524
1625use hermit_entry:: boot_info:: TlsInfo ;
1726
1827use self :: allocation:: Allocation ;
1928
29+ /// Thread-local storage data structures.
2030pub struct Tls {
2131 _block : Allocation ,
2232 thread_ptr : * mut ( ) ,
2333}
2434
35+ /// Thread control block.
2536#[ repr( C ) ]
2637struct Tcb {
27- /// > For the implementation on GNU systems we can add one more requirement. The
28- /// > address %gs:0 represents is actually the same as the thread pointer. I.e., the content of
29- /// > the word addressed via %gs:0 is the address of the very same location.
30- ///
31- /// [[tls]] Section 3.4.2: IA-32 specific
38+ /// Thread pointer.
3239 #[ cfg( target_arch = "x86_64" ) ]
3340 thread_ptr : * mut ( ) ,
3441
3542 /// Pointer to the dynamic thread vector (dtv).
3643 ///
37- /// Currently unused .
38- dtv_ptr : * mut ( ) ,
44+ /// Currently not needed on Hermit .
45+ dtv : * mut ( ) ,
3946
40- /// OS-specific TCB data.
47+ /// Implementation-defined TCB data.
4148 ///
42- /// Currently unused .
49+ /// Currently not used on Hermit .
4350 tcb_data : * mut ( ) ,
4451}
4552
4653impl Tls {
47- pub fn new ( tls_info : TlsInfo ) -> Self {
54+ unsafe fn new ( tls_info : TlsInfo ) -> Self {
4855 let start = usize:: try_from ( tls_info. start ) . unwrap ( ) ;
4956 let filesz = usize:: try_from ( tls_info. filesz ) . unwrap ( ) ;
5057 let memsz = usize:: try_from ( tls_info. memsz ) . unwrap ( ) ;
@@ -53,7 +60,6 @@ impl Tls {
5360 // Get TLS initialization image
5461 let tls_init_image = {
5562 let start = ptr:: with_exposed_provenance ( start) ;
56- // SAFETY: We will have to trust the environment here.
5763 unsafe { slice:: from_raw_parts ( start, filesz) }
5864 } ;
5965
@@ -62,37 +68,53 @@ impl Tls {
6268 . unwrap ( )
6369 . pad_to_align ( ) ;
6470
65- let ( tls_layout , data_offset , tcb_offset) =
71+ let ( layout , tls_offset , tcb_offset) =
6672 if cfg ! ( any( target_arch = "aarch64" , target_arch = "riscv64" ) ) {
6773 // AArch64 and 64-bit RISC-V use TLS data structures variant I.
68- // This means the TLS data comes after the TCB.
74+
75+ // Variant I does not guarantee more than 16 bytes of space for the TCB.
76+ assert_eq ! ( tcb_layout. size( ) , 16 ) ;
77+
78+ // Variant I requires the dtv pointer to be at the start of the TCB.
79+ assert_eq ! ( mem:: offset_of!( Tcb , dtv) , 0 ) ;
80+
81+ // In variant I, the TLS data comes after the TCB.
6982 let ( tls_layout, data_offset) = tcb_layout. extend ( data_layout) . unwrap ( ) ;
7083 ( tls_layout. pad_to_align ( ) , data_offset, 0 )
7184 } else if cfg ! ( target_arch = "x86_64" ) {
7285 // x86-64 uses TLS data structures variant II.
73- // This means the TCB comes after the TLS data.
86+
87+ // Variant II (on GNU systems) requires the thread pointer to be at the start of the TCB:
88+ // > For the implementation on GNU systems we can add one more requirement. The
89+ // > address %gs:0 represents is actually the same as the thread pointer. I.e., the content of
90+ // > the word addressed via %gs:0 is the address of the very same location.
91+ #[ cfg( target_arch = "x86_64" ) ]
92+ assert_eq ! ( mem:: offset_of!( Tcb , thread_ptr) , 0 ) ;
93+
94+ // In Variant II, the TCB comes after the TLS data.
7495 let ( tls_layout, tcb_offset) = data_layout. extend ( tcb_layout) . unwrap ( ) ;
7596 ( tls_layout. pad_to_align ( ) , 0 , tcb_offset)
7697 } else {
7798 unimplemented ! ( )
7899 } ;
79100
80- let mut block = Allocation :: new ( tls_layout ) . unwrap ( ) ;
101+ let mut block = Allocation :: new ( layout ) . unwrap ( ) ;
81102
82- // Initialize beginning of the TLS block with TLS initialization image
83- block. as_mut_slice ( ) [ data_offset ..] [ ..tls_init_image. len ( ) ] . copy_from_slice ( tls_init_image) ;
103+ // Initialize the beginning of the TLS block with the TLS initialization image.
104+ block. as_mut_slice ( ) [ tls_offset ..] [ ..tls_init_image. len ( ) ] . copy_from_slice ( tls_init_image) ;
84105
85- // Fill the rest of the block with zeros
86- block. as_mut_slice ( ) [ data_offset ..] [ tls_init_image. len ( ) ..data_layout. size ( ) ]
106+ // Fill the rest of the TLS block with zeros.
107+ block. as_mut_slice ( ) [ tls_offset ..] [ tls_init_image. len ( ) ..data_layout. size ( ) ]
87108 . fill ( MaybeUninit :: new ( 0 ) ) ;
88109
89- let thread_ptr = if cfg ! ( target_arch = "aarch64" ) {
110+ let thread_ptr = if cfg ! ( target_arch = "riscv64" ) {
111+ // On RISC-V, `tp` points to the address one past the end of the TCB.
112+ unsafe { block. as_mut_ptr ( ) . add ( tls_offset) . cast ( ) }
113+ } else if cfg ! ( target_arch = "aarch64" ) {
114+ // For variant I, `tp` points to the start of the block.
90115 block. as_mut_ptr ( ) . cast ( )
91- } else if cfg ! ( target_arch = "riscv64" ) {
92- // > `tp` containing the address one past the end of the TCB.
93- // [[riscv]]
94- unsafe { block. as_mut_ptr ( ) . add ( data_offset) . cast ( ) }
95116 } else if cfg ! ( target_arch = "x86_64" ) {
117+ // For variant II, `tp` points to the TCB after the TLS data.
96118 unsafe { block. as_mut_ptr ( ) . add ( tcb_offset) . cast ( ) }
97119 } else {
98120 unimplemented ! ( )
@@ -102,7 +124,7 @@ impl Tls {
102124 let tcb = Tcb {
103125 #[ cfg( target_arch = "x86_64" ) ]
104126 thread_ptr,
105- dtv_ptr : ptr:: null_mut ( ) ,
127+ dtv : ptr:: null_mut ( ) ,
106128 tcb_data : ptr:: null_mut ( ) ,
107129 } ;
108130 unsafe {
@@ -115,9 +137,10 @@ impl Tls {
115137 }
116138 }
117139
118- pub fn from_environment ( ) -> Option < Self > {
140+ pub fn from_env ( ) -> Option < Self > {
119141 let tls_info = crate :: env:: boot_info ( ) . load_info . tls_info ?;
120- Some ( Self :: new ( tls_info) )
142+ let this = unsafe { Self :: new ( tls_info) } ;
143+ Some ( this)
121144 }
122145
123146 pub fn thread_ptr ( & self ) -> * mut ( ) {
@@ -137,20 +160,22 @@ mod allocation {
137160
138161 impl Allocation {
139162 pub fn new ( layout : Layout ) -> Option < Self > {
140- let ptr = unsafe { :: alloc:: alloc:: alloc_zeroed ( layout) } ;
163+ let ptr = unsafe { :: alloc:: alloc:: alloc ( layout) } ;
164+
141165 if ptr. is_null ( ) {
142166 return None ;
143167 }
144- Some ( Self { ptr, layout } )
145- }
146168
147- pub fn as_mut_slice ( & mut self ) -> & mut [ MaybeUninit < u8 > ] {
148- unsafe { slice:: from_raw_parts_mut ( self . ptr . cast ( ) , self . layout . size ( ) ) }
169+ Some ( Self { ptr, layout } )
149170 }
150171
151172 pub fn as_mut_ptr ( & self ) -> * mut u8 {
152173 self . ptr
153174 }
175+
176+ pub fn as_mut_slice ( & mut self ) -> & mut [ MaybeUninit < u8 > ] {
177+ unsafe { slice:: from_raw_parts_mut ( self . ptr . cast ( ) , self . layout . size ( ) ) }
178+ }
154179 }
155180
156181 impl Drop for Allocation {
0 commit comments