From 79c0df3dda3b409d5754c692d366a5198093072b Mon Sep 17 00:00:00 2001 From: Anhad Singh Date: Wed, 7 Jan 2026 22:29:31 +1100 Subject: [PATCH] fix(ld_so/dso): `TPOFF` with undefined symbol index Fixes random `rustc` errors/page fault: ``` Dump of assembler code for function _RNvYNCNkNvNtNtCskfd1poFMNVt_21rustc_data_structures4sync12worker_local8REGISTRY00INtNtNtCs713rKgAoQ4g_4core3ops8function6FnOnceTINtNtB1o_6option6OptionQIB23_INtNtNtB1o_4cell4once8OnceCellNtB8_8RegistryEEEEE9call_onceBc_: 0x0000000007651e50 <+0>: 41 56 push r14 0x0000000007651e52 <+2>: 53 push rbx 0x0000000007651e53 <+3>: 50 push rax 0x0000000007651e54 <+4>: 48 8b 1d 6d 9c 46 02 mov rbx,QWORD PTR [rip+0x2469c6d] # 0x9abbac8 0x0000000007651e5b <+11>: 64 48 8b 04 25 00 00 00 00 mov rax,QWORD PTR fs:0x0 0x0000000007651e64 <+20>: 0f b6 4c 18 08 movzx ecx,BYTE PTR [rax+rbx*1+0x8] 0x0000000007651e69 <+25>: 48 01 d8 add rax,rbx 0x0000000007651e6c <+28>: 83 f9 01 cmp ecx,0x1 0x0000000007651e6f <+31>: 74 07 je 0x7651e78 <_RNvYNCNkNvNtNtCskfd1poFMNVt_21rustc_data_structures4sync12worker_local8REGISTRY00INtNtNtCs713rKgAoQ4g_4core3ops8function6FnOnceTINtNtB1o_6option6OptionQIB23_INtNtNtB1o_4cell4once8OnceCellNtB8_8RegistryEEEEE9call_onceBc_+40> 0x0000000007651e71 <+33>: 83 f9 02 cmp ecx,0x2 0x0000000007651e74 <+36>: 75 0a jne 0x7651e80 <_RNvYNCNkNvNtNtCskfd1poFMNVt_21rustc_data_structures4sync12worker_local8REGISTRY00INtNtNtCs713rKgAoQ4g_4core3ops8function6FnOnceTINtNtB1o_6option6OptionQIB23_INtNtNtB1o_4cell4once8OnceCellNtB8_8RegistryEEEEE9call_onceBc_+48> 0x0000000007651e76 <+38>: 31 c0 xor eax,eax 0x0000000007651e78 <+40>: 48 83 c4 08 add rsp,0x8 0x0000000007651e7c <+44>: 5b pop rbx 0x0000000007651e7d <+45>: 41 5e pop r14 0x0000000007651e7f <+47>: c3 ret 0x0000000007651e80 <+48>: 48 8d 35 69 11 00 00 lea rsi,[rip+0x1169] # 0x7652ff0 <_RINvNtNtNtNtCs7Gz96wxcBnT_3std3sys12thread_local6native5eager7destroyINtNtNtCs713rKgAoQ4g_4core4cell4once8OnceCellNtNtNtCskfd1poFMNVt_21rustc_data_structures4sync12worker_local8RegistryEEB1U_.llvm.5183627946997663933> 0x0000000007651e87 <+55>: 48 89 c7 mov rdi,rax 0x0000000007651e8a <+58>: 49 89 c6 mov r14,rax 0x0000000007651e8d <+61>: ff 15 c5 6b 47 02 call QWORD PTR [rip+0x2476bc5] # 0x9ac8a58 0x0000000007651e93 <+67>: 4c 89 f0 mov rax,r14 0x0000000007651e96 <+70>: 64 48 8b 0c 25 00 00 00 00 mov rcx,QWORD PTR fs:0x0 # rcx = 0x20aeb000 [FSBASE] # rbx = 0x2b40 0x0000000007651e9f <+79>: c6 44 19 08 01 mov BYTE PTR [rcx+rbx*1+0x8],0x1 # <--- PAGE FAULT 0x0000000007651ea4 <+84>: 48 83 c4 08 add rsp,0x8 0x0000000007651ea8 <+88>: 5b pop rbx 0x0000000007651ea9 <+89>: 41 5e pop r14 0x0000000007651eab <+91>: c3 ret ``` Signed-off-by: Anhad Singh --- src/ld_so/dso.rs | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/ld_so/dso.rs b/src/ld_so/dso.rs index 2fe3fdd495..ec2446f5cd 100644 --- a/src/ld_so/dso.rs +++ b/src/ld_so/dso.rs @@ -62,6 +62,9 @@ mod shim { pub use shim::*; +/// Undefined Symbol Index +pub const STN_UNDEF: SymbolIndex = SymbolIndex(0); + enum HashTable<'a> { Gnu(GnuHashTable<'a, FileHeader>), Sysv(SysVHashTable<'a, FileHeader>), @@ -878,7 +881,7 @@ impl DSO { fn static_relocate(&self, global_scope: &Scope, reloc: Relocation) -> object::Result<()> { let b = self.mmap.as_ptr() as usize; - let (sym, my_sym) = if reloc.sym.0 > 0 { + let (sym, my_sym) = if reloc.sym != STN_UNDEF { let name = self.dynamic.symbol_name(reloc.sym).unwrap(); let lookup_scopes = [global_scope, self.scope()]; @@ -896,11 +899,18 @@ impl DSO { (None, None) }; - let (s, t, tls_id) = sym + let (s, tls_obj) = sym .as_ref() - .map(|(sym, obj)| (sym.as_ptr() as usize, obj.tls_offset, obj.tls_module_id)) - // TODO: is self.tls_module_id the right fallback? - .unwrap_or((0, 0, self.tls_module_id)); + .map(|(sym, obj)| (sym.as_ptr() as usize, obj.as_ref())) + // (1) According to the System V gABI (Chapter 4, "Relocation"): if + // the symbol index (`reloc.sym`) is undefined, the symbol value + // (`s`) is defined as 0. + // + // (2) According to Drepper's ELF Handling For Thread-Local Storage + // (Section 4.2, "Local Dynamic TLS Model"): a relocation with + // undefined symbol index implies a reference to the current module + // itself. Hence we resolve the object to `self`. + .unwrap_or((0, self)); let ptr = if self.pie { (b + reloc.offset) as *mut u8 @@ -922,7 +932,7 @@ impl DSO { }; match reloc.kind { - RelocationKind::DTPMOD => set_usize(tls_id), + RelocationKind::DTPMOD => set_usize(tls_obj.tls_module_id), // TODO: Subtract DTP_OFFSET, which is 0x800 on riscv64, 0 on x86? RelocationKind::DTPOFF => { if reloc.sym.0 > 0 { @@ -939,13 +949,18 @@ impl DSO { RelocationKind::RELATIVE => set_usize(b + a), RelocationKind::SYMBOLIC => set_usize(s + a), RelocationKind::TPOFF => { + assert!( + !tls_obj.dlopened, + "The {{local/initial}}-exec access model is used for symbol '{}' in '{}', which requires a static TLS block. However, the definition in '{}' resides in the dynamic TLS block because the object was loaded via dlopen(2).", + reloc.sym, self.name, tls_obj.name + ); if reloc.sym.0 > 0 { let (sym, _) = sym .as_ref() .expect("RelocationKind::TPOFF called without valid symbol"); - set_usize((sym.value + a).wrapping_sub(t)); + set_usize((sym.value + a).wrapping_sub(tls_obj.tls_offset)); } else { - set_usize(a.wrapping_sub(t)); + set_usize(a.wrapping_sub(tls_obj.tls_offset)); } } RelocationKind::IRELATIVE => unsafe {