Merge branch 'patch5' into 'master'

fix(ld.so): properly do copy relocations

See merge request redox-os/relibc!604
This commit is contained in:
Jeremy Soller
2025-01-18 13:17:38 +00:00
6 changed files with 186 additions and 69 deletions
Generated
+27 -1
View File
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "aho-corasick"
@@ -243,6 +243,16 @@ dependencies = [
"libc",
]
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.22"
@@ -511,6 +521,7 @@ dependencies = [
"scrypt",
"sha-crypt",
"sha2",
"spin",
"unicode-width",
]
@@ -529,6 +540,12 @@ version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "010e18bd3bfd1d45a7e666b236c78720df0d9a7698ebaa9c1c559961eb60a38b"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "scroll"
version = "0.11.0"
@@ -594,6 +611,15 @@ version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]]
name = "subtle"
version = "2.6.1"
+1
View File
@@ -50,6 +50,7 @@ chrono-tz = {version = "0.10", default-features = false}
chrono = {version = "0.4", default-features = false, features = ["alloc"]}
libm = "0.2"
object = { version = "0.36.7", git = "https://gitlab.redox-os.org/andypython/object", default-features = false, features = ["elf", "read_core"] }
spin = "0.9.8"
[dependencies.dlmalloc]
path = "dlmalloc-rs"
+3 -3
View File
@@ -13,7 +13,7 @@ use core::{
use crate::{
c_str::CStr,
ld_so::{
linker::{DlError, ObjectHandle, ObjectScope, Resolve},
linker::{DlError, ObjectHandle, Resolve, ScopeKind},
tcb::Tcb,
},
platform::types::*,
@@ -67,9 +67,9 @@ pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut
};
let scope = if flags & RTLD_GLOBAL == RTLD_GLOBAL {
ObjectScope::Global
ScopeKind::Global
} else {
ObjectScope::Local
ScopeKind::Local
};
let noload = flags & RTLD_NOLOAD == RTLD_NOLOAD;
+3 -3
View File
@@ -1,11 +1,11 @@
use super::linker::{Linker, ObjectHandle, ObjectScope, Resolve, Result};
use super::linker::{Linker, ObjectHandle, Resolve, Result, ScopeKind};
use crate::platform::types::c_void;
use alloc::boxed::Box;
pub struct LinkerCallbacks {
pub unload: Box<dyn Fn(&mut Linker, ObjectHandle)>,
pub load_library:
Box<dyn Fn(&mut Linker, Option<&str>, Resolve, ObjectScope, bool) -> Result<ObjectHandle>>,
Box<dyn Fn(&mut Linker, Option<&str>, Resolve, ScopeKind, bool) -> Result<ObjectHandle>>,
pub get_sym: Box<dyn Fn(&Linker, Option<ObjectHandle>, &str) -> Option<*mut c_void>>,
}
@@ -27,7 +27,7 @@ fn load_library(
linker: &mut Linker,
name: Option<&str>,
resolve: Resolve,
scope: ObjectScope,
scope: ScopeKind,
noload: bool,
) -> Result<ObjectHandle> {
linker.load_library(name, resolve, scope, noload)
+41 -19
View File
@@ -21,7 +21,6 @@ use crate::{
platform::{types::c_void, Pal, Sys},
};
use alloc::{
collections::BTreeMap,
string::{String, ToString},
sync::Arc,
vec::Vec,
@@ -235,14 +234,12 @@ pub struct DSO {
pub entry_point: usize,
/// Loaded library in-memory data
pub mmap: &'static [u8],
pub global_syms: BTreeMap<String, Symbol>,
pub weak_syms: BTreeMap<String, Symbol>,
pub tls_module_id: usize,
pub tls_offset: usize,
pub(super) dynamic: Dynamic<'static>,
pub scope: Scope,
pub scope: spin::Once<Scope>,
/// Position Independent Executable.
pub pie: bool,
}
@@ -281,8 +278,6 @@ impl DSO {
dlopened,
entry_point,
mmap,
global_syms: BTreeMap::new(),
weak_syms: BTreeMap::new(),
tls_module_id: if tcb_master.is_some() {
tls_module_id
} else {
@@ -292,12 +287,17 @@ impl DSO {
pie: is_pie_enabled(&elf),
dynamic,
scope: Scope::local(),
scope: spin::Once::new(),
};
Ok((dso, tcb_master, elf.elf_program_headers().to_vec()))
}
#[inline]
pub fn scope(&self) -> &Scope {
self.scope.get().expect("scope not initialized")
}
/// Global Offset Table
#[inline]
pub fn got(&self) -> Option<NonNull<usize>> {
@@ -314,7 +314,7 @@ impl DSO {
&self.dynamic.needed
}
pub fn get_sym(&self, name: &str) -> Option<(Symbol, SymbolBinding)> {
pub fn get_sym<'a>(&self, name: &'a str) -> Option<(Symbol<'a>, SymbolBinding)> {
let (_, sym) = self.dynamic.hash_table.find(
name,
None,
@@ -329,6 +329,7 @@ impl DSO {
Some((
Symbol {
name,
base: if self.pie {
self.mmap.as_ptr() as usize
} else {
@@ -658,7 +659,7 @@ impl DSO {
let dynstrtab = StringTable::new(
&*mmap,
strtab_offset as u64,
strtab_offset as u64 + strtab_size as u64,
strtab_offset as u64 + strtab_size,
);
let get_str = |entry: &Dyn| {
@@ -725,18 +726,27 @@ impl DSO {
fn static_relocate(&self, global_scope: &Scope, reloc: Relocation) -> object::Result<()> {
let b = self.mmap.as_ptr() as usize;
let sym = if reloc.sym.0 > 0 {
let (sym, my_sym) = if reloc.sym.0 > 0 {
let name = self.dynamic.symbol_name(reloc.sym).unwrap();
resolve_sym(name, &[global_scope, &self.scope])
.map(|(sym, _, obj)| (sym, obj.tls_offset))
let lookup_scopes = [global_scope, self.scope()];
let sym = if reloc.kind == elf::R_X86_64_COPY {
lookup_scopes
.iter()
.find_map(|scope| scope._get_sym(name, 1))
} else {
resolve_sym(name, &lookup_scopes)
}
.map(|(sym, _, obj)| (sym, obj));
(sym, self.dynamic.symbol(reloc.sym))
} else {
None
(None, None)
};
let (s, t) = sym
.as_ref()
.map(|(sym, t)| (sym.as_ptr() as usize, *t))
.map(|(sym, obj)| (sym.as_ptr() as usize, obj.tls_offset))
.unwrap_or((0, 0));
let a = reloc.addend;
@@ -777,9 +787,18 @@ impl DSO {
set_u64(f());
},
elf::R_X86_64_COPY => unsafe {
let (sym, _) = sym
let (sym, obj) = sym
.as_ref()
.expect("R_X86_64_COPY called without valid symbol");
let my_sym = my_sym.expect("R_X86_64_COPY called without valid symbol");
assert!(
sym.size == my_sym.st_size(NativeEndian) as usize,
"R_X86_64_COPY failed: I was trying to use the symbol {} from {} for {} but they had different sizes. Please consider relinking.",
sym.name,
obj.name,
self.name
);
// SAFETY: Both the source and destination have the same size.
ptr::copy_nonoverlapping(sym.as_ptr() as *const u8, ptr, sym.size);
},
_ => unimplemented!("relocation type {:#x}", reloc.kind),
@@ -834,9 +853,9 @@ impl DSO {
(elf::R_X86_64_JUMP_SLOT, Resolve::Now) => {
let name = self.dynamic.symbol_name(reloc.sym).unwrap();
let resolved = resolve_sym(name, &[global_scope, &self.scope])
let resolved = resolve_sym(name, &[global_scope, self.scope()])
.map(|(sym, _, _)| sym.as_ptr() as usize)
.expect("unresolved symbol");
.unwrap_or_else(|| panic!("unresolved symbol: {name}"));
unsafe {
*ptr = resolved + reloc.addend;
@@ -862,7 +881,7 @@ impl DSO {
if entry & 1 == 0 {
// An even entry sets up `addr` for subsequent odd entries.
unsafe {
addr = base.add(entry as usize) as *mut usize;
addr = base.add(entry) as *mut usize;
*addr += base as usize;
addr = addr.add(1);
}
@@ -950,6 +969,9 @@ fn dirname(path: &str) -> String {
parts.join("/")
}
pub fn resolve_sym(name: &str, scopes: &[&Scope]) -> Option<(Symbol, SymbolBinding, Arc<DSO>)> {
pub fn resolve_sym<'a>(
name: &'a str,
scopes: &[&'a Scope],
) -> Option<(Symbol<'a>, SymbolBinding, Arc<DSO>)> {
scopes.iter().find_map(|scope| scope.get_sym(name))
}
+111 -43
View File
@@ -121,16 +121,17 @@ impl Drop for MmapFile {
}
}
#[derive(Clone, Copy, Debug)]
pub struct Symbol {
#[derive(Clone, Debug)]
pub struct Symbol<'a> {
pub name: &'a str,
pub value: usize,
pub base: usize,
pub size: usize,
pub sym_type: u8,
}
impl Symbol {
pub fn as_ptr(self) -> *mut c_void {
impl Symbol<'_> {
pub fn as_ptr(&self) -> *mut c_void {
(self.base + self.value) as *mut c_void
}
}
@@ -147,15 +148,16 @@ pub enum Resolve {
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ObjectScope {
pub enum ScopeKind {
Global,
Local,
}
pub enum Scope {
Global {
objs: Vec<Weak<DSO>>,
},
/// The global scope initially contains the main program and all of its
/// dependencies. Additional objects will be added to this scope via
/// `dlopen(2)` if the `RTLD_GLOBAL` flag is set.
Global { objs: Vec<Weak<DSO>> },
Local {
owner: Option<Weak<DSO>>,
objs: Vec<Arc<DSO>>,
@@ -169,7 +171,7 @@ impl Scope {
}
#[inline]
pub(super) const fn local() -> Self {
const fn local() -> Self {
Self::Local {
owner: None,
objs: Vec::new(),
@@ -211,7 +213,18 @@ impl Scope {
}
}
pub(super) fn get_sym(&self, name: &str) -> Option<(Symbol, SymbolBinding, Arc<DSO>)> {
pub(super) fn get_sym<'a>(
&self,
name: &'a str,
) -> Option<(Symbol<'a>, SymbolBinding, Arc<DSO>)> {
self._get_sym(name, 0)
}
pub(super) fn _get_sym<'a>(
&self,
name: &'a str,
skip: usize,
) -> Option<(Symbol<'a>, SymbolBinding, Arc<DSO>)> {
let mut res = None;
let get_sym = |obj: Arc<DSO>| {
@@ -227,7 +240,11 @@ impl Scope {
};
match self {
Self::Global { objs } => objs.iter().map(|o| o.upgrade().unwrap()).find_map(get_sym),
Self::Global { objs } => objs
.iter()
.skip(skip)
.map(|o| o.upgrade().unwrap())
.find_map(get_sym),
Self::Local { owner, objs } => {
let owner = owner
.as_ref()
@@ -237,16 +254,17 @@ impl Scope {
core::iter::once(owner)
.chain(objs.iter().cloned())
.skip(skip)
.find_map(get_sym)
}
}
.or(res)
}
fn move_into(&self, other: &mut Self) {
// FIXME: move not copy? as afiak u cannot downgrade from a global to a local scope.
fn copy_into(&self, other: &mut Self) {
match (self, other) {
(Self::Local { owner, objs }, Self::Global { objs: other_objs }) => {
// FIXME: may have duplicates
let owner = owner.as_ref().expect("local scope without owner");
other_objs.push(owner.clone());
other_objs.extend(objs.iter().map(Arc::downgrade));
@@ -255,6 +273,28 @@ impl Scope {
_ => unreachable!(),
}
}
fn debug(&self) {
match self {
Self::Global { objs } => {
println!(
"[@global] {:?}",
objs.iter()
.map(|x| x.upgrade().unwrap().name.clone())
.collect::<Vec<_>>()
);
}
Self::Local { owner, objs } => {
let owner = owner.as_ref().unwrap().upgrade().unwrap();
println!(
"[{}] {:?}",
owner.name,
objs.iter().map(|x| x.name.clone()).collect::<Vec<_>>()
)
}
}
}
}
// Used by dlfcn.h
@@ -383,7 +423,7 @@ impl Linker {
} else {
Resolve::default()
},
ObjectScope::Global,
ScopeKind::Global,
)?;
Ok(dso.entry_point)
}
@@ -392,7 +432,7 @@ impl Linker {
&mut self,
name: Option<&str>,
resolve: Resolve,
scope: ObjectScope,
scope: ScopeKind,
noload: bool,
) -> Result<ObjectHandle> {
trace!(
@@ -413,17 +453,18 @@ impl Linker {
match name {
Some(name) => {
if let Some(id) = self.name_to_object_id_map.get(name) {
let obj = self.objects.get_mut(id).unwrap();
let obj = self.objects.get(id).unwrap();
// We may be upgrading the object from a local scope to the
// global scope.
if scope == ObjectScope::Global {
if scope == ScopeKind::Global {
if self.config.debug_flags.contains(DebugFlags::SCOPES) {
eprintln!("[ld.so]: moving {} into the global scope", obj.name);
}
let mut global_scope = GLOBAL_SCOPE.write();
obj.scope.move_into(&mut global_scope);
obj.scope().copy_into(&mut global_scope);
self.scope_debug();
}
Ok(ObjectHandle::new(obj.clone()))
@@ -466,7 +507,7 @@ impl Linker {
let guard;
if let Some(handle) = handle.as_ref() {
&handle.as_ref().scope
handle.as_ref().scope()
} else {
guard = GLOBAL_SCOPE.read();
&guard
@@ -541,7 +582,7 @@ impl Linker {
base_addr: Option<usize>,
dlopened: bool,
resolve: Resolve,
scope: ObjectScope,
scope: ScopeKind,
) -> Result<Arc<DSO>> {
let resolve = if cfg!(target_arch = "x86_64") {
resolve
@@ -666,9 +707,11 @@ impl Linker {
}
}
// new_objects is already reversed.
for (i, obj) in new_objects.into_iter().enumerate() {
for (i, obj) in new_objects.iter().enumerate() {
obj.relocate(&objects_data[i], resolve).unwrap();
}
for obj in new_objects.into_iter() {
self.run_init(&obj);
self.register_object(obj);
}
@@ -684,6 +727,19 @@ impl Linker {
self.objects.insert(obj.id, obj);
}
/// Loads the specified object and all of its dependencies.
///
/// `new_objects` contains any new objects that were loaded. Order is
/// reverse of how the scope is populated.
///
/// The scope is populated such that the loaded objects are in breadth-first
/// order. This means that first the requested object is added to the scope,
/// and then its dependencies are added in the order of their respective
/// `DT_NEEDED` entries in the requested object. This is done recursively
/// until all dependencies have been loaded.
///
/// If a dependency has already been loaded, it is *not* added to the scope
/// nor to `new_objects`.
fn load_objects_recursive<'a>(
&mut self,
name: &str,
@@ -693,9 +749,9 @@ impl Linker {
new_objects: &mut Vec<Arc<DSO>>,
objects_data: &mut Vec<Vec<ProgramHeader>>,
tcb_masters: &mut Vec<Master>,
// The object that caused this object to be loaded.
dependent: Option<&mut DSO>,
scope: ObjectScope,
// Scope of the object that caused this object to be loaded.
dependent_scope: Option<&mut Scope>,
scope_kind: ScopeKind,
) -> Result<Arc<DSO>> {
// fixme: double lookup slow
if let Some(id) = self.name_to_object_id_map.get(name) {
@@ -711,7 +767,7 @@ impl Linker {
let path = self.search_object(name, parent_runpath)?;
let file = self.read_file(&path)?;
let data = file.data();
let (mut obj, tcb_master, elf) = DSO::new(
let (obj, tcb_master, elf) = DSO::new(
&path,
data,
base_addr,
@@ -754,6 +810,18 @@ impl Linker {
.map(|dep| dep.to_string())
.collect::<Vec<_>>();
let obj = Arc::new(obj);
let mut scope = Scope::local();
if let Some(dependent_scope) = dependent_scope {
match scope_kind {
ScopeKind::Local => dependent_scope.add(&obj),
ScopeKind::Global => GLOBAL_SCOPE.write().add(&obj),
}
} else if let ScopeKind::Global = scope_kind {
GLOBAL_SCOPE.write().add(&obj);
}
for dep_name in dependencies.iter() {
self.load_objects_recursive(
dep_name,
@@ -763,28 +831,17 @@ impl Linker {
new_objects,
objects_data,
tcb_masters,
Some(&mut obj),
scope,
Some(&mut scope),
scope_kind,
)?;
}
let obj = Arc::new_cyclic(|sref| {
obj.scope.set_owner(sref.clone());
obj
});
if let Some(dependent) = dependent {
match scope {
ObjectScope::Local => dependent.scope.add(&obj),
ObjectScope::Global => GLOBAL_SCOPE.write().add(&obj),
}
} else if let ObjectScope::Global = scope {
GLOBAL_SCOPE.write().add(&obj);
}
objects_data.push(elf);
new_objects.push(obj.clone());
scope.set_owner(Arc::downgrade(&obj));
obj.scope.call_once(|| scope);
Ok(obj)
}
@@ -867,6 +924,17 @@ impl Linker {
obj.run_init();
}
fn scope_debug(&self) {
if self.config.debug_flags.contains(DebugFlags::SCOPES) {
println!("[ld.so]: =========== SCOPES ==========");
GLOBAL_SCOPE.read().debug();
for obj in self.objects.values() {
obj.scope().debug();
}
println!("[ld.so]: ==============================");
}
}
}
// GOT[1] = object_id
@@ -901,7 +969,7 @@ extern "C" fn __plt_resolve_inner(obj: *const DSO, relocation_index: c_uint) ->
)
.expect("non utf8 symbol name");
let resolved = resolve_sym(name, &[&GLOBAL_SCOPE.read(), &obj.scope])
let resolved = resolve_sym(name, &[&GLOBAL_SCOPE.read(), obj.scope()])
.map(|(sym, _, _)| sym)
.unwrap_or_else(|| panic!("symbol '{name}' not found"))
.as_ptr();