Merge branch 'ld_preload_program' into 'master'
feat(ld.so): position independent See merge request redox-os/relibc!1007
This commit is contained in:
+46
-39
@@ -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);
|
||||
|
||||
@@ -170,11 +175,11 @@ unsafe impl Send for Dynamic<'_> {}
|
||||
unsafe impl Sync for Dynamic<'_> {}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Relocation {
|
||||
offset: usize,
|
||||
addend: Option<usize>,
|
||||
sym: SymbolIndex,
|
||||
kind: RelocationKind,
|
||||
pub(super) struct Relocation {
|
||||
pub(super) offset: usize,
|
||||
pub(super) addend: Option<usize>,
|
||||
pub(super) sym: SymbolIndex,
|
||||
pub(super) kind: RelocationKind,
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
@@ -522,8 +527,8 @@ impl DSO {
|
||||
};
|
||||
_r_debug
|
||||
.lock()
|
||||
.insert_first(addr, path, addr + l_ld as usize);
|
||||
slice::from_raw_parts_mut(addr as *mut u8, size)
|
||||
.insert_first(addr + bounds.0, path, addr + l_ld as usize);
|
||||
slice::from_raw_parts_mut((addr + bounds.0) as *mut u8, size)
|
||||
} else {
|
||||
let (start, end) = bounds;
|
||||
let size = end - start;
|
||||
@@ -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) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -348,7 +348,7 @@ bitflags::bitflags! {
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Config {
|
||||
debug_flags: DebugFlags,
|
||||
pub debug_flags: DebugFlags,
|
||||
library_path: Option<String>,
|
||||
/// Resolve symbols at program startup.
|
||||
bind_now: bool,
|
||||
|
||||
+174
-23
@@ -1,5 +1,7 @@
|
||||
// Start code adapted from https://gitlab.redox-os.org/redox-os/relibc/blob/master/src/start.rs
|
||||
|
||||
use core::slice;
|
||||
|
||||
use alloc::{
|
||||
borrow::ToOwned,
|
||||
boxed::Box,
|
||||
@@ -7,14 +9,26 @@ use alloc::{
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use object::{
|
||||
NativeEndian,
|
||||
elf::{self, PT_DYNAMIC, PT_PHDR},
|
||||
read::elf::{Dyn as _, ProgramHeader as _},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
c_str::CStr,
|
||||
header::{
|
||||
sys_auxv::{AT_ENTRY, AT_PHDR},
|
||||
elf::{AT_BASE, AT_ENTRY, AT_PHDR, AT_PHENT, AT_PHNUM},
|
||||
unistd,
|
||||
},
|
||||
platform::{get_auxv, get_auxvs, types::c_char},
|
||||
ld_so::{
|
||||
dso::{
|
||||
DT_RELR, DT_RELRENT, DT_RELRSZ, Dyn, ProgramHeader, Rel, Rela, Relocation,
|
||||
RelocationKind, Relr, apply_relr,
|
||||
},
|
||||
linker::DebugFlags,
|
||||
},
|
||||
platform::{auxv_iter, get_auxvs, types::c_char},
|
||||
start::Stack,
|
||||
sync::mutex::Mutex,
|
||||
};
|
||||
@@ -149,7 +163,155 @@ 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,
|
||||
ld_entry: usize,
|
||||
dynamic: *const Dyn,
|
||||
) -> usize {
|
||||
// Relocate ourselves.
|
||||
//
|
||||
// This function is very delicate as it must **not** contain relocations itself. References to
|
||||
// external symbols **cannot** be made until `stage2()` so, this function might not be very
|
||||
// elegant.
|
||||
//
|
||||
// At this stage the TCB is not setup either so `expect_notls` must be used instead of `expect`
|
||||
// and `unwrap`.
|
||||
let mut at_phdr = None;
|
||||
let mut at_phnum = None;
|
||||
let mut at_phent = None;
|
||||
let mut at_base = None;
|
||||
let mut at_entry = 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_BASE => at_base = Some(value),
|
||||
AT_ENTRY => at_entry = Some(value),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let at_phdr = at_phdr.expect_notls("`AT_PHDR` must be present");
|
||||
let at_phnum = at_phnum.expect_notls("`AT_PHNUM` must be present if `AT_PHDR` is");
|
||||
let at_phent = at_phent.expect_notls("`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 at_entry = at_entry.expect_notls("`AT_ENTRY` must be present");
|
||||
let at_base = at_base.unwrap_or_default();
|
||||
|
||||
let self_base = if at_base != 0 {
|
||||
at_base
|
||||
} else {
|
||||
let ph = phdrs
|
||||
.iter()
|
||||
.find(|ph| ph.p_type(NativeEndian) == PT_DYNAMIC as u32)
|
||||
.unwrap();
|
||||
unsafe { dynamic.byte_sub(ph.p_vaddr(NativeEndian) as usize) as usize }
|
||||
};
|
||||
|
||||
let is_manual = at_entry == ld_entry; // Whether the dynamic linker was invoked as a command.
|
||||
|
||||
let mut i = dynamic;
|
||||
let mut rela_ptr = None;
|
||||
let mut rela_len = None;
|
||||
let mut relr_ptr = None;
|
||||
let mut relr_len = None;
|
||||
let mut rel_ptr = None;
|
||||
let mut rel_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>(),);
|
||||
}
|
||||
elf::DT_REL => rel_ptr = Some(ptr.cast::<Rel>()),
|
||||
elf::DT_RELSZ => rel_len = Some(val as usize / size_of::<Rel>()),
|
||||
elf::DT_RELENT => {
|
||||
assert_eq!(val as usize, size_of::<Rel>());
|
||||
}
|
||||
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_notls("dynamic entry was present without it's corresponding size");
|
||||
unsafe { core::slice::from_raw_parts(ptr.byte_add(base_addr), len) }
|
||||
} else {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
|
||||
fn do_relocs<'a, T>(relocs: &'a [T], self_base: usize)
|
||||
where
|
||||
Relocation: From<&'a T>,
|
||||
{
|
||||
for reloc in relocs {
|
||||
let reloc: Relocation = reloc.into();
|
||||
let ptr = (reloc.offset + self_base) as *mut usize;
|
||||
match reloc.kind {
|
||||
RelocationKind::RELATIVE => {
|
||||
unsafe { *ptr = self_base + reloc.addend.unwrap_or_default() };
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let rela = unsafe { get_array::<Rela>(rela_ptr, rela_len, self_base) };
|
||||
let rel = unsafe { get_array::<Rel>(rel_ptr, rel_len, self_base) };
|
||||
do_relocs(rela, self_base);
|
||||
do_relocs(rel, self_base);
|
||||
|
||||
unsafe {
|
||||
let relr = get_array(relr_ptr, relr_len, self_base);
|
||||
apply_relr(self_base as *const u8, relr);
|
||||
}
|
||||
|
||||
let mut base_addr = None;
|
||||
if !is_manual {
|
||||
// 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.
|
||||
for ph in phdrs.iter() {
|
||||
if ph.p_type(NativeEndian) == PT_PHDR {
|
||||
assert!(base_addr.is_none(), "`PT_PHDR` cannot occur more than once");
|
||||
base_addr = Some(unsafe {
|
||||
phdrs
|
||||
.as_ptr()
|
||||
.cast::<u8>()
|
||||
.sub(ph.p_vaddr(NativeEndian) as usize)
|
||||
} as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage2(sp, self_base, is_manual, base_addr)
|
||||
}
|
||||
|
||||
fn stage2(
|
||||
sp: &'static mut Stack,
|
||||
self_base: usize,
|
||||
is_manual: bool,
|
||||
base_addr: Option<usize>,
|
||||
) -> usize {
|
||||
// Setup TCB for ourselves.
|
||||
unsafe {
|
||||
#[cfg(target_os = "redox")]
|
||||
@@ -225,14 +387,8 @@ pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: us
|
||||
crate::platform::environ = crate::platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr();
|
||||
}
|
||||
|
||||
let is_manual = if let Some(img_entry) = get_auxv(&auxv, AT_ENTRY) {
|
||||
img_entry == ld_entry
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
// 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")]
|
||||
@@ -264,20 +420,15 @@ pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: us
|
||||
}
|
||||
};
|
||||
|
||||
// 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 base_addr = {
|
||||
let mut base = None;
|
||||
if !is_manual && cfg!(not(target_os = "redox")) {
|
||||
let phdr = get_auxv(&auxv, AT_PHDR).unwrap();
|
||||
if phdr != 0 {
|
||||
base = Some(phdr - SIZEOF_EHDR);
|
||||
}
|
||||
let config = Config::from_env(&envs);
|
||||
if config.debug_flags.contains(DebugFlags::LOAD) {
|
||||
println!("[ld.so]: relocated self at {self_base:#x}!");
|
||||
if let Some(base_addr) = base_addr {
|
||||
println!("[ld.so]: executable has been already loaded at {base_addr:#x?}");
|
||||
}
|
||||
base
|
||||
};
|
||||
let mut linker = Linker::new(Config::from_env(&envs));
|
||||
}
|
||||
|
||||
let mut linker = Linker::new(config);
|
||||
let entry = match linker.load_program(&path, base_addr) {
|
||||
Ok(entry) => entry,
|
||||
Err(err) => {
|
||||
|
||||
+4
-4
@@ -280,18 +280,18 @@ impl<T: Write> Write for CountingWriter<T> {
|
||||
// get_auxv.
|
||||
|
||||
#[cold]
|
||||
unsafe fn auxv_iter<'a>(ptr: *const usize) -> impl Iterator<Item = [usize; 2]> + 'a {
|
||||
pub unsafe fn auxv_iter<'a>(ptr: *const usize) -> impl Iterator<Item = [usize; 2]> + 'a {
|
||||
struct St(*const usize);
|
||||
impl Iterator for St {
|
||||
type Item = [usize; 2];
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
if self.0.read() == self::auxv_defs::AT_NULL {
|
||||
if *self.0 == self::auxv_defs::AT_NULL {
|
||||
return None;
|
||||
}
|
||||
let kind = self.0.read();
|
||||
let value = self.0.add(1).read();
|
||||
let kind = *self.0;
|
||||
let value = *self.0.add(1);
|
||||
self.0 = self.0.add(2);
|
||||
|
||||
Some([kind, value])
|
||||
|
||||
Reference in New Issue
Block a user