Commit 677f0c98 authored by Jeremy Soller's avatar Jeremy Soller
Browse files

Merge branch 'dlopen_dlclose_dlsym' into 'master'

Dlopen dlclose dlsym

See merge request redox-os/relibc!290
parents 36ac4166 37a462de
use crate::{
io::{self, Write},
platform::{self, WriteByte, types::*},
platform::{self, types::*, WriteByte},
};
use core::{
cmp,
fmt,
cmp, fmt,
iter::IntoIterator,
mem,
ops::{Deref, DerefMut},
......@@ -60,7 +59,8 @@ impl<T> CVec<T> {
let ptr = if cap == 0 {
NonNull::dangling()
} else if self.cap > 0 {
NonNull::new(platform::realloc(self.ptr.as_ptr() as *mut c_void, size) as *mut T).ok_or(AllocError)?
NonNull::new(platform::realloc(self.ptr.as_ptr() as *mut c_void, size) as *mut T)
.ok_or(AllocError)?
} else {
NonNull::new((platform::alloc(size)) as *mut T).ok_or(AllocError)?
};
......@@ -208,10 +208,12 @@ impl<'a, T> IntoIterator for &'a mut CVec<T> {
impl Write for CVec<u8> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.extend_from_slice(buf).map_err(|err| io::Error::new(
io::ErrorKind::Other,
"AllocStringWriter::write failed to allocate",
))?;
self.extend_from_slice(buf).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
"AllocStringWriter::write failed to allocate",
)
})?;
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
......
......@@ -27,6 +27,7 @@ pub struct Dl_info {
#[no_mangle]
pub unsafe extern "C" fn dladdr(addr: *mut c_void, info: *mut Dl_info) -> c_int {
//TODO
(*info).dli_fname = ptr::null();
(*info).dli_fbase = ptr::null_mut();
(*info).dli_sname = ptr::null();
......@@ -35,50 +36,56 @@ pub unsafe extern "C" fn dladdr(addr: *mut c_void, info: *mut Dl_info) -> c_int
}
#[no_mangle]
pub unsafe extern "C" fn dlopen(filename: *const c_char, flags: c_int) -> *mut c_void {
let filename_opt = if filename.is_null() {
None
pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut c_void {
//TODO support all sort of flags
let filename = if cfilename.is_null() {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
} else {
Some(str::from_utf8_unchecked(
CStr::from_ptr(filename).to_bytes(),
))
str::from_utf8_unchecked(CStr::from_ptr(cfilename).to_bytes())
};
eprintln!("dlopen({:?}, {:#>04x})", filename_opt, flags);
if let Some(filename) = filename_opt {
if let Some(tcb) = Tcb::current() {
if tcb.linker_ptr.is_null() {
eprintln!("dlopen: linker not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
eprintln!("dlopen: linker_ptr: {:p}", tcb.linker_ptr);
let mut linker = (&*tcb.linker_ptr).lock();
if let Err(err) = linker.load_library(filename) {
eprintln!("dlopen: failed to load {}", filename);
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
if let Err(err) = linker.link(None, None) {
eprintln!("dlopen: failed to link '{}': {}", filename, err);
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
};
// TODO
1 as *mut c_void
} else {
let tcb = match Tcb::current() {
Some(tcb) => tcb,
None => {
eprintln!("dlopen: tcb not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
ptr::null_mut()
return ptr::null_mut();
}
} else {
1 as *mut c_void
};
if tcb.linker_ptr.is_null() {
eprintln!("dlopen: linker not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
let mut linker = (&*tcb.linker_ptr).lock();
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
let id = match (cbs.load_library)(&mut linker, filename) {
Err(err) => {
eprintln!("dlopen: failed to load {}", filename);
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
Ok(id) => id,
};
if let Err(err) = (cbs.link)(&mut linker, None, None, Some(id)) {
(cbs.unload)(&mut linker, id);
eprintln!("dlopen: failed to link '{}': {}", filename, err);
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
};
if let Err(err) = (cbs.run_init)(&mut linker, Some(id)) {
(cbs.unload)(&mut linker, id);
eprintln!("dlopen: failed to link '{}': {}", filename, err);
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
};
id as *mut c_void
}
#[no_mangle]
......@@ -90,37 +97,57 @@ pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *m
let symbol_str = str::from_utf8_unchecked(CStr::from_ptr(symbol).to_bytes());
eprintln!("dlsym({:p}, {})", handle, symbol_str);
if let Some(tcb) = Tcb::current() {
if tcb.linker_ptr.is_null() {
eprintln!("dlopen: linker not found");
let tcb = match Tcb::current() {
Some(tcb) => tcb,
None => {
eprintln!("dlsym: tcb not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
};
eprintln!("dlsym: linker_ptr: {:p}", tcb.linker_ptr);
let linker = (&*tcb.linker_ptr).lock();
if tcb.linker_ptr.is_null() {
eprintln!("dlsym: linker not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
if let Some(global) = linker.get_sym(symbol_str) {
eprintln!("dlsym({:p}, {}) = 0x{:x}", handle, symbol_str, global);
global as *mut c_void
} else {
eprintln!("dlsym: symbol not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
ptr::null_mut()
}
let linker = (&*tcb.linker_ptr).lock();
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
if let Some(global) = (cbs.get_sym)(&linker, symbol_str, Some(handle as usize)) {
global as *mut c_void
} else {
eprintln!("dlsym: tcb not found");
eprintln!("dlsym: symbol not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
ptr::null_mut()
}
}
#[no_mangle]
pub extern "C" fn dlclose(handle: *mut c_void) -> c_int {
// TODO: Loader::fini() should be called about here
pub unsafe extern "C" fn dlclose(handle: *mut c_void) -> c_int {
let tcb = match Tcb::current() {
Some(tcb) => tcb,
None => {
eprintln!("dlclose: tcb not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
}
};
if tcb.linker_ptr.is_null() {
eprintln!("dlclose: linker not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
};
let mut linker = (&*tcb.linker_ptr).lock();
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
if let Err(err) = (cbs.run_fini)(&mut linker, Some(handle as usize)) {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
};
(cbs.unload)(&mut linker, handle as usize);
0
}
......
......@@ -12,7 +12,7 @@ use alloc::{borrow::ToOwned, boxed::Box, str::SplitWhitespace, vec::Vec};
use crate::{
c_str::{CStr, CString},
header::{
arpa_inet::htons, arpa_inet::ntohl, arpa_inet::inet_aton,
arpa_inet::{htons, inet_aton, ntohl},
errno::*,
fcntl::O_RDONLY,
netinet_in::{in_addr, sockaddr_in, sockaddr_in6},
......
use super::linker::{Linker, DSO};
use alloc::boxed::Box;
use goblin::error::Result;
pub struct LinkerCallbacks {
pub unload: Box<dyn Fn(&mut Linker, usize)>,
pub load_library: Box<dyn Fn(&mut Linker, &str) -> Result<usize>>,
pub link:
Box<dyn Fn(&mut Linker, Option<&str>, Option<DSO>, Option<usize>) -> Result<Option<usize>>>,
pub get_sym: Box<dyn Fn(&Linker, &str, Option<usize>) -> Option<usize>>,
pub run_init: Box<dyn Fn(&Linker, Option<usize>) -> Result<()>>,
pub run_fini: Box<dyn Fn(&Linker, Option<usize>) -> Result<()>>,
}
impl LinkerCallbacks {
pub fn new() -> LinkerCallbacks {
LinkerCallbacks {
unload: Box::new(unload),
load_library: Box::new(load_library),
link: Box::new(link),
get_sym: Box::new(get_sym),
run_init: Box::new(run_init),
run_fini: Box::new(run_fini),
}
}
}
fn unload(linker: &mut Linker, libspace: usize) {
linker.unload(libspace)
}
fn load_library(linker: &mut Linker, name: &str) -> Result<usize> {
linker.load_library(name)
}
fn link(
linker: &mut Linker,
primary_opt: Option<&str>,
dso: Option<DSO>,
libspace: Option<usize>,
) -> Result<Option<usize>> {
linker.link(primary_opt, dso, libspace)
}
fn get_sym(linker: &Linker, name: &str, libspace: Option<usize>) -> Option<usize> {
linker.get_sym(name, libspace)
}
fn run_init(linker: &Linker, libspace: Option<usize>) -> Result<()> {
linker.run_init(libspace)
}
fn run_fini(linker: &Linker, libspace: Option<usize>) -> Result<()> {
linker.run_fini(libspace)
}
use alloc::{
boxed::Box,
collections::{BTreeMap, BTreeSet},
string::String,
vec::Vec,
};
#[derive(Default, Debug)]
pub struct DepTree {
pub name: String,
pub deps: Vec<DepTree>,
}
impl DepTree {
pub fn new(name: String) -> DepTree {
DepTree {
name,
deps: Vec::new(),
}
}
}
/// Use to represnt a library as well as all th symbols that is loaded withen it.
#[derive(Default)]
pub struct Library {
/// Global symbols
pub globals: BTreeMap<String, usize>,
/// Weak symbols
pub weak_syms: BTreeMap<String, usize>,
/// Loaded library raw data
pub objects: BTreeMap<String, Box<[u8]>>,
/// Loaded library in-memory data
pub mmaps: BTreeMap<String, &'static mut [u8]>,
/// Each object will have its children called once with no repetition.
pub dep_tree: DepTree,
/// A set used to detect circular dependencies in the Linker::load function
pub cir_dep: BTreeSet<String>,
}
impl Library {
pub fn new() -> Library {
Default::default()
}
pub fn get_sym(&self, name: &str) -> Option<usize> {
if let Some(value) = self.globals.get(name) {
Some(*value)
} else if let Some(value) = self.weak_syms.get(name) {
Some(*value)
} else {
None
}
}
}
use alloc::{
boxed::Box,
collections::{BTreeMap, BTreeSet},
collections::BTreeMap,
rc::Rc,
string::{String, ToString},
vec::Vec,
};
use core::{
mem::{size_of, transmute},
cell::RefCell,
mem::{size_of, swap, transmute},
ptr, slice,
};
use goblin::{
......@@ -28,7 +30,9 @@ use crate::{
use super::{
access::access,
callbacks::LinkerCallbacks,
debug::{RTLDDebug, RTLDState, _dl_debug_state, _r_debug},
library::{DepTree, Library},
tcb::{Master, Tcb},
PAGE_SIZE,
};
......@@ -43,70 +47,53 @@ pub struct DSO {
pub base_addr: usize,
pub entry_point: usize,
}
#[derive(Default, Debug)]
pub struct DepTree {
pub name: String,
pub deps: Vec<DepTree>,
}
impl DepTree {
fn new(name: String) -> DepTree {
DepTree {
name,
deps: Vec::new(),
}
}
}
pub struct Linker {
// Used by load
/// Library path to search when loading library by name
library_path: String,
/// Loaded library raw data
objects: BTreeMap<String, Box<[u8]>>,
// Used by link
/// Global symbols
globals: BTreeMap<String, usize>,
/// Weak symbols
weak_syms: BTreeMap<String, usize>,
/// Loaded library in-memory data
mmaps: BTreeMap<String, &'static mut [u8]>,
root: Library,
verbose: bool,
tls_index_offset: usize,
/// A set used to detect circular dependencies in the Linker::load function
cir_dep: BTreeSet<String>,
/// Each object will have its children callec once with no repetition.
dep_tree: DepTree,
lib_spaces: BTreeMap<usize, Library>,
counter: usize,
pub cbs: Rc<RefCell<LinkerCallbacks>>,
}
impl Linker {
pub fn new(library_path: &str, verbose: bool) -> Self {
Self {
library_path: library_path.to_string(),
objects: BTreeMap::new(),
globals: BTreeMap::new(),
weak_syms: BTreeMap::new(),
mmaps: BTreeMap::new(),
root: Library::new(),
verbose,
tls_index_offset: 0,
cir_dep: BTreeSet::new(),
dep_tree: Default::default(),
lib_spaces: BTreeMap::new(),
counter: 1,
cbs: Rc::new(RefCell::new(LinkerCallbacks::new())),
}
}
pub fn load(&mut self, name: &str, path: &str) -> Result<()> {
self.dep_tree = self.load_recursive(name, path)?;
let mut lib: Library = Library::new();
swap(&mut lib, &mut self.root);
lib.dep_tree = self.load_recursive(name, path, &mut lib)?;
swap(&mut lib, &mut self.root);
if self.verbose {
println!("Dep tree: {:#?}", self.dep_tree);
println!("Dep tree: {:#?}", self.root.dep_tree);
}
return Ok(());
}
fn load_recursive(&mut self, name: &str, path: &str) -> Result<DepTree> {
pub fn unload(&mut self, libspace: usize) {
if let Some(lib) = self.lib_spaces.remove(&libspace) {
for (_, mmap) in lib.mmaps {
unsafe { sys_mman::munmap(mmap.as_mut_ptr() as *mut c_void, mmap.len()) };
}
}
}
fn load_recursive(&mut self, name: &str, path: &str, lib: &mut Library) -> Result<DepTree> {
if self.verbose {
println!("load {}: {}", name, path);
}
if self.cir_dep.contains(name) {
if lib.cir_dep.contains(name) {
return Err(Error::Malformed(format!(
"Circular dependency: {} is a dependency of itself",
name
......@@ -115,7 +102,7 @@ impl Linker {
let mut deps = DepTree::new(name.to_string());
let mut data = Vec::new();
self.cir_dep.insert(name.to_string());
lib.cir_dep.insert(name.to_string());
let path_c = CString::new(path)
.map_err(|err| Error::Malformed(format!("invalid path '{}': {}", path, err)))?;
......@@ -127,32 +114,45 @@ impl Linker {
file.read_to_end(&mut data)
.map_err(|err| Error::Malformed(format!("failed to read '{}': {}", path, err)))?;
}
deps.deps = self.load_data(name, data.into_boxed_slice())?;
self.cir_dep.remove(name);
deps.deps = self.load_data(name, data.into_boxed_slice(), lib)?;
lib.cir_dep.remove(name);
Ok(deps)
}
pub fn load_data(&mut self, name: &str, data: Box<[u8]>) -> Result<Vec<DepTree>> {
fn load_data(
&mut self,
name: &str,
data: Box<[u8]>,
lib: &mut Library,
) -> Result<Vec<DepTree>> {
let elf = Elf::parse(&data)?;
//println!("{:#?}", elf);
let mut deps = Vec::new();
for library in elf.libraries.iter() {
if let Some(dep) = self.load_library(library)? {
if let Some(dep) = self._load_library(library, lib)? {
deps.push(dep);
}
}
self.objects.insert(name.to_string(), data);
lib.objects.insert(name.to_string(), data);
return Ok(deps);
}
pub fn load_library(&mut self, name: &str) -> Result<Option<DepTree>> {
if self.objects.contains_key(name) {
pub fn load_library(&mut self, name: &str) -> Result<usize> {
let mut lib = Library::new();
self._load_library(name, &mut lib)?;
let ret = self.counter;
self.lib_spaces.insert(ret, lib);
self.counter += 1;
return Ok(ret);
}
fn _load_library(&mut self, name: &str, lib: &mut Library) -> Result<Option<DepTree>> {
if lib.objects.contains_key(name) || self.root.objects.contains_key(name) {
// It should be previously resolved so we don't need to worry about it
Ok(None)
} else if name.contains('/') {
Ok(Some(self.load_recursive(name, name)?))
Ok(Some(self.load_recursive(name, name, lib)?))
} else {
let library_path = self.library_path.clone();
for part in library_path.split(PATH_SEP) {
......@@ -176,7 +176,7 @@ impl Linker {
};
if access {
return Ok(Some(self.load_recursive(name, &path)?));
return Ok(Some(self.load_recursive(name, &path, lib)?));
}
}
......@@ -227,40 +227,51 @@ impl Linker {
return Ok((globals, weak_syms));
}
pub fn get_sym(&self, name: &str) -> Option<usize> {
if let Some(value) = self.globals.get(name) {
if self.verbose {
println!(" sym {} = {:#x}", name, value);
}
Some(*value)
} else if let Some(value) = self.weak_syms.get(name) {
if self.verbose {
println!(" sym {} = {:#x}", name, value);
pub fn get_sym(&self, name: &str, libspace: Option<usize>) -> Option<usize> {
match libspace {
Some(id) => {
let lib = self.lib_spaces.get(&id)?;
lib.get_sym(name)
}
Some(*value)
} else {
if self.verbose {
println!(" sym {} = undefined", name);
None => self.root.get_sym(name),
}
}
pub fn run_init(&self, libspace: Option<usize>) -> Result<()> {
match libspace {
Some(id) => {
let lib = self.lib_spaces.get(&id).unwrap();
self.run_tree(&lib, &lib.dep_tree, ".init_array")
}
None
None => self.run_tree(&self.root, &self.root.dep_tree, ".init_array"),
}
}
pub fn run_init(&self) -> Result<()> {
self.run_init_tree(&self.dep_tree)
pub fn run_fini(&self, libspace: Option<usize>) -> Result<()> {
match libspace {
Some(id) => {
let lib = self.lib_spaces.get(&id).unwrap();
self.run_tree(&lib, &lib.dep_tree, ".fini_array")
}
None => {
//TODO we first need to deinitialize all the loaded libraries first!
self.run_tree(&self.root, &self.root.dep_tree, ".fini_array")
}
}
}
fn run_init_tree(&self, root: &DepTree) -> Result<()> {
fn run_tree(&self, lib: &Library, root: &DepTree, tree_name: &str) -> Result<()> {
for node in root.deps.iter() {
self.run_init_tree(node)?;
self.run_tree(lib, node, tree_name)?;
}
if self.verbose {
println!("init {}", &root.name);
println!("running {} {}", tree_name, &root.name);
}
let mmap = match self.mmaps.get(&root.name) {
let mmap = match lib.mmaps.get(&root.name) {
Some(some) => some,
None => return Ok(()),
};
let elf = Elf::parse(self.objects.get(&root.name).unwrap())?;
let elf = Elf::parse(lib.objects.get(&root.name).unwrap())?;
for section in &elf.section_headers {
let name = match elf.shdr_strtab.get(section.sh_name) {
Some(x) => match x {
......@@ -269,7 +280,7 @@ impl Linker {
},
_ => continue,
};
if name == ".init_array" {
if name == tree_name {
let addr = if is_pie_enabled(&elf) {
<