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 <andypython@protonmail.com>
This commit is contained in:
Anhad Singh
2026-01-07 22:29:31 +11:00
parent a97216aa93
commit 79c0df3dda
+23 -8
View File
@@ -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 {