Merge branch 'master' into 'master'

fix(ld_so): dynamic linker on aarch64

See merge request redox-os/relibc!788
This commit is contained in:
Jeremy Soller
2025-12-09 06:28:14 -07:00
7 changed files with 288 additions and 103 deletions
+2 -1
View File
@@ -1,3 +1,4 @@
{
"cmake.ignoreCMakeListsMissing": true
"cmake.ignoreCMakeListsMissing": true,
"editor.formatOnSave": true
}
+7 -75
View File
@@ -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
+12 -7
View File
@@ -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
+73
View File
@@ -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
+40 -4
View File
@@ -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
"
);
+142 -8
View File
@@ -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),
);
+12 -8
View File
@@ -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