feat(ld_so/dso): TLSDESC
The dynamic linker now works on aarch64. You can also try it on Linux by setting `TEST_RUNNER` to `qemu-aarch64` when running `make test TARGET=aarch64-unknown-linux-gnu` Co-authored-by: @willnode Signed-off-by: Anhad Singh <andypython@protonmail.com>
This commit is contained in:
Vendored
+2
-1
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"cmake.ignoreCMakeListsMissing": true
|
||||
"cmake.ignoreCMakeListsMissing": true,
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
+142
-8
@@ -13,20 +13,21 @@ use object::{
|
||||
use super::{
|
||||
debug::{_r_debug, RTLDDebug},
|
||||
linker::{__plt_resolve_trampoline, GLOBAL_SCOPE, Resolve, Scope, Symbol},
|
||||
tcb::Master,
|
||||
tcb::{Master, Tcb},
|
||||
};
|
||||
use crate::{
|
||||
header::sys_mman,
|
||||
header::{dl_tls::__tls_get_addr, sys_mman},
|
||||
platform::{Pal, Sys, types::c_void},
|
||||
};
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
string::{String, ToString},
|
||||
sync::Arc,
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{
|
||||
ffi::c_char,
|
||||
mem::size_of,
|
||||
mem::{offset_of, size_of},
|
||||
ptr::{self, NonNull},
|
||||
slice,
|
||||
};
|
||||
@@ -208,7 +209,7 @@ impl From<&Rel> for Relocation {
|
||||
|
||||
// This is matched up to REL_* constants used by musl for ease of comparison
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum RelocationKind {
|
||||
COPY,
|
||||
DTPMOD,
|
||||
@@ -817,6 +818,47 @@ impl DSO {
|
||||
))
|
||||
}
|
||||
|
||||
/// `TLSDESC` relocation being an extension to the original TLS ABI spec can
|
||||
/// be present in either `.rela.plt` (handled in [`Self::static_relocate`])
|
||||
/// or `.rela.dyn` (handled in [`Self::lazy_relocate`]) due to the lack of a
|
||||
/// standard unfortunately.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `reloc.kind` is not `RelocationKind::TLSDESC`.
|
||||
fn do_tlsdesc_reloc(&self, reloc: Relocation, ptr: *mut usize, global_scope: &Scope) {
|
||||
assert!(reloc.kind == RelocationKind::TLSDESC);
|
||||
let (sym, tls_module_id, tls_offset) = if reloc.sym != SymbolIndex(0) {
|
||||
let sym_name = self.dynamic.symbol_name(reloc.sym).unwrap();
|
||||
let (sym, _, obj) = resolve_sym(sym_name, &[global_scope, self.scope()]).unwrap();
|
||||
(sym.value, obj.tls_module_id, obj.tls_offset)
|
||||
} else {
|
||||
(0, self.tls_module_id, self.tls_offset)
|
||||
};
|
||||
|
||||
let resolver = unsafe { &mut *ptr };
|
||||
let descriptor = unsafe { &mut *ptr.add(1) };
|
||||
|
||||
if self.dlopened {
|
||||
let mut tls_index = crate::header::dl_tls::dl_tls_index {
|
||||
ti_module: tls_module_id,
|
||||
ti_offset: reloc.addend.unwrap_or_default(),
|
||||
};
|
||||
|
||||
// Ensure the DTV entry is initialised.
|
||||
unsafe { __tls_get_addr(&mut tls_index) };
|
||||
|
||||
*resolver = __tlsdesc_dynamic as *const () as usize;
|
||||
*descriptor = Box::into_raw(Box::new(TlsDescriptor {
|
||||
module_id: tls_module_id - 1,
|
||||
addend: sym + reloc.addend.unwrap_or_default(),
|
||||
})) as usize;
|
||||
} else {
|
||||
*resolver = __tlsdesc_static as *const () as usize;
|
||||
*descriptor = sym + tls_offset + reloc.addend.unwrap_or_default();
|
||||
}
|
||||
}
|
||||
|
||||
fn static_relocate(&self, global_scope: &Scope, reloc: Relocation) -> object::Result<()> {
|
||||
let b = self.mmap.as_ptr() as usize;
|
||||
|
||||
@@ -841,7 +883,7 @@ impl DSO {
|
||||
let (s, t, tls_id) = 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?
|
||||
// TODO: is self.tls_module_id the right fallback?
|
||||
.unwrap_or((0, 0, self.tls_module_id));
|
||||
|
||||
let ptr = if self.pie {
|
||||
@@ -858,14 +900,14 @@ impl DSO {
|
||||
},
|
||||
};
|
||||
|
||||
//TODO: support different sizes?
|
||||
// TODO: support different sizes?
|
||||
let set_usize = |value| unsafe {
|
||||
*(ptr as *mut usize) = value;
|
||||
};
|
||||
|
||||
match reloc.kind {
|
||||
RelocationKind::DTPMOD => set_usize(tls_id),
|
||||
//TODO: Subtract DTP_OFFSET, which is 0x800 on riscv64, 0 on x86?
|
||||
// TODO: Subtract DTP_OFFSET, which is 0x800 on riscv64, 0 on x86?
|
||||
RelocationKind::DTPOFF => {
|
||||
if reloc.sym.0 > 0 {
|
||||
let (sym, _) = sym
|
||||
@@ -909,6 +951,9 @@ impl DSO {
|
||||
// SAFETY: Both the source and destination have the same size.
|
||||
ptr::copy_nonoverlapping(sym.as_ptr() as *const u8, ptr, sym.size);
|
||||
},
|
||||
RelocationKind::TLSDESC => {
|
||||
self.do_tlsdesc_reloc(reloc, ptr.cast::<usize>(), global_scope)
|
||||
}
|
||||
_ => unimplemented!("relocation type {:?}", reloc.kind),
|
||||
}
|
||||
|
||||
@@ -927,7 +972,8 @@ impl DSO {
|
||||
|
||||
unsafe {
|
||||
got.add(1).write(core::ptr::addr_of!(*self) as usize);
|
||||
got.add(2).write(__plt_resolve_trampoline as usize);
|
||||
got.add(2)
|
||||
.write(__plt_resolve_trampoline as *const () as usize);
|
||||
}
|
||||
|
||||
let relsz = if self.dynamic.explicit_addend {
|
||||
@@ -975,6 +1021,13 @@ impl DSO {
|
||||
}
|
||||
}
|
||||
|
||||
(RelocationKind::TLSDESC, Resolve::Now) => {
|
||||
self.do_tlsdesc_reloc(reloc, ptr, global_scope);
|
||||
}
|
||||
(RelocationKind::TLSDESC, Resolve::Lazy) => {
|
||||
unreachable!("TLSDESC cannot be lazily resolved")
|
||||
}
|
||||
|
||||
_ => {
|
||||
unimplemented!(
|
||||
"relocation type {:?} with resolve {:?}",
|
||||
@@ -1092,3 +1145,84 @@ pub fn resolve_sym<'a>(
|
||||
) -> Option<(Symbol<'a>, SymbolBinding, Arc<DSO>)> {
|
||||
scopes.iter().find_map(|scope| scope.get_sym(name))
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct TlsDescriptor {
|
||||
module_id: usize,
|
||||
addend: usize,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[unsafe(naked)]
|
||||
unsafe extern "C" fn __tlsdesc_static() {
|
||||
core::arch::naked_asm!("ldr x0, [x0, #8]", "ret")
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[unsafe(naked)]
|
||||
unsafe extern "C" fn __tlsdesc_static() {
|
||||
core::arch::naked_asm!("mov rax, [rax + 8]", "ret")
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
fn __tlsdesc_dynamic();
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
core::arch::global_asm!(
|
||||
"
|
||||
.global __tlsdesc_dynamic
|
||||
.hidden __tlsdesc_dynamic
|
||||
__tlsdesc_dynamic:
|
||||
push rbx
|
||||
push rcx
|
||||
|
||||
mov rax, [rax + 8] // TLS descriptor
|
||||
mov rbx, [rax + {TLS_DESCRIPTOR_MODULE_ID_OFF}] // tls_descriptor.module_id
|
||||
mov rcx, [rax + {TLS_DESCRIPTOR_ADDEND_OFF}] // tls_descriptor.addend
|
||||
mov rax, qword ptr fs:[{DTV_PTR_OFF}] // tcb.dtv_ptr
|
||||
|
||||
// tcb.dtv_ptr[tls_descriptor.module_id] + tls_descriptor.addend
|
||||
mov rax, [rax + rbx * 8]
|
||||
add rax, rcx
|
||||
sub rax, qword ptr fs:[{TCB_SELF_PTR_OFF}]
|
||||
|
||||
pop rcx
|
||||
pop rbx
|
||||
|
||||
ret
|
||||
",
|
||||
TLS_DESCRIPTOR_MODULE_ID_OFF = const offset_of!(TlsDescriptor, module_id),
|
||||
TLS_DESCRIPTOR_ADDEND_OFF = const offset_of!(TlsDescriptor, addend),
|
||||
DTV_PTR_OFF = const offset_of!(Tcb, dtv_ptr),
|
||||
TCB_SELF_PTR_OFF = const offset_of!(Tcb, generic.tcb_ptr),
|
||||
);
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
core::arch::global_asm!(
|
||||
"
|
||||
.global __tlsdesc_dynamic
|
||||
.hidden __tlsdesc_dynamic
|
||||
__tlsdesc_dynamic:
|
||||
stp x1, x2, [sp, #-16]!
|
||||
|
||||
ldr x0, [x0, #8] // TLS descriptor
|
||||
|
||||
// x0 := tls_descriptor.module_id
|
||||
// x1 := tls_descriptor.addend
|
||||
ldp x0, x1, [x0]
|
||||
|
||||
mrs x2, tpidr_el0 // ABI ptr
|
||||
ldr x2, [x2] // TCB ptr
|
||||
|
||||
sub x1, x1, x2 // tls_descriptor.addend -= tcb
|
||||
|
||||
ldr x2, [x2, {DTV_PTR_OFF}] // tcb.dtv_ptr
|
||||
ldr x2, [x2, x0, lsl #3] // tcb.dtv_ptr[tls_descriptor.module_id]
|
||||
add x0, x2, x1 // tcb.dtv_ptr[tls_descriptor.module_id] + tls_descriptor.addend
|
||||
|
||||
ldp x1, x2, [sp], #16
|
||||
ret
|
||||
",
|
||||
DTV_PTR_OFF = const offset_of!(Tcb, dtv_ptr),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user