feat(ld.so): shared library
Signed-off-by: Anhad Singh <andypython@protonmail.com>
This commit is contained in:
@@ -133,6 +133,10 @@ $(BUILD)/$(PROFILE)/libc.so: $(BUILD)/$(PROFILE)/librelibc.a $(BUILD)/openlibm/l
|
||||
$(LINKFLAGS) \
|
||||
-o $@
|
||||
|
||||
$(BUILD)/$(PROFILE)/ld_so: $(BUILD)/$(PROFILE)/ld_so.o $(BUILD)/$(PROFILE)/crti.o $(BUILD)/$(PROFILE)/libc.a $(BUILD)/$(PROFILE)/crtn.o
|
||||
# TODO: merge ld.so with libc.so: --dynamic-list=dynamic-list-file
|
||||
$(LD) --shared -Bsymbolic --no-relax -T ld_so/ld_script/$(TARGET).ld --allow-multiple-definition --gc-sections $^ -o $@
|
||||
|
||||
# Debug targets
|
||||
|
||||
$(BUILD)/debug/libc.a: $(BUILD)/debug/librelibc.a $(BUILD)/openlibm/libopenlibm.a
|
||||
@@ -145,7 +149,7 @@ $(BUILD)/debug/libc.a: $(BUILD)/debug/librelibc.a $(BUILD)/openlibm/libopenlibm.
|
||||
$(AR) -M < "$@.mri"
|
||||
|
||||
$(BUILD)/debug/librelibc.a: $(SRC)
|
||||
$(CARGO) rustc $(CARGOFLAGS) -- --emit link=$@ -g -C debug-assertions=no $(RUSTCFLAGS)
|
||||
$(CARGO) rustc $(CARGOFLAGS) -- --emit link=$@ -g -C debug-assertions=no -C opt-level=2 $(RUSTCFLAGS)
|
||||
./renamesyms.sh "$@" "$(BUILD)/debug/deps/"
|
||||
./stripcore.sh "$@"
|
||||
touch $@
|
||||
@@ -166,9 +170,6 @@ $(BUILD)/debug/ld_so.o: $(SRC)
|
||||
$(CARGO) rustc --manifest-path ld_so/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort -g -C debug-assertions=no $(RUSTCFLAGS)
|
||||
touch $@
|
||||
|
||||
$(BUILD)/debug/ld_so: $(BUILD)/debug/ld_so.o $(BUILD)/debug/crti.o $(BUILD)/debug/libc.a $(BUILD)/debug/crtn.o
|
||||
$(LD) --no-relax -T ld_so/ld_script/$(TARGET).ld --allow-multiple-definition --gc-sections $^ -o $@
|
||||
|
||||
# Release targets
|
||||
|
||||
$(BUILD)/release/libc.a: $(BUILD)/release/librelibc.a $(BUILD)/openlibm/libopenlibm.a
|
||||
@@ -204,9 +205,6 @@ $(BUILD)/release/ld_so.o: $(SRC)
|
||||
$(CARGO) rustc --release --manifest-path ld_so/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
||||
touch $@
|
||||
|
||||
$(BUILD)/release/ld_so: $(BUILD)/release/ld_so.o $(BUILD)/release/crti.o $(BUILD)/release/libc.a $(BUILD)/release/crtn.o
|
||||
$(LD) --no-relax -T ld_so/ld_script/$(TARGET).ld --allow-multiple-definition --gc-sections $^ -o $@
|
||||
|
||||
# Other targets
|
||||
|
||||
$(BUILD)/openlibm: openlibm
|
||||
|
||||
+11
-5
@@ -7,14 +7,18 @@ use core::arch::global_asm;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
global_asm!(
|
||||
"
|
||||
.weak _DYNAMIC
|
||||
.hidden _DYNAMIC
|
||||
|
||||
.global _start
|
||||
_start:
|
||||
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)
|
||||
adrp x1, _DYNAMIC
|
||||
add x1, x1, #:lo12:_DYNAMIC
|
||||
// ld_so_start(stack=x0, dynamic=x1)
|
||||
bl relibc_ld_so_start
|
||||
// restore original stack, clear registers, and jump to the new start function
|
||||
mov sp, x28
|
||||
@@ -69,16 +73,18 @@ _start:
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
global_asm!(
|
||||
"
|
||||
.weak _DYNAMIC
|
||||
.hidden _DYNAMIC
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
lea rsi, [rip + _start]
|
||||
|
||||
# Save original stack and align stack to 16 bytes
|
||||
mov rbp, rsp
|
||||
and rsp, 0xfffffffffffffff0
|
||||
|
||||
# Call ld_so_start(stack=rdi, ld_entry=rsi)
|
||||
# Call ld_so_start(stack=rdi, dynamic=rsi)
|
||||
mov rdi, rbp
|
||||
lea rsi, [rip + _DYNAMIC]
|
||||
call relibc_ld_so_start
|
||||
|
||||
# Restore original stack, clear registers, and jump to new start function
|
||||
|
||||
+39
-32
@@ -64,6 +64,11 @@ mod shim {
|
||||
|
||||
pub use shim::*;
|
||||
|
||||
// TODO: missing from the `object` crate
|
||||
pub const DT_RELRSZ: u32 = 35;
|
||||
pub const DT_RELR: u32 = 36;
|
||||
pub const DT_RELRENT: u32 = 37;
|
||||
|
||||
/// Undefined Symbol Index
|
||||
pub const STN_UNDEF: SymbolIndex = SymbolIndex(0);
|
||||
|
||||
@@ -662,10 +667,6 @@ impl DSO {
|
||||
is_pie: bool,
|
||||
(_, entries): (&ProgramHeader, &[Dyn]),
|
||||
) -> object::Result<(Dynamic<'a>, Option<usize>)> {
|
||||
const DT_RELRSZ: u32 = 35;
|
||||
const DT_RELR: u32 = 36;
|
||||
const DT_RELRENT: u32 = 37;
|
||||
|
||||
let mut runpath = None;
|
||||
let mut got = None;
|
||||
let mut needed = vec![];
|
||||
@@ -1078,34 +1079,8 @@ impl DSO {
|
||||
let global_scope = GLOBAL_SCOPE.read();
|
||||
let base = self.mmap.as_ptr();
|
||||
|
||||
// Apply DT_RELR relative relocations.
|
||||
let mut addr = ptr::null_mut();
|
||||
for &entry in self.dynamic.relr {
|
||||
if entry & 1 == 0 {
|
||||
// An even entry sets up `addr` for subsequent odd entries.
|
||||
unsafe {
|
||||
addr = base.add(entry) as *mut usize;
|
||||
*addr += base as usize;
|
||||
addr = addr.add(1);
|
||||
}
|
||||
} else {
|
||||
// An odd entry indicates a bitmap describing at maximum 63
|
||||
// (for 64-bit) or 31 (for 32-bit) locations following `addr`.
|
||||
// Odd entries can be chained.
|
||||
let mut entry = entry >> 1;
|
||||
let mut i = 0;
|
||||
while entry != 0 {
|
||||
if entry & 1 != 0 {
|
||||
unsafe {
|
||||
*addr.add(i) += base as usize;
|
||||
}
|
||||
}
|
||||
entry >>= 1;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
addr = unsafe { addr.add(CHAR_BITS * size_of::<Relr>() - 1) };
|
||||
}
|
||||
unsafe {
|
||||
apply_relr(base, self.dynamic.relr);
|
||||
}
|
||||
|
||||
self.dynamic
|
||||
@@ -1295,3 +1270,35 @@ __tlsdesc_dynamic:
|
||||
unimp
|
||||
"
|
||||
);
|
||||
|
||||
/// Applies [`DT_RELR`] relative relocations.
|
||||
pub unsafe fn apply_relr(base: *const u8, relr: &[Relr]) {
|
||||
let mut addr = ptr::null_mut();
|
||||
for &entry in relr {
|
||||
if entry & 1 == 0 {
|
||||
// An even entry sets up `addr` for subsequent odd entries.
|
||||
unsafe {
|
||||
addr = base.add(entry) as *mut usize;
|
||||
*addr += base as usize;
|
||||
addr = addr.add(1);
|
||||
}
|
||||
} else {
|
||||
// An odd entry indicates a bitmap describing at maximum 63
|
||||
// (for 64-bit) or 31 (for 32-bit) locations following `addr`.
|
||||
// Odd entries can be chained.
|
||||
let mut entry = entry >> 1;
|
||||
let mut i = 0;
|
||||
while entry != 0 {
|
||||
if entry & 1 != 0 {
|
||||
unsafe {
|
||||
*addr.add(i) += base as usize;
|
||||
}
|
||||
}
|
||||
entry >>= 1;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
addr = unsafe { addr.add(CHAR_BITS * size_of::<Relr>() - 1) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+98
-26
@@ -9,15 +9,19 @@ use alloc::{
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use object::{NativeEndian, elf::PT_PHDR, read::elf::ProgramHeader as _};
|
||||
use object::{
|
||||
NativeEndian,
|
||||
elf::{self, PT_PHDR},
|
||||
read::elf::{Dyn as _, ProgramHeader as _, Rela as _},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
c_str::CStr,
|
||||
header::{
|
||||
elf::{AT_ENTRY, AT_PHDR, AT_PHENT, AT_PHNUM},
|
||||
elf::{AT_BASE, AT_PHDR, AT_PHENT, AT_PHNUM, PT_DYNAMIC},
|
||||
unistd,
|
||||
},
|
||||
ld_so::dso::ProgramHeader,
|
||||
ld_so::dso::{DT_RELR, DT_RELRENT, DT_RELRSZ, Dyn, ProgramHeader, Rela, Relr, apply_relr},
|
||||
platform::{auxv_iter, get_auxvs, types::c_char},
|
||||
start::Stack,
|
||||
sync::mutex::Mutex,
|
||||
@@ -153,35 +157,111 @@ fn resolve_path_name(
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) -> usize {
|
||||
pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, dynamic: *const Dyn) -> usize {
|
||||
let mut at_phdr = None;
|
||||
let mut at_phnum = None;
|
||||
let mut at_phent = None;
|
||||
let mut at_entry = None;
|
||||
let mut at_base = None;
|
||||
for [kind, value] in unsafe { auxv_iter(sp.auxv().cast::<usize>()) } {
|
||||
match kind {
|
||||
AT_PHDR => at_phdr = Some(value as *const ProgramHeader),
|
||||
AT_PHNUM => at_phnum = Some(value),
|
||||
AT_PHENT => at_phent = Some(value),
|
||||
AT_ENTRY => at_entry = Some(value),
|
||||
AT_BASE => at_base = Some(value),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let at_entry = at_entry.expect("`AT_ENTRY` must be present");
|
||||
let (is_manual, base_addr) = if at_entry == ld_entry {
|
||||
(true, None)
|
||||
let at_phdr = at_phdr.unwrap();
|
||||
let at_phnum = at_phnum.expect("`AT_PHNUM` must be present if `AT_PHDR` is");
|
||||
let at_phent = at_phent.expect("`AT_PHENT` must be present if `AT_PHDR` is");
|
||||
assert!(!at_phdr.is_null() && at_phnum != 0 && at_phent == size_of::<ProgramHeader>());
|
||||
let phdrs = unsafe { slice::from_raw_parts(at_phdr, at_phnum) };
|
||||
|
||||
// [`AT_BASE`] is the base address at which the dynamic linker was loaded in memory. On Linux,
|
||||
// this entry is always present. If the dynamic linker was not loaded (i.e. run as a command),
|
||||
// its value is 0. On Redox, it is only present if the dynamic linker is loaded.
|
||||
let at_base = at_base.unwrap_or_default();
|
||||
|
||||
let (is_manual, self_base) = if at_base != 0 {
|
||||
(false, at_base)
|
||||
} else {
|
||||
// Dynamic linker was run as a command.
|
||||
let ph = phdrs
|
||||
.iter()
|
||||
.find(|ph| ph.p_type(NativeEndian) == PT_DYNAMIC as u32)
|
||||
.unwrap();
|
||||
(true, unsafe {
|
||||
dynamic.byte_sub(ph.p_vaddr(NativeEndian) as usize) as usize
|
||||
})
|
||||
};
|
||||
|
||||
let mut i = dynamic;
|
||||
let mut rela_ptr = None;
|
||||
let mut rela_len = None;
|
||||
let mut relr_ptr = None;
|
||||
let mut relr_len = None;
|
||||
loop {
|
||||
let entry = unsafe { &*i };
|
||||
let val = entry.d_val(NativeEndian);
|
||||
let ptr = val as *const u8;
|
||||
match entry.d_tag(NativeEndian) as u32 {
|
||||
elf::DT_NULL => break,
|
||||
elf::DT_RELA => rela_ptr = Some(ptr.cast::<Rela>()),
|
||||
elf::DT_RELASZ => rela_len = Some(val as usize / size_of::<Rela>()),
|
||||
elf::DT_RELAENT => {
|
||||
assert_eq!(val as usize, size_of::<Rela>(),);
|
||||
}
|
||||
DT_RELR => relr_ptr = Some(ptr.cast::<Relr>()),
|
||||
DT_RELRSZ => relr_len = Some(val as usize / size_of::<Relr>()),
|
||||
DT_RELRENT => {
|
||||
assert_eq!(val as usize, size_of::<Relr>());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
i = unsafe { i.add(1) };
|
||||
}
|
||||
|
||||
unsafe fn get_array<'a, T>(
|
||||
ptr: Option<*const T>,
|
||||
len: Option<usize>,
|
||||
base_addr: usize,
|
||||
) -> &'a [T] {
|
||||
if let Some(ptr) = ptr {
|
||||
let len = len.expect("dynamic entry was present without it's corresponding size");
|
||||
unsafe { core::slice::from_raw_parts(ptr.byte_add(base_addr), len) }
|
||||
} else {
|
||||
assert!(len.is_none());
|
||||
&[]
|
||||
}
|
||||
}
|
||||
|
||||
for rela in unsafe { get_array(rela_ptr, rela_len, self_base) } {
|
||||
let offset = rela.r_offset(NativeEndian);
|
||||
let addend = rela.r_addend(NativeEndian) as isize;
|
||||
let reloc_ptr = (offset + self_base as u64) as *mut usize;
|
||||
match rela.r_type(NativeEndian, false) as u32 {
|
||||
elf::R_X86_64_RELATIVE => {
|
||||
unsafe { reloc_ptr.write((self_base as isize + addend) as usize) };
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let relr = get_array(relr_ptr, relr_len, self_base);
|
||||
apply_relr(self_base as *const u8, relr);
|
||||
}
|
||||
|
||||
println!(
|
||||
"[ld.so]: relocated self at {self_base:#x} (DT_RELASZ={rela_len:?}, DT_RELRSZ={relr_len:?})"
|
||||
);
|
||||
|
||||
let mut base_addr = None;
|
||||
if is_manual && cfg!(target_os = "linux") {
|
||||
// if we are not running in manual mode, then the main
|
||||
// program is already loaded by the kernel and we want
|
||||
// to use it. on redox, we treat it the same.
|
||||
let at_phdr = at_phdr.unwrap();
|
||||
let at_phnum = at_phnum.expect("`AT_PHNUM` must be present if `AT_PHDR` is");
|
||||
let at_phent = at_phent.expect("`AT_PHENT` must be present if `AT_PHDR` is");
|
||||
assert!(!at_phdr.is_null() && at_phnum != 0 && at_phent == size_of::<ProgramHeader>());
|
||||
let phdrs = unsafe { slice::from_raw_parts(at_phdr, at_phnum) };
|
||||
|
||||
let mut base_addr = None;
|
||||
for ph in phdrs.iter() {
|
||||
if ph.p_type(NativeEndian) == PT_PHDR {
|
||||
assert!(base_addr.is_none(), "`PT_PHDR` cannot occur more than once");
|
||||
@@ -193,15 +273,7 @@ pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: us
|
||||
} as usize);
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
false,
|
||||
#[cfg(target_os = "redox")]
|
||||
None,
|
||||
#[cfg(target_os = "linux")]
|
||||
Some(base_addr.expect("`PT_PHDR` must be present for executables")),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// Setup TCB for ourselves.
|
||||
unsafe {
|
||||
@@ -279,7 +351,7 @@ pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: us
|
||||
}
|
||||
|
||||
// we might need global lock for this kind of stuff
|
||||
_r_debug.lock().r_ldbase = ld_entry;
|
||||
_r_debug.lock().r_ldbase = self_base;
|
||||
|
||||
// TODO: Fix memory leak, although minimal.
|
||||
#[cfg(target_os = "redox")]
|
||||
|
||||
Reference in New Issue
Block a user