diff --git a/.vscode/settings.json b/.vscode/settings.json index 9ddf6b280c..731634dd32 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "cmake.ignoreCMakeListsMissing": true + "cmake.ignoreCMakeListsMissing": true, + "editor.formatOnSave": true } \ No newline at end of file diff --git a/Makefile b/Makefile index 47ba77c97f..bf5570fc5e 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,4 @@ -ifndef TARGET - export TARGET:=$(shell rustc -Z unstable-options --print target-spec-json | grep llvm-target | cut -d '"' -f4) -endif +include config.mk CARGO?=cargo CARGO_TEST?=$(CARGO) @@ -23,76 +21,6 @@ HEADERS_UNPARSED=$(shell find src/header -mindepth 1 -maxdepth 1 -type d -not -n HEADERS_DEPS=$(shell find src/header -type f \( -name "cbindgen.toml" -o -name "*.rs" \)) #HEADERS=$(patsubst %,%.h,$(subst _,/,$(HEADERS_UNPARSED))) -ifeq ($(TARGET),aarch64-unknown-linux-gnu) - export CC=aarch64-linux-gnu-gcc - export LD=aarch64-linux-gnu-ld - export AR=aarch64-linux-gnu-ar - export NM=aarch64-linux-gnu-nm - export OBJCOPY=aarch64-linux-gnu-objcopy - export CPPFLAGS= - LD_SO_PATH=lib/ld.so.1 -endif - -ifeq ($(TARGET),aarch64-unknown-redox) - export CC=aarch64-unknown-redox-gcc - export LD=aarch64-unknown-redox-ld - export AR=aarch64-unknown-redox-ar - export NM=aarch64-unknown-redox-nm - export OBJCOPY=aarch64-unknown-redox-objcopy - export CPPFLAGS= - LD_SO_PATH=lib/ld.so.1 -endif - -ifeq ($(TARGET),i586-unknown-redox) - export CC=i586-unknown-redox-gcc - export LD=i586-unknown-redox-ld - export AR=i586-unknown-redox-ar - export NM=i586-unknown-redox-nm - export OBJCOPY=i586-unknown-redox-objcopy - export CPPFLAGS= - LD_SO_PATH=lib/libc.so.1 -endif - -ifeq ($(TARGET),i686-unknown-redox) - export CC=i686-unknown-redox-gcc - export LD=i686-unknown-redox-ld - export AR=i686-unknown-redox-ar - export NM=i686-unknown-redox-nm - export OBJCOPY=i686-unknown-redox-objcopy - export CPPFLAGS= - LD_SO_PATH=lib/libc.so.1 -endif - -ifeq ($(TARGET),x86_64-unknown-linux-gnu) - export CC=x86_64-linux-gnu-gcc - export LD=x86_64-linux-gnu-ld - export AR=x86_64-linux-gnu-ar - export NM=x86_64-linux-gnu-nm - export OBJCOPY=objcopy - export CPPFLAGS= - LD_SO_PATH=lib/ld64.so.1 -endif - -ifeq ($(TARGET),x86_64-unknown-redox) - export CC=x86_64-unknown-redox-gcc - export LD=x86_64-unknown-redox-ld - export AR=x86_64-unknown-redox-ar - export NM=x86_64-unknown-redox-nm - export OBJCOPY=x86_64-unknown-redox-objcopy - export CPPFLAGS= - LD_SO_PATH=lib/ld64.so.1 -endif - -ifeq ($(TARGET),riscv64gc-unknown-redox) - export CC=riscv64-unknown-redox-gcc - export LD=riscv64-unknown-redox-ld - export AR=riscv64-unknown-redox-ar - export NM=riscv64-unknown-redox-nm - export OBJCOPY=riscv64-unknown-redox-objcopy - export CPPFLAGS=-march=rv64gc -mabi=lp64d - LD_SO_PATH=lib/ld.so.1 -endif - SRC=\ Cargo.* \ $(shell find src -type f) @@ -171,14 +99,18 @@ submodules: git submodule update --init --recursive sysroot: + @mkdir -p $@ + +.PHONY: sysroot/$(TARGET) +sysroot/$(TARGET): | sysroot rm -rf $@ rm -rf $@.partial mkdir -p $@.partial - $(MAKE) install DESTDIR=$@.partial + $(MAKE) install DESTDIR=$(shell pwd)/$@.partial mv $@.partial $@ touch $@ -test: sysroot +test: sysroot/$(TARGET) # TODO: Fix SIGILL when running cargo test # $(CARGO_TEST) test $(MAKE) -C tests run diff --git a/README.md b/README.md index 3c803293f7..44d5cfc13f 100644 --- a/README.md +++ b/README.md @@ -153,17 +153,22 @@ tests/bins_static/test-name The Makefile expects GNU compiler tools prefixed with the platform specifier, as would be present when you installed a cross compiler. Since you are building for your own platform, some Linux distributions (like Manjaro) don't install/symlink the prefixed executables. -An easy fix would be to replace the corresponding lines in the Makefile, e.g. +An easy fix would be to replace the corresponding lines in `config.mk`, e.g. ```diff - ifeq ($(TARGET),x86_64-unknown-linux-gnu) - export CC=x86_64-linux-gnu-gcc -- export LD=x86_64-linux-gnu-ld -- export AR=x86_64-linux-gnu-ar +ifeq ($(TARGET),x86_64-unknown-linux-gnu) +- export CC=x86_64-linux-gnu-gcc +- export LD=x86_64-linux-gnu-ld +- export AR=x86_64-linux-gnu-ar +- export NM=x86_64-linux-gnu-nm ++ export CC=gcc + export LD=ld + export AR=ar - export OBJCOPY=x86_64-linux-gnu-objcopy - endif ++ export NM=nm + export OBJCOPY=objcopy + export CPPFLAGS= + LD_SO_PATH=lib/ld64.so.1 +endif ``` ## Contributing diff --git a/config.mk b/config.mk new file mode 100644 index 0000000000..e9ffa218ae --- /dev/null +++ b/config.mk @@ -0,0 +1,73 @@ +ifndef TARGET + export TARGET:=$(shell rustc -Z unstable-options --print target-spec-json | grep llvm-target | cut -d '"' -f4) +endif + +ifeq ($(TARGET),aarch64-unknown-linux-gnu) + export CC=aarch64-linux-gnu-gcc + export LD=aarch64-linux-gnu-ld + export AR=aarch64-linux-gnu-ar + export NM=aarch64-linux-gnu-nm + export OBJCOPY=aarch64-linux-gnu-objcopy + export CPPFLAGS= + LD_SO_PATH=lib/ld.so.1 +endif + +ifeq ($(TARGET),aarch64-unknown-redox) + export CC=aarch64-unknown-redox-gcc + export LD=aarch64-unknown-redox-ld + export AR=aarch64-unknown-redox-ar + export NM=aarch64-unknown-redox-nm + export OBJCOPY=aarch64-unknown-redox-objcopy + export CPPFLAGS= + LD_SO_PATH=lib/ld.so.1 +endif + +ifeq ($(TARGET),i586-unknown-redox) + export CC=i586-unknown-redox-gcc + export LD=i586-unknown-redox-ld + export AR=i586-unknown-redox-ar + export NM=i586-unknown-redox-nm + export OBJCOPY=i586-unknown-redox-objcopy + export CPPFLAGS= + LD_SO_PATH=lib/libc.so.1 +endif + +ifeq ($(TARGET),i686-unknown-redox) + export CC=i686-unknown-redox-gcc + export LD=i686-unknown-redox-ld + export AR=i686-unknown-redox-ar + export NM=i686-unknown-redox-nm + export OBJCOPY=i686-unknown-redox-objcopy + export CPPFLAGS= + LD_SO_PATH=lib/libc.so.1 +endif + +ifeq ($(TARGET),x86_64-unknown-linux-gnu) + export CC=x86_64-linux-gnu-gcc + export LD=x86_64-linux-gnu-ld + export AR=x86_64-linux-gnu-ar + export NM=x86_64-linux-gnu-nm + export OBJCOPY=objcopy + export CPPFLAGS= + LD_SO_PATH=lib/ld64.so.1 +endif + +ifeq ($(TARGET),x86_64-unknown-redox) + export CC=x86_64-unknown-redox-gcc + export LD=x86_64-unknown-redox-ld + export AR=x86_64-unknown-redox-ar + export NM=x86_64-unknown-redox-nm + export OBJCOPY=x86_64-unknown-redox-objcopy + export CPPFLAGS= + LD_SO_PATH=lib/ld64.so.1 +endif + +ifeq ($(TARGET),riscv64gc-unknown-redox) + export CC=riscv64-unknown-redox-gcc + export LD=riscv64-unknown-redox-ld + export AR=riscv64-unknown-redox-ar + export NM=riscv64-unknown-redox-nm + export OBJCOPY=riscv64-unknown-redox-objcopy + export CPPFLAGS=-march=rv64gc -mabi=lp64d + LD_SO_PATH=lib/ld.so.1 +endif diff --git a/ld_so/src/lib.rs b/ld_so/src/lib.rs index 634727fae5..b4c54565e3 100644 --- a/ld_so/src/lib.rs +++ b/ld_so/src/lib.rs @@ -6,12 +6,48 @@ use core::arch::global_asm; #[cfg(target_arch = "aarch64")] global_asm!( " -.globl _start +.global _start _start: - mov x0, sp - and sp, x0, #0xfffffffffffffff0 //align sp + mov x28, sp + // align stack to 16 bytes + and sp, x28, #0xfffffffffffffff0 + adr x1, _start + mov x0, x28 + // ld_so_start(stack=x0, ld_entry=x1) bl relibc_ld_so_start - # TODO: aarch64 + // restore original stack, clear registers, and jump to the new start function + mov sp, x28 + mov x1, xzr + mov x2, xzr + mov x3, xzr + mov x4, xzr + mov x5, xzr + mov x6, xzr + mov x7, xzr + mov x8, xzr + mov x9, xzr + mov x10, xzr + mov x11, xzr + mov x12, xzr + mov x13, xzr + mov x14, xzr + mov x15, xzr + mov x16, xzr + mov x17, xzr + mov x18, xzr + mov x19, xzr + mov x20, xzr + mov x21, xzr + mov x22, xzr + mov x23, xzr + mov x24, xzr + mov x25, xzr + mov x26, xzr + mov x27, xzr + mov x28, xzr + mov x29, xzr + mov x30, xzr + br x0 udf #0 " ); diff --git a/src/ld_so/dso.rs b/src/ld_so/dso.rs index bb6e4d2ee0..e29d327033 100644 --- a/src/ld_so/dso.rs +++ b/src/ld_so/dso.rs @@ -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::(), 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)> { 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), +); diff --git a/tests/Makefile b/tests/Makefile index d7d3f2bbdc..8fceefdbe1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,3 +1,5 @@ +include ../config.mk + SKIP_REDOX?=0 # If compiling for Redox, SKIP_REDOX must be 1 @@ -371,7 +373,9 @@ DYNAMIC_FLAGS=\ -Wl,--enable-new-dtags \ -Wl,-export-dynamic -../sysroot: +SYSROOT?=$(abspath ../sysroot/$(TARGET)/) + +$(SYSROOT): $(MAKE) -C .. sysroot NATIVE_RELIBC?=0 @@ -379,24 +383,24 @@ ifeq ($(NATIVE_RELIBC),0) FLAGS+=\ -nostdinc \ -nostdlib \ - -isystem ../sysroot/include \ - ../sysroot/lib/crt0.o \ - ../sysroot/lib/crti.o \ - ../sysroot/lib/crtn.o + -isystem $(SYSROOT)/include \ + $(SYSROOT)/lib/crt0.o \ + $(SYSROOT)/lib/crti.o \ + $(SYSROOT)/lib/crtn.o -SYSROOT_LIB=$(shell realpath ../sysroot/lib/) +SYSROOT_LIB=$(shell realpath ../sysroot/$(TARGET)/lib/) STATIC_FLAGS+=\ $(SYSROOT_LIB)/libc.a DYNAMIC_FLAGS+=\ - -Wl,-dynamic-linker=$(SYSROOT_LIB)/ld64.so.1 \ + -Wl,-dynamic-linker=$(SYSROOT)/$(LD_SO_PATH) \ -Wl,-rpath=$(SYSROOT_LIB):\$$ORIGIN \ -L $(SYSROOT_LIB) \ -lc \ -fpic -DEPS=../sysroot +DEPS=../sysroot/$(TARGET) else DYNAMIC_FLAGS+=\ -Wl,-rpath=\$$ORIGIN