aarch64: dynamic linking, libc and TLS for aarch64
This commit is contained in:
parent
d198571ac7
commit
7b9788188b
@ -243,6 +243,10 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
|
||||
__aarch64_switch_task_and_drop(self.inner.get(), thread);
|
||||
}
|
||||
|
||||
fn set_thread_pointer(&self, _tp: usize) {
|
||||
// Do nothing: tp can be set from EL0 by writing to TPIDR_EL0 directly
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>> Drop
|
||||
|
@ -49,62 +49,6 @@ pub struct LoadOptions<'e, P: AsRef<Path>> {
|
||||
pub disable_aslr: bool,
|
||||
}
|
||||
|
||||
// struct BufferPlacer<'a> {
|
||||
// buffer: &'a mut [u8],
|
||||
// virtual_offset: usize,
|
||||
// offset: usize,
|
||||
// }
|
||||
//
|
||||
// impl<'a> BufferPlacer<'a> {
|
||||
// pub fn new(virtual_offset: usize, buffer: &'a mut [u8]) -> Self {
|
||||
// Self {
|
||||
// buffer,
|
||||
// virtual_offset,
|
||||
// offset: 0,
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// unsafe fn alloc_layout<T>(
|
||||
// &mut self,
|
||||
// layout: Layout,
|
||||
// ) -> Result<(NonNull<T>, NonNull<T>), Error> {
|
||||
// // TODO checks
|
||||
// let aligned = (self.offset + layout.align() - 1) & !(layout.align() - 1);
|
||||
// self.offset = aligned + layout.size();
|
||||
// Ok((
|
||||
// NonNull::new_unchecked(self.buffer.as_mut_ptr().add(aligned) as *mut T),
|
||||
// NonNull::new_unchecked((self.virtual_offset + aligned) as *mut T),
|
||||
// ))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// unsafe impl<'a> Placer for BufferPlacer<'a> {
|
||||
// fn place_ref<T: Place>(&mut self, r: &T) -> Result<NonNull<T::Output>, Error> {
|
||||
// let layout = Layout::new::<T::Output>();
|
||||
// unsafe {
|
||||
// let (kernel, user) = self.alloc_layout::<T::Output>(layout)?;
|
||||
// kernel.as_ptr().write(r.place(self)?);
|
||||
// Ok(user)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fn place_slice<T: Place>(&mut self, r: &[T]) -> Result<NonNull<[T::Output]>, Error> {
|
||||
// let layout = Layout::array::<T>(r.len()).unwrap();
|
||||
// unsafe {
|
||||
// let (kernel, user) = self.alloc_layout::<T::Output>(layout)?;
|
||||
// let kernel = NonNull::slice_from_raw_parts(kernel, r.len());
|
||||
// let user = NonNull::slice_from_raw_parts(user, r.len());
|
||||
// for (i, elem) in r.iter().enumerate() {
|
||||
// kernel
|
||||
// .get_unchecked_mut(i)
|
||||
// .as_ptr()
|
||||
// .write(elem.place(self)?);
|
||||
// }
|
||||
// Ok(user)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
struct ArgPlacer<'a> {
|
||||
buffer: &'a mut [u8],
|
||||
position: usize,
|
||||
@ -192,24 +136,18 @@ fn setup_program_env(
|
||||
let mut arg_ptrs = vec![];
|
||||
let mut env_ptrs = vec![];
|
||||
|
||||
log::debug!("place karg: {:#x}", virt);
|
||||
for arg in args.iter() {
|
||||
let ptr = placer.put_str(arg)? + virt;
|
||||
log::debug!("put arg {:?} -> {:#x}", arg, ptr);
|
||||
arg_ptrs.push(ptr);
|
||||
}
|
||||
for env in envs.iter() {
|
||||
let ptr = placer.put_str(env)? + virt;
|
||||
log::debug!("put env {:?} -> {:#x}", env, ptr);
|
||||
env_ptrs.push(ptr);
|
||||
}
|
||||
|
||||
let argv = placer.put_ptr_array(&arg_ptrs)? + virt;
|
||||
log::debug!("put argv {} -> {:#x}", arg_ptrs.len(), argv);
|
||||
let envp = placer.put_ptr_array(&env_ptrs)? + virt;
|
||||
log::debug!("put envp {} -> {:#x}", env_ptrs.len(), envp);
|
||||
let auxv = placer.put_aux_array(aux)? + virt;
|
||||
log::debug!("put auxv {} -> {:#x}", aux.len(), auxv);
|
||||
|
||||
// Put ProgramArgumentInner struct
|
||||
let arg_address = placer.position + virt;
|
||||
|
@ -71,7 +71,10 @@ pub fn kinit() -> Result<(), Error> {
|
||||
|
||||
// TODO move this to userspace so it doesn't block the init process, maybe lazy-load on first
|
||||
// attempt to load a module?
|
||||
load_kernel_symbol_table(&mut ioctx, "/kernel.sym")?;
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
{
|
||||
load_kernel_symbol_table(&mut ioctx, "/kernel.sym")?;
|
||||
}
|
||||
|
||||
{
|
||||
let group_id = Process::create_group();
|
||||
|
@ -48,12 +48,14 @@ pub(crate) fn map_memory(
|
||||
}
|
||||
};
|
||||
|
||||
space.allocate(
|
||||
let result = space.allocate(
|
||||
None,
|
||||
len,
|
||||
backing,
|
||||
MapAttributes::USER_WRITE | MapAttributes::USER_READ | MapAttributes::NON_GLOBAL,
|
||||
)
|
||||
);
|
||||
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -11,5 +11,8 @@ pub fn get_thread_pointer() -> usize {
|
||||
}
|
||||
|
||||
pub unsafe fn set_thread_pointer(value: usize) -> Result<(), Error> {
|
||||
todo!()
|
||||
unsafe {
|
||||
core::arch::asm!("msr tpidr_el0, {0}", in(reg) value);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -56,6 +56,8 @@ pub struct Dtv {
|
||||
entries: Vec<*mut c_void>,
|
||||
// Entries for pthread_setspecific()-like behavior
|
||||
specific: Vec<*mut c_void>,
|
||||
// Self-reference for the thread
|
||||
thread_self: *mut c_void,
|
||||
}
|
||||
|
||||
struct TcbHeader {
|
||||
@ -124,6 +126,7 @@ impl Dtv {
|
||||
Self {
|
||||
entries: Vec::new(),
|
||||
specific: Vec::new(),
|
||||
thread_self: null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,14 +200,13 @@ impl Dtv {
|
||||
pub fn init_tls_from_auxv<'a, I: Iterator<Item = &'a AuxValue>>(
|
||||
auxv: I,
|
||||
force: bool,
|
||||
tcb_size: usize,
|
||||
) -> Result<Option<TlsImage>, Error> {
|
||||
let Some(tls_image) = TlsImage::from_auxv(auxv) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
if force || !tls_image.already_initialized {
|
||||
let (base, tp) = clone_tls(&tls_image, tcb_size)?;
|
||||
let (base, tp) = clone_tls(&tls_image)?;
|
||||
unsafe { set_thread_pointer(tp) }?;
|
||||
setup_dtv(&tls_image, base)?;
|
||||
}
|
||||
@ -214,14 +216,14 @@ pub fn init_tls_from_auxv<'a, I: Iterator<Item = &'a AuxValue>>(
|
||||
|
||||
/// Initializes thread-local storage for this thread from a [TlsImage]. If no image is provided,
|
||||
/// a dummy (platform-specific) TLS copy will be created.
|
||||
pub fn init_tls(image: Option<&TlsImage>, force: bool, tcb_size: usize) -> Result<(), Error> {
|
||||
pub fn init_tls(image: Option<&TlsImage>, force: bool) -> Result<(), Error> {
|
||||
let Some(image) = image else {
|
||||
crate::debug_trace!("TODO: handle executables with no TLS");
|
||||
return Err(Error::NotImplemented);
|
||||
};
|
||||
|
||||
if force || !image.already_initialized {
|
||||
let (base, tp) = clone_tls(image, tcb_size)?;
|
||||
let (base, tp) = clone_tls(image)?;
|
||||
unsafe { set_thread_pointer(tp) }?;
|
||||
setup_dtv(&image, base)?;
|
||||
}
|
||||
@ -253,11 +255,6 @@ fn setup_dtv(image: &TlsImage, tls_base: usize) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the pointer to the TCB contents after the self-pointer and the DTV
|
||||
pub fn get_tcb() -> *mut u8 {
|
||||
unsafe { get_tcb_raw().add(1).cast() }
|
||||
}
|
||||
|
||||
/// Gets or allocates a DTV for current thread.
|
||||
pub fn get_dtv() -> &'static mut Dtv {
|
||||
let tcb = get_tcb_mut();
|
||||
@ -269,7 +266,10 @@ pub fn get_dtv() -> &'static mut Dtv {
|
||||
unsafe { &mut *tcb.dtv_pointer }
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "__tls_get_addr", rust_analyzer))]
|
||||
#[cfg(all(
|
||||
any(target_arch = "x86", target_arch = "x86_64"),
|
||||
any(feature = "__tls_get_addr", rust_analyzer)
|
||||
))]
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn __tls_get_addr(index: *mut usize) -> *mut c_void {
|
||||
let module_id = index.read();
|
||||
|
@ -1,13 +1,82 @@
|
||||
use abi::error::Error;
|
||||
use abi::{
|
||||
error::Error,
|
||||
mem::{MappingFlags, MappingSource},
|
||||
};
|
||||
|
||||
use super::TlsImage;
|
||||
|
||||
// Variant I TLS layout:
|
||||
//
|
||||
// [ TCB ] [ pad to p_align ] [ MODULE ] [ MODULE ] [ MODULE ] ...
|
||||
// | | | |
|
||||
// | | | |
|
||||
// tp off1 off2 off3
|
||||
//
|
||||
// TCB size is fixed at 2 * sizeof(usize).
|
||||
|
||||
/// Creates a new TLS image in the process memory, copying data from the TLS master copy (if any).
|
||||
/// Returns the resulting thread pointer.
|
||||
pub fn clone_tls(image: &TlsImage, tcb_size: usize) -> Result<(usize, usize), Error> {
|
||||
Err(Error::NotImplemented)
|
||||
pub fn clone_tls(image: &TlsImage) -> Result<(usize, usize), Error> {
|
||||
const TCB_SIZE: usize = size_of::<usize>() * 2;
|
||||
|
||||
if !image.align.is_power_of_two() {
|
||||
panic!("TLS layout not aligned to a power of two: {}", image.align)
|
||||
}
|
||||
if image.align > 0x1000 {
|
||||
panic!("TODO: TLS alignment larger than a page size is not supported");
|
||||
}
|
||||
|
||||
let align = image.align;
|
||||
|
||||
// TCB size, padded to align. Also the start of the first module
|
||||
let tcb_aligned_size = (TCB_SIZE + image.align - 1) & !(image.align - 1);
|
||||
let full_size = tcb_aligned_size + image.full_size;
|
||||
let page_aligned_size = (full_size + 0xFFF) & !0xFFF;
|
||||
|
||||
let base = unsafe {
|
||||
crate::sys::map_memory(
|
||||
None,
|
||||
page_aligned_size,
|
||||
MappingFlags::WRITE,
|
||||
&MappingSource::Anonymous,
|
||||
)
|
||||
}?;
|
||||
|
||||
let ptr = core::ptr::with_exposed_provenance_mut::<u8>(base);
|
||||
|
||||
let module_data =
|
||||
unsafe { core::slice::from_raw_parts_mut(ptr.add(tcb_aligned_size), image.full_size) };
|
||||
let tcb = unsafe { core::slice::from_raw_parts_mut(ptr, TCB_SIZE) };
|
||||
|
||||
// Clone the Master Copy, if any
|
||||
let data_size = if let Some(master_copy) = image.master_copy {
|
||||
let source = unsafe { master_copy.as_ref() };
|
||||
module_data[..source.len()].copy_from_slice(source);
|
||||
source.len()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// Zero the rest
|
||||
module_data[data_size..image.full_size].fill(0);
|
||||
|
||||
// Place the self-pointer to point at the TP itself
|
||||
let tp = base;
|
||||
assert_eq!(
|
||||
tp % size_of::<usize>(),
|
||||
0,
|
||||
"TLS self-pointer should at least be word-aligned"
|
||||
);
|
||||
tcb[..size_of::<usize>()].copy_from_slice(&tp.to_ne_bytes());
|
||||
|
||||
// Zero the TCB after the self-pointer
|
||||
tcb[size_of::<usize>()..].fill(0);
|
||||
|
||||
crate::debug_trace!("TLS: base={:#x}, tp={:#x}", base, tp);
|
||||
|
||||
Ok((base, tp))
|
||||
}
|
||||
|
||||
pub(super) fn get_tcb_raw(_tp: usize) -> *mut u8 {
|
||||
todo!()
|
||||
pub(super) fn get_tcb_raw(tp: usize) -> *mut u8 {
|
||||
core::ptr::with_exposed_provenance_mut(tp)
|
||||
}
|
||||
|
@ -14,18 +14,13 @@ use super::TlsImage;
|
||||
|
||||
/// Creates a new TLS image in the process memory, copying data from the TLS master copy (if any).
|
||||
/// Returns the resulting thread pointer.
|
||||
pub fn clone_tls(image: &TlsImage, tcb_size: usize) -> Result<(usize, usize), Error> {
|
||||
pub fn clone_tls(image: &TlsImage) -> Result<(usize, usize), Error> {
|
||||
// Basically, the layout is:
|
||||
// * align(image.full_size) below the TP
|
||||
// * tcb_size starting with the TP
|
||||
|
||||
// If the caller asked for no TCB to be placed, just reserve two words, the first one
|
||||
// will be used to put the self-pointer
|
||||
let tcb_size = if tcb_size < size_of::<usize>() * 2 {
|
||||
size_of::<usize>() * 2
|
||||
} else {
|
||||
(tcb_size + size_of::<usize>() - 1) & !(size_of::<usize>() - 1)
|
||||
};
|
||||
// Reserve two words for the TCB to be consistent with Variant I
|
||||
let tcb_size = size_of::<usize>() * 2;
|
||||
|
||||
if !image.align.is_power_of_two() {
|
||||
panic!("TLS layout not aligned to a power of two: {}", image.align);
|
||||
|
1
test.c
1
test.c
@ -15,6 +15,7 @@ static void *function(void *arg) {
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
pthread_t thread;
|
||||
printf("[main] &x = %p\n", &x);
|
||||
printf("[main] x = %d\n", x);
|
||||
x = 321;
|
||||
printf("[main] x = %d\n", x);
|
||||
|
1
userspace/Cargo.lock
generated
1
userspace/Cargo.lock
generated
@ -376,6 +376,7 @@ name = "dyn-loader"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"elf",
|
||||
"thiserror",
|
||||
"yggdrasil-rt",
|
||||
|
@ -9,6 +9,7 @@ thiserror.workspace = true
|
||||
|
||||
elf = "0.7.4"
|
||||
bytemuck = "1.19.0"
|
||||
cfg-if = "1.0.0"
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(rust_analyzer)'] }
|
||||
|
@ -0,0 +1,13 @@
|
||||
|
||||
// Has to be naked: need to prevent the function from clobbering any registers besides x0
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
#[naked]
|
||||
pub unsafe extern "C" fn tlsdesc_resolve_static(argument: *const usize) -> usize {
|
||||
// x0 -- pointer to tlsdesc struct:
|
||||
// [0]: this function pointer
|
||||
// [1]: static offset to return
|
||||
core::arch::naked_asm!(r#"
|
||||
ldr x0, [x0, #8]
|
||||
ret
|
||||
"#);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#![feature(yggdrasil_os, never_type, map_try_insert, slice_ptr_get, iter_chain)]
|
||||
#![feature(yggdrasil_os, never_type, map_try_insert, slice_ptr_get, iter_chain, naked_functions)]
|
||||
|
||||
use std::process::ExitCode;
|
||||
|
||||
@ -81,7 +81,7 @@ fn run(binary: &str, args: &[String]) -> Result<!, Error> {
|
||||
// Set up TLS for the main thread. This needs to be done here, because
|
||||
// we still need to call initializers before handing off control to the executable
|
||||
// TODO use an ELF note to indicate the extra size required in the TCB
|
||||
yggdrasil_rt::process::thread_local::init_tls_from_auxv(auxv.iter(), true, size_of::<usize>() * 4)
|
||||
yggdrasil_rt::process::thread_local::init_tls_from_auxv(auxv.iter(), true)
|
||||
.expect("Could not initialize main thread TLS for the executable");
|
||||
|
||||
auxv.push(AuxValue {
|
||||
@ -117,8 +117,14 @@ unsafe fn enter(entry: extern "C" fn(usize), argument: usize) -> ! {
|
||||
);
|
||||
}
|
||||
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
||||
{}
|
||||
unreachable!()
|
||||
{
|
||||
loop {}
|
||||
}
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
{
|
||||
entry(argument);
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
|
104
userspace/dyn-loader/src/relocation/aarch64.rs
Normal file
104
userspace/dyn-loader/src/relocation/aarch64.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use elf::{
|
||||
abi::{
|
||||
R_AARCH64_ABS64, R_AARCH64_GLOB_DAT, R_AARCH64_JUMP_SLOT, R_AARCH64_RELATIVE,
|
||||
R_AARCH64_TLSDESC, R_AARCH64_TLS_TPREL,
|
||||
},
|
||||
relocation::{Rel, Rela},
|
||||
};
|
||||
|
||||
use crate::{builtins, error::Error, object::ResolvedSymbol, state::State};
|
||||
|
||||
use super::{RelValue, RelaValue, Relocation};
|
||||
|
||||
// Descriptor method of TLS access:
|
||||
// * For static access:
|
||||
// [0]: resolver function pointer
|
||||
// [1]: fixed offset of the variable with the TLS area
|
||||
// * For dynamic, TODO
|
||||
fn make_tlsdesc_relocation(
|
||||
dynamic: bool,
|
||||
state: &State,
|
||||
module_id: u32,
|
||||
symbol_offset: usize,
|
||||
) -> RelaValue {
|
||||
if dynamic {
|
||||
todo!("Dynamic TLSDESC relocations are not yet supported")
|
||||
}
|
||||
let layout = state
|
||||
.tls_layout
|
||||
.as_ref()
|
||||
.expect("TLSDESC relocation, but no TLS segments in linked objects");
|
||||
let Some(offset) = layout.offset(module_id, symbol_offset) else {
|
||||
panic!("TLSDESC relocation against unknown module_id={module_id}, offset={symbol_offset}");
|
||||
};
|
||||
|
||||
RelaValue::DQWord(builtins::tlsdesc_resolve_static as i64, offset as i64)
|
||||
}
|
||||
|
||||
impl Relocation for Rel {
|
||||
type Value = RelValue;
|
||||
|
||||
fn resolve(
|
||||
&self,
|
||||
_state: &State,
|
||||
_name: &str,
|
||||
_symbol: &ResolvedSymbol,
|
||||
_load_base: usize,
|
||||
) -> Result<Option<Self::Value>, Error> {
|
||||
unimplemented!("rel-type relocations are not implemented for aarch64")
|
||||
}
|
||||
}
|
||||
|
||||
impl Relocation for Rela {
|
||||
type Value = RelaValue;
|
||||
|
||||
fn resolve(
|
||||
&self,
|
||||
state: &State,
|
||||
name: &str,
|
||||
symbol: &ResolvedSymbol,
|
||||
load_base: usize,
|
||||
) -> Result<Option<Self::Value>, Error> {
|
||||
match symbol {
|
||||
&ResolvedSymbol::Tls(tls) => match self.r_type {
|
||||
// See make_tlsdesc_relocation()
|
||||
R_AARCH64_TLSDESC => Ok(Some(make_tlsdesc_relocation(
|
||||
false,
|
||||
state,
|
||||
tls.module_id,
|
||||
tls.offset,
|
||||
))),
|
||||
R_AARCH64_TLS_TPREL => {
|
||||
let layout = state.tls_layout.as_ref().unwrap();
|
||||
let offset = layout.offset(tls.module_id, tls.offset).unwrap();
|
||||
debug_trace!("{}@tprel -> {}", name, offset);
|
||||
Ok(Some(RelaValue::QWord(offset as _)))
|
||||
}
|
||||
_ => todo!("Unsupported relocation against TLS symbol: {}", self.r_type),
|
||||
},
|
||||
&ResolvedSymbol::Null(object_id) => match self.r_type {
|
||||
// See make_tlsdesc_relocation()
|
||||
R_AARCH64_TLSDESC => Ok(Some(make_tlsdesc_relocation(false, state, object_id, 0))),
|
||||
// B + A
|
||||
R_AARCH64_RELATIVE => Ok(Some(RelaValue::QWord(load_base as i64 + self.r_addend))),
|
||||
_ => todo!(
|
||||
"Unsupported relocation against NULL symbol: {}",
|
||||
self.r_type
|
||||
),
|
||||
},
|
||||
_ => {
|
||||
let s = symbol.value() as i64;
|
||||
if s == 0 {
|
||||
todo!()
|
||||
}
|
||||
match self.r_type {
|
||||
// Direct 64 bit: S
|
||||
R_AARCH64_ABS64 | R_AARCH64_JUMP_SLOT | R_AARCH64_GLOB_DAT => {
|
||||
Ok(Some(RelaValue::QWord(s)))
|
||||
}
|
||||
_ => todo!("Unsupported relocation: {}", self.r_type),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,12 +2,15 @@ use elf::{parse::ParseAt, relocation::{Rel, Rela}};
|
||||
|
||||
use crate::{error::Error, mapping::Mapping, object::ResolvedSymbol, state::State};
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
mod aarch64;
|
||||
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
||||
mod x86_64;
|
||||
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
||||
mod i686;
|
||||
|
||||
pub enum RelaValue {
|
||||
DQWord(i64, i64),
|
||||
QWord(i64),
|
||||
}
|
||||
|
||||
@ -40,7 +43,17 @@ pub trait Relocation {
|
||||
impl RelocationValue for RelaValue {
|
||||
fn write(&self, mapping: &mut Mapping, offset: u64) {
|
||||
match *self {
|
||||
Self::QWord(value) => unsafe { mapping.qword(offset).write_unaligned(value) },
|
||||
Self::DQWord(v0, v1) => {
|
||||
unsafe {
|
||||
mapping.qword(offset).write_unaligned(v0);
|
||||
mapping.qword(offset + 8).write_unaligned(v1);
|
||||
}
|
||||
}
|
||||
Self::QWord(value) => unsafe {
|
||||
let ptr = mapping.qword(offset);
|
||||
// yggdrasil_rt::debug_trace!("write({:p}, {:#x})", ptr, value);
|
||||
ptr.write_unaligned(value)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ impl Relocation for Rel {
|
||||
_symbol: &ResolvedSymbol,
|
||||
_load_base: usize,
|
||||
) -> Result<Option<Self::Value>, Error> {
|
||||
unimplemented!("rel-type relocations are not implemented for i686")
|
||||
unimplemented!("rel-type relocations are not implemented for x86_64")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,26 @@
|
||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86", rust_analyzer))]
|
||||
mod variant2;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use elf::segment::ProgramHeader;
|
||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86", rust_analyzer))]
|
||||
pub use variant2::TlsLayoutImpl;
|
||||
use yggdrasil_rt::mem::MappingFlags;
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
use crate::{error::Error, mapping::Mapping, object::Object};
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(rust_analyzer)] {
|
||||
mod variant1;
|
||||
mod variant2;
|
||||
pub use variant2::TlsLayoutImpl;
|
||||
} else if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] {
|
||||
mod variant2;
|
||||
pub use variant2::TlsLayoutImpl;
|
||||
} else if #[cfg(target_arch = "aarch64")] {
|
||||
mod variant1;
|
||||
pub use variant1::TlsLayoutImpl;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TlsLayoutBuilder {
|
||||
fn build(align: usize, root: &Object, libraries: &HashMap<u32, Object>) -> TlsLayout;
|
||||
fn build(root: &Object, libraries: &HashMap<u32, Object>) -> Option<(TlsLayout, usize)>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -18,6 +28,7 @@ pub struct TlsLayout {
|
||||
pub segments: Vec<TlsSegment>,
|
||||
pub total_size: usize,
|
||||
pub tp_offset: usize,
|
||||
pub prefix_len: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -46,30 +57,20 @@ pub fn build_tls_image(
|
||||
root: &mut Object,
|
||||
libraries: &mut HashMap<u32, Object>,
|
||||
) -> Result<Option<(TlsImage, TlsLayout)>, Error> {
|
||||
// Find max align
|
||||
let mut align = usize::MIN;
|
||||
if let Some(tls) = root.tls.as_ref() {
|
||||
align = align.max(tls.p_align as usize);
|
||||
}
|
||||
for tls in libraries.values().filter_map(|lib| lib.tls.as_ref()) {
|
||||
align = align.max(tls.p_align as usize);
|
||||
}
|
||||
|
||||
// No TLS segment in any of the objects
|
||||
if align == usize::MIN {
|
||||
let Some((layout, align)) = TlsLayoutImpl::build(root, libraries) else {
|
||||
return Ok(None);
|
||||
}
|
||||
let align = align.max(size_of::<usize>());
|
||||
|
||||
let layout = TlsLayoutImpl::build(align, root, libraries);
|
||||
};
|
||||
|
||||
// Create a master image based on this layout
|
||||
let mut image_data = Mapping::new(layout.total_size, MappingFlags::empty())?;
|
||||
|
||||
let tp_offset = layout.tp_offset;
|
||||
for (i, segment) in layout.segments.iter().enumerate() {
|
||||
|
||||
for segment in layout.segments.iter() {
|
||||
debug_trace!(
|
||||
"Load TLS segment: tlsoffset_i={:#x?}, module_id={}",
|
||||
"Load TLS segment: tlsoffset_i={:#x?} (-{:#x}), module_id={}",
|
||||
segment.offset..segment.offset + segment.size,
|
||||
layout.prefix_len,
|
||||
segment.object_id
|
||||
);
|
||||
|
||||
@ -78,7 +79,8 @@ pub fn build_tls_image(
|
||||
_ => libraries.get_mut(&segment.object_id).unwrap(),
|
||||
};
|
||||
object.tls_offset = Some(segment.offset);
|
||||
object.place_tls_copy(&mut image_data[segment.offset..segment.offset + segment.size])?;
|
||||
let load_offset = segment.offset - layout.prefix_len;
|
||||
object.place_tls_copy(&mut image_data[load_offset..load_offset + segment.size])?;
|
||||
}
|
||||
|
||||
Ok(Some((
|
||||
|
77
userspace/dyn-loader/src/thread_local/variant1.rs
Normal file
77
userspace/dyn-loader/src/thread_local/variant1.rs
Normal file
@ -0,0 +1,77 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{object::Object, thread_local::TlsSegment};
|
||||
|
||||
use super::{TlsLayout, TlsLayoutBuilder};
|
||||
|
||||
pub struct TlsLayoutImpl;
|
||||
|
||||
impl TlsLayoutBuilder for TlsLayoutImpl {
|
||||
fn build(root: &Object, libraries: &HashMap<u32, Object>) -> Option<(TlsLayout, usize)> {
|
||||
const TCB_SIZE: usize = size_of::<usize>() * 2;
|
||||
|
||||
// Variant I doesn't use global align for modules. Instead, it aligns each of those
|
||||
// individually
|
||||
|
||||
let mut segments = vec![];
|
||||
let mut total_size = TCB_SIZE;
|
||||
let mut first_module_align = 0;
|
||||
|
||||
if let Some(tls) = root.tls.as_ref() {
|
||||
let align = tls.p_align as usize;
|
||||
assert!(align.is_power_of_two());
|
||||
|
||||
if first_module_align == 0 {
|
||||
first_module_align = align;
|
||||
}
|
||||
|
||||
total_size = (total_size + align - 1) & !(align - 1);
|
||||
|
||||
segments.push(TlsSegment {
|
||||
offset: total_size,
|
||||
size: tls.p_memsz as _,
|
||||
object_id: 0,
|
||||
});
|
||||
|
||||
total_size += tls.p_memsz as usize;
|
||||
}
|
||||
|
||||
for (&object_id, lib) in libraries {
|
||||
let Some(tls) = lib.tls.as_ref() else {
|
||||
continue;
|
||||
};
|
||||
let align = tls.p_align as usize;
|
||||
assert!(align.is_power_of_two());
|
||||
|
||||
if first_module_align == 0 {
|
||||
first_module_align = align;
|
||||
}
|
||||
|
||||
total_size = (total_size + align - 1) & !(align - 1);
|
||||
|
||||
segments.push(TlsSegment {
|
||||
offset: total_size,
|
||||
size: tls.p_memsz as _,
|
||||
object_id,
|
||||
});
|
||||
|
||||
total_size += tls.p_memsz as usize;
|
||||
}
|
||||
|
||||
if first_module_align == 0 {
|
||||
assert!(segments.is_empty());
|
||||
None
|
||||
} else {
|
||||
let prefix_len = (TCB_SIZE + first_module_align - 1) & !(first_module_align - 1);
|
||||
Some((
|
||||
TlsLayout {
|
||||
segments,
|
||||
total_size,
|
||||
tp_offset: 0,
|
||||
prefix_len,
|
||||
},
|
||||
first_module_align,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
@ -9,7 +9,22 @@ use super::{TlsLayout, TlsLayoutBuilder, TlsSegment};
|
||||
pub struct TlsLayoutImpl;
|
||||
|
||||
impl TlsLayoutBuilder for TlsLayoutImpl {
|
||||
fn build(align: usize, root: &Object, libraries: &HashMap<u32, Object>) -> TlsLayout {
|
||||
fn build(root: &Object, libraries: &HashMap<u32, Object>) -> Option<(TlsLayout, usize)> {
|
||||
// Find max align
|
||||
let mut align = usize::MIN;
|
||||
if let Some(tls) = root.tls.as_ref() {
|
||||
align = align.max(tls.p_align as usize);
|
||||
}
|
||||
for tls in libraries.values().filter_map(|lib| lib.tls.as_ref()) {
|
||||
align = align.max(tls.p_align as usize);
|
||||
}
|
||||
|
||||
// No TLS segment in any of the objects
|
||||
if align == usize::MIN {
|
||||
return None;
|
||||
}
|
||||
let align = align.max(size_of::<usize>());
|
||||
|
||||
let mut total_size = 0;
|
||||
// Layout:
|
||||
//
|
||||
@ -54,12 +69,16 @@ impl TlsLayoutBuilder for TlsLayoutImpl {
|
||||
offset,
|
||||
size,
|
||||
object_id,
|
||||
}).collect();
|
||||
})
|
||||
.collect();
|
||||
|
||||
TlsLayout {
|
||||
segments,
|
||||
total_size,
|
||||
tp_offset
|
||||
}
|
||||
Some((
|
||||
TlsLayout {
|
||||
segments,
|
||||
total_size,
|
||||
tp_offset,
|
||||
},
|
||||
align,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
6
userspace/lib/ygglibc/crt/aarch64/crt0.c
Normal file
6
userspace/lib/ygglibc/crt/aarch64/crt0.c
Normal file
@ -0,0 +1,6 @@
|
||||
extern void __ygglibc_entry(const void *, const void *);
|
||||
extern int main(int, const char **, const char **);
|
||||
|
||||
void _start(const void *arg) {
|
||||
__ygglibc_entry(main, arg);
|
||||
}
|
28
userspace/lib/ygglibc/etc/aarch64-unknown-none.json
Normal file
28
userspace/lib/ygglibc/etc/aarch64-unknown-none.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"is-builtin": false,
|
||||
"arch": "aarch64",
|
||||
"os": "none",
|
||||
"llvm-target": "aarch64-unknown-none",
|
||||
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32",
|
||||
"max-atomic-width": 128,
|
||||
"target-pointer-width": "64",
|
||||
"features": "+v8a,+strict-align,+neon,+fp-armv8",
|
||||
|
||||
"disable-redzone": true,
|
||||
"executables": true,
|
||||
"panic-strategy": "abort",
|
||||
"dynamic-linking": true,
|
||||
"relocation-model": "pic",
|
||||
"position-independent-executables": true,
|
||||
"crt-static-allows-dylibs": true,
|
||||
|
||||
"has-thread-local": true,
|
||||
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld",
|
||||
"pre-link-args": {
|
||||
"ld.lld": [
|
||||
"--dynamic-linker=/libexec/dyn-loader"
|
||||
]
|
||||
}
|
||||
}
|
37
userspace/lib/ygglibc/src/headers/setjmp/aarch64.rs
Normal file
37
userspace/lib/ygglibc/src/headers/setjmp/aarch64.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use core::ffi::c_int;
|
||||
|
||||
pub type __jmp_buf = [usize; 8];
|
||||
|
||||
// TODO
|
||||
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn setjmp(buf: *mut __jmp_buf) -> c_int {
|
||||
core::arch::naked_asm!(
|
||||
r#"
|
||||
b .
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn _setjmp(buf: *mut __jmp_buf) -> c_int {
|
||||
core::arch::naked_asm!("b .");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn longjmp(buf: *mut __jmp_buf, val: c_int) -> c_int {
|
||||
core::arch::naked_asm!(
|
||||
r#"
|
||||
b .
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
unsafe extern "C" fn _longjmp(buf: *mut __jmp_buf, val: c_int) -> c_int {
|
||||
core::arch::naked_asm!("b .")
|
||||
}
|
@ -8,4 +8,9 @@ pub mod i686;
|
||||
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
||||
pub use i686::__jmp_buf;
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
pub mod aarch64;
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
pub use aarch64::__jmp_buf;
|
||||
|
||||
pub type jmp_buf = __jmp_buf;
|
||||
|
@ -22,6 +22,9 @@ pub mod tls;
|
||||
|
||||
static THREADS: Mutex<BTreeMap<pthread_t, Arc<Thread>>> = Mutex::new(BTreeMap::new());
|
||||
|
||||
#[thread_local]
|
||||
static mut SELF: Option<Arc<Thread>> = None;
|
||||
|
||||
enum ThreadStack {
|
||||
Owned(usize, usize),
|
||||
Borrowed,
|
||||
@ -114,24 +117,16 @@ impl Thread {
|
||||
}
|
||||
|
||||
pub fn this() -> EResult<Arc<Thread>> {
|
||||
let tcb: *const *const Self = thread_local::get_tcb().cast();
|
||||
let raw = unsafe { tcb.read() };
|
||||
if !raw.is_null() {
|
||||
// Arc::from_raw creates an owned Arc, which will decrement refcount when dropped,
|
||||
// prevent this by incrementing the count once more
|
||||
unsafe { Arc::increment_strong_count(raw) };
|
||||
let arc = unsafe { Arc::from_raw(raw) };
|
||||
|
||||
EResult::Ok(arc)
|
||||
} else {
|
||||
panic!("pthread_self() is NULL")
|
||||
}
|
||||
let ptr = unsafe { SELF.clone() }.expect("pthread_self() is NULL");
|
||||
EResult::Ok(ptr)
|
||||
}
|
||||
|
||||
unsafe fn set_this(thread: Arc<Self>) {
|
||||
let tcb: *mut usize = thread_local::get_tcb().cast();
|
||||
let raw = Arc::into_raw(thread);
|
||||
tcb.write(raw.addr());
|
||||
unsafe { SELF = Some(thread) };
|
||||
}
|
||||
|
||||
unsafe fn clear_this() {
|
||||
unsafe { SELF = None };
|
||||
}
|
||||
|
||||
pub fn result(&self) -> *mut c_void {
|
||||
@ -142,7 +137,7 @@ impl Thread {
|
||||
// Set up TLS as soon as possible. Note the `force = true` parameter, because the image
|
||||
// contains "already initialized" tag, which only matters for the main thread.
|
||||
if let Err(err) =
|
||||
unsafe { thread_local::init_tls(tls::TLS_IMAGE.as_ref(), true, size_of::<usize>() * 4) }
|
||||
unsafe { thread_local::init_tls(tls::TLS_IMAGE.as_ref(), true) }
|
||||
{
|
||||
yggdrasil_rt::debug_trace!("thread_entry failed: TLS init error: {err:?}");
|
||||
unsafe { yggdrasil_rt::sys::exit_thread() };
|
||||
@ -169,9 +164,7 @@ impl Thread {
|
||||
let result = (argument.entry)(argument.argument);
|
||||
argument.thread.result.store(result, Ordering::Release);
|
||||
|
||||
// Drop the Arc<Self> written to TLS
|
||||
let this = Self::this().expect("pthread_self() is NULL");
|
||||
unsafe { Arc::decrement_strong_count(Arc::as_ptr(&this)) };
|
||||
unsafe { Self::clear_this() };
|
||||
}
|
||||
|
||||
// TODO call thread-local destructors
|
||||
@ -194,7 +187,7 @@ pub fn init_main_thread(arg: &ProgramArgumentInner) {
|
||||
// Usually, a dynamic loader will do this for us, but this still needs to be
|
||||
// done for statically-linked programs if the kernel transfers control directly to
|
||||
// the program.
|
||||
let tls_image = thread_local::init_tls_from_auxv(arg.auxv(), false, size_of::<usize>() * 4)
|
||||
let tls_image = thread_local::init_tls_from_auxv(arg.auxv(), false)
|
||||
.expect("Could not initialize TLS");
|
||||
|
||||
// Store the TLS image, it'll be needed when creating new threads
|
||||
|
@ -5,7 +5,7 @@ use std::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
env::{Arch, BuildEnv},
|
||||
env::BuildEnv,
|
||||
error::Error,
|
||||
util::{self, git_clone},
|
||||
};
|
||||
@ -113,29 +113,29 @@ fn build_test_c_program(
|
||||
|
||||
install.push((target_dir.join("c-test"), "c-test".into()));
|
||||
|
||||
if env.arch == Arch::x86_64 {
|
||||
log::info!("Building a test C program [static]");
|
||||
let mut command = llvm.c_clang(env);
|
||||
command
|
||||
.args([
|
||||
"-v",
|
||||
"-static",
|
||||
"-O0",
|
||||
"-ggdb",
|
||||
"-fstack-protector-strong",
|
||||
"-lm",
|
||||
])
|
||||
.arg("-o")
|
||||
.arg(target_dir.join("c-test-static"))
|
||||
.arg(env.workspace_root.join("test.c"));
|
||||
// if env.arch == Arch::x86_64 {
|
||||
log::info!("Building a test C program [static]");
|
||||
let mut command = llvm.c_clang(env);
|
||||
command
|
||||
.args([
|
||||
"-v",
|
||||
"-static",
|
||||
"-O0",
|
||||
"-ggdb",
|
||||
"-fstack-protector-strong",
|
||||
"-lm",
|
||||
])
|
||||
.arg("-o")
|
||||
.arg(target_dir.join("c-test-static"))
|
||||
.arg(env.workspace_root.join("test.c"));
|
||||
|
||||
if !command.status()?.success() {
|
||||
return Err(Error::ExternalCommandFailed);
|
||||
}
|
||||
|
||||
install.push((target_dir.join("c-test-static"), "c-test-static".into()));
|
||||
if !command.status()?.success() {
|
||||
return Err(Error::ExternalCommandFailed);
|
||||
}
|
||||
|
||||
install.push((target_dir.join("c-test-static"), "c-test-static".into()));
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -188,6 +188,7 @@ fn build_openlibm(env: &BuildEnv, llvm: &Llvm) -> Result<Openlibm, Error> {
|
||||
let mut command = Command::new("make");
|
||||
command.env("SYSROOT", &env.llvm_sysroot);
|
||||
command.env("ARCH", env.arch.name());
|
||||
command.env("OS", "yggdrasil");
|
||||
command.env("TRIPLE", format!("{}-unknown-yggdrasil", env.arch.name()));
|
||||
command.env("USEGCC", "0");
|
||||
command.env("USECLANG", "1");
|
||||
@ -265,7 +266,6 @@ pub fn build_c(env: &BuildEnv, install: &mut Vec<(PathBuf, PathBuf)>) -> Result<
|
||||
build_test_c_program(env, &llvm, install)?;
|
||||
build_test_cpp_program(env, &llvm, install)?;
|
||||
|
||||
// TODO
|
||||
install.push((
|
||||
env.llvm_sysroot.join("lib/libc++.so"),
|
||||
"lib/libc++.so".into(),
|
||||
|
@ -9,7 +9,7 @@ use walkdir::WalkDir;
|
||||
use crate::{
|
||||
build::{c, cargo::CargoBuilder},
|
||||
check::AllOk,
|
||||
env::{Arch, BuildEnv},
|
||||
env::BuildEnv,
|
||||
error::Error,
|
||||
util,
|
||||
};
|
||||
@ -73,9 +73,7 @@ fn build_userspace(
|
||||
CargoBuilder::Userspace(env).build(env.workspace_root.join("userspace"))?;
|
||||
CargoBuilder::Userspace(env).build(env.workspace_root.join("userspace/dynload-program"))?;
|
||||
// TODO other architectures
|
||||
if env.arch == Arch::x86_64 || env.arch == Arch::i686 {
|
||||
c::build_c(env, extra)?;
|
||||
}
|
||||
c::build_c(env, extra)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user