dyn-loader: implement basic dladdr()
This commit is contained in:
parent
8e45e48362
commit
5d5379ac8a
87
test.c
87
test.c
@ -1,85 +1,16 @@
|
||||
#include <netinet/in.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static void fmt_inaddr(char *buffer, const struct sockaddr_in *inaddr) {
|
||||
uint8_t a = (uint8_t) inaddr->sin_addr.s_addr;
|
||||
uint8_t b = (uint8_t) (inaddr->sin_addr.s_addr >> 8);
|
||||
uint8_t c = (uint8_t) (inaddr->sin_addr.s_addr >> 16);
|
||||
uint8_t d = (uint8_t) (inaddr->sin_addr.s_addr >> 24);
|
||||
uint16_t port = ntohs(inaddr->sin_port);
|
||||
sprintf(buffer, "%hhu.%hhu.%hhu.%hhu:%hu", a, b, c, d, port);
|
||||
}
|
||||
int main(int argc, char **argv) {
|
||||
struct Dl_info dl0, dl1;
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
dladdr(main, &dl0);
|
||||
dladdr(printf, &dl1);
|
||||
printf("%s: %p\n", dl1.dli_sname, dl1.dli_saddr);
|
||||
printf("%s @ %p\n", dl1.dli_fname, dl1.dli_fbase);
|
||||
|
||||
if (fd < 0) {
|
||||
perror("socket()");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
struct sockaddr_in sa;
|
||||
socklen_t slen;
|
||||
char buffer[256];
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(4321);
|
||||
sa.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (bind(fd, (const struct sockaddr *) &sa, sizeof(sa)) != 0) {
|
||||
perror("bind()");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (listen(fd, 64) != 0) {
|
||||
perror("listen()");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int rfd;
|
||||
|
||||
slen = sizeof(sa);
|
||||
if ((rfd = accept(fd, (struct sockaddr *) &sa, &slen)) < 0) {
|
||||
perror("accept()");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fmt_inaddr(buffer, &sa);
|
||||
printf("Received connection from %s\n", buffer);
|
||||
|
||||
while (1) {
|
||||
ssize_t len;
|
||||
|
||||
if ((len = recv(rfd, buffer, sizeof(buffer), 0)) < 0) {
|
||||
perror("recv()");
|
||||
break;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (len >= 4 && !strncmp(buffer, "quit", 4)) {
|
||||
break;
|
||||
}
|
||||
|
||||
fwrite(buffer, 1, len, stdout);
|
||||
|
||||
if ((len = send(rfd, buffer, len, 0)) < 0) {
|
||||
perror("send()");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Connection closed\n");
|
||||
close(rfd);
|
||||
}
|
||||
printf("%s: %p\n", dl0.dli_sname, dl0.dli_saddr);
|
||||
printf("%s @ %p\n", dl0.dli_fname, dl0.dli_fbase);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,3 +1,24 @@
|
||||
use std::{
|
||||
ffi::{c_char, c_int, c_void},
|
||||
ptr,
|
||||
};
|
||||
|
||||
use crate::OBJECTS;
|
||||
|
||||
pub const RTLD_LAZY: c_int = 1 << 0;
|
||||
pub const RTLD_NOW: c_int = 0 << 0;
|
||||
pub const RTLD_LOCAL: c_int = 1 << 1;
|
||||
pub const RTLD_GLOBAL: c_int = 0 << 1;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct Dl_info {
|
||||
pub dli_fname: *const c_char,
|
||||
pub dli_fbase: *mut c_void,
|
||||
pub dli_sname: *const c_char,
|
||||
pub dli_saddr: *mut c_void,
|
||||
}
|
||||
|
||||
// Has to be naked: need to prevent the function from clobbering any registers besides x0
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
@ -6,8 +27,53 @@ pub(crate) unsafe extern "C" fn tlsdesc_resolve_static(argument: *const usize) -
|
||||
// x0 -- pointer to tlsdesc struct:
|
||||
// [0]: this function pointer
|
||||
// [1]: static offset to return
|
||||
core::arch::naked_asm!(r#"
|
||||
core::arch::naked_asm!(
|
||||
r#"
|
||||
ldr x0, [x0, #8]
|
||||
ret
|
||||
"#);
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn dlopen(_path: *const c_char, _flags: c_int) -> *mut c_void {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn dlclose(_object: *mut c_void) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn dlsym(_object: *mut c_void, _name: *const c_char) -> *mut c_void {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn dlerror(_object: *mut c_void) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn dladdr(addr: *mut c_void, info: *mut Dl_info) -> c_int {
|
||||
#[allow(static_mut_refs)]
|
||||
let objects = OBJECTS.assume_init_ref();
|
||||
let Some(symbol) = objects.lookup_address(addr.addr()) else {
|
||||
log::warn!("dladdr({addr:p}) -> NULL");
|
||||
return 0;
|
||||
};
|
||||
|
||||
if let Some(info) = info.as_mut() {
|
||||
info.dli_sname = symbol.symbol_name.as_ptr();
|
||||
info.dli_saddr = ptr::with_exposed_provenance_mut(symbol.symbol_base);
|
||||
info.dli_fname = symbol.object_name.as_ptr();
|
||||
info.dli_fbase = ptr::with_exposed_provenance_mut(symbol.object_base);
|
||||
}
|
||||
|
||||
1
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn dladdr1(
|
||||
_addr: *mut c_void,
|
||||
_info: *mut Dl_info,
|
||||
_extra_info: *mut *mut c_void,
|
||||
_flags: c_int,
|
||||
) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use std::io;
|
||||
pub enum Error {
|
||||
#[error("{0}")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("ELF parse error")]
|
||||
#[error("ELF parse error: {0}")]
|
||||
ElfParse(#[from] elf::ParseError),
|
||||
#[error("Memory mapping error: {0:?}")]
|
||||
MemoryMap(yggdrasil_rt::Error),
|
||||
|
@ -8,7 +8,7 @@
|
||||
)]
|
||||
#![allow(clippy::new_without_default, clippy::missing_transmute_annotations)]
|
||||
|
||||
use std::process::ExitCode;
|
||||
use std::{mem::MaybeUninit, process::ExitCode};
|
||||
|
||||
use error::Error;
|
||||
use object::Object;
|
||||
@ -25,12 +25,15 @@ pub mod search;
|
||||
pub mod state;
|
||||
pub mod thread_local;
|
||||
|
||||
static mut OBJECTS: MaybeUninit<ObjectSet> = MaybeUninit::uninit();
|
||||
|
||||
fn run(binary: &str, args: &[String]) -> Result<!, Error> {
|
||||
// Open root and its dependencies
|
||||
let root = Object::open(binary)?;
|
||||
let root = Object::open(binary, true)?;
|
||||
let mut objects = ObjectSet::new(root);
|
||||
objects.open_root_dependencies()?;
|
||||
let tls_image = objects.load_all()?;
|
||||
objects.state.add_magic_symbols();
|
||||
objects.resolve_symbols()?;
|
||||
|
||||
if let Err(undefined) = objects.state.no_undefined_symbols() {
|
||||
@ -100,6 +103,9 @@ fn run(binary: &str, args: &[String]) -> Result<!, Error> {
|
||||
let entry = objects.root.entry().ok_or(Error::NoEntrypoint)?;
|
||||
log::info!("entry = {:p}", entry);
|
||||
|
||||
#[allow(static_mut_refs)]
|
||||
unsafe { OBJECTS.write(objects) };
|
||||
|
||||
let argument = env::build_argument(args, &auxv)?;
|
||||
unsafe { enter(entry, argument) };
|
||||
}
|
||||
|
@ -1,11 +1,5 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufReader, Read, Seek, SeekFrom},
|
||||
mem,
|
||||
ops::Range,
|
||||
path::{Path, PathBuf},
|
||||
ptr,
|
||||
rc::Rc,
|
||||
cmp, collections::HashMap, ffi::{CStr, CString}, fs::File, io::{BufReader, Read, Seek, SeekFrom}, mem, ops::Range, path::{Path, PathBuf}, ptr, rc::Rc
|
||||
};
|
||||
|
||||
use elf::{
|
||||
@ -51,6 +45,8 @@ pub struct DynamicSymbol {
|
||||
|
||||
pub struct Object {
|
||||
pub path: PathBuf,
|
||||
pub c_name: CString,
|
||||
pub load_range: Option<Range<usize>>,
|
||||
pub file: BufReader<File>,
|
||||
pub elf: ElfStream<AnyEndian, BufReader<File>>,
|
||||
|
||||
@ -69,6 +65,8 @@ pub struct Object {
|
||||
|
||||
init_array: Option<Range<usize>>,
|
||||
pre_init_array: Option<Range<usize>>,
|
||||
|
||||
symtab: Vec<(Symbol, Option<CString>)>,
|
||||
}
|
||||
|
||||
impl ResolvedSymbol<'_> {
|
||||
@ -83,8 +81,9 @@ impl ResolvedSymbol<'_> {
|
||||
}
|
||||
|
||||
impl Object {
|
||||
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
|
||||
pub fn open<P: AsRef<Path>>(path: P, need_symtab: bool) -> Result<Self, Error> {
|
||||
let path = path.as_ref().to_owned();
|
||||
let c_name = CString::new(path.to_str().unwrap()).unwrap();
|
||||
let file = BufReader::new(File::open(&path)?);
|
||||
let mut elf = ElfStream::open_stream(file)?;
|
||||
let file = BufReader::new(File::open(&path)?);
|
||||
@ -155,8 +154,22 @@ impl Object {
|
||||
}
|
||||
let tls = tls_segments.first().map(|s| **s);
|
||||
|
||||
let mut symtab = vec![];
|
||||
if need_symtab {
|
||||
if let Some((symtab_items, strtab)) = elf.symbol_table()? {
|
||||
for symbol in symtab_items {
|
||||
let name = strtab.get(symbol.st_name as _)?;
|
||||
let c_name = CString::new(name).ok();
|
||||
symtab.push((symbol, c_name));
|
||||
}
|
||||
}
|
||||
symtab.sort_by(|(s0, _), (s1, _)| cmp::Ord::cmp(&s0.st_value, &s1.st_value));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
path,
|
||||
c_name,
|
||||
load_range: None,
|
||||
file,
|
||||
elf,
|
||||
|
||||
@ -174,6 +187,7 @@ impl Object {
|
||||
|
||||
init_array: None,
|
||||
pre_init_array: None,
|
||||
symtab,
|
||||
})
|
||||
}
|
||||
|
||||
@ -200,6 +214,9 @@ impl Object {
|
||||
ElfType::Static => todo!(),
|
||||
};
|
||||
|
||||
let load_base = (mapping.base() as isize - self.vma_start as isize) as usize;
|
||||
self.load_range = Some(load_base..load_base + mapping_size);
|
||||
|
||||
log::info!(
|
||||
"Actual range: {:#x?} ({:+#x})",
|
||||
mapping.range(),
|
||||
@ -275,6 +292,22 @@ impl Object {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn lookup_address(&self, address: usize) -> Option<(&CStr, usize)> {
|
||||
const NULL_STRING: &CStr = c"";
|
||||
let address = address as u64;
|
||||
|
||||
let base = self.load_range.as_ref().map(|r| r.start).unwrap_or(0);
|
||||
|
||||
for (symbol, name) in self.symtab.iter() {
|
||||
if address >= symbol.st_value && address - symbol.st_value < symbol.st_size {
|
||||
let name = name.as_ref().map(|c| c.as_c_str()).unwrap_or(NULL_STRING);
|
||||
return Some((name, symbol.st_value as usize + base));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn place_tls_copy(&mut self, dst: &mut [u8]) -> Result<(), Error> {
|
||||
let tls = self.tls.as_ref().unwrap();
|
||||
if tls.p_filesz > 0 {
|
||||
|
@ -12,7 +12,7 @@ pub fn find_library(name: &str) -> Result<PathBuf, Error> {
|
||||
if path.exists() {
|
||||
return Ok(path);
|
||||
}
|
||||
let path = Path::new("/tmp").join(name);
|
||||
let path = Path::new("/mnt").join(name);
|
||||
if path.exists() {
|
||||
return Ok(path);
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
collections::{HashMap, HashSet}, ffi::{CStr, CString}, iter, path::{Path, PathBuf}, rc::Rc
|
||||
};
|
||||
|
||||
use elf::abi::{STT_FUNC, STT_NOTYPE, STT_OBJECT, STT_TLS};
|
||||
@ -13,10 +11,20 @@ use crate::{
|
||||
thread_local::{self, TlsImage, TlsLayout},
|
||||
};
|
||||
|
||||
pub struct SymbolByAddress<'a> {
|
||||
pub object_name: &'a CStr,
|
||||
pub object_base: usize,
|
||||
pub symbol_name: &'a CStr,
|
||||
pub symbol_base: usize,
|
||||
}
|
||||
|
||||
pub struct ExportedNormalSymbol {
|
||||
pub source: PathBuf,
|
||||
pub value: usize,
|
||||
pub size: usize,
|
||||
pub weak: bool,
|
||||
pub object_id: u32,
|
||||
pub c_name: CString
|
||||
}
|
||||
|
||||
pub struct ExportedTlsSymbol {
|
||||
@ -75,6 +83,8 @@ impl State {
|
||||
let value: usize = (isize::try_from(sym.raw.st_value).unwrap() + offset)
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let size: usize = sym.raw.st_size as usize;
|
||||
let c_name = CString::new(sym.name.as_ref()).unwrap();
|
||||
|
||||
match self.symbol_table.get_mut(&sym.name) {
|
||||
// Stronger binding exported
|
||||
@ -82,7 +92,10 @@ impl State {
|
||||
*export = ExportedNormalSymbol {
|
||||
source,
|
||||
value,
|
||||
size,
|
||||
weak,
|
||||
c_name,
|
||||
object_id,
|
||||
};
|
||||
}
|
||||
// Do nothing, already strong or already weak
|
||||
@ -93,7 +106,10 @@ impl State {
|
||||
ExportedNormalSymbol {
|
||||
source,
|
||||
value,
|
||||
size,
|
||||
weak,
|
||||
c_name,
|
||||
object_id,
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -175,6 +191,27 @@ impl State {
|
||||
Err(&self.undefined_references)
|
||||
}
|
||||
}
|
||||
|
||||
fn export_magic_symbol(&mut self, name: &str, value: usize) {
|
||||
let c_name = CString::new(name).unwrap();
|
||||
self.symbol_table.insert(name.into(), ExportedNormalSymbol {
|
||||
source: "".into(),
|
||||
weak: false,
|
||||
value,
|
||||
size: 0,
|
||||
c_name,
|
||||
object_id: u32::MAX
|
||||
});
|
||||
}
|
||||
|
||||
pub fn add_magic_symbols(&mut self) {
|
||||
self.export_magic_symbol("dlopen", crate::builtins::dlopen as usize);
|
||||
self.export_magic_symbol("dlclose", crate::builtins::dlclose as usize);
|
||||
self.export_magic_symbol("dlsym", crate::builtins::dlsym as usize);
|
||||
self.export_magic_symbol("dlerror", crate::builtins::dlerror as usize);
|
||||
self.export_magic_symbol("dladdr", crate::builtins::dladdr as usize);
|
||||
self.export_magic_symbol("dladdr1", crate::builtins::dladdr1 as usize);
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectSet {
|
||||
@ -189,23 +226,37 @@ impl ObjectSet {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_root_dependencies(&mut self) -> Result<(), Error> {
|
||||
for needed in self.root.needed() {
|
||||
// TODO load needed of needed
|
||||
pub fn objects(&self) -> impl Iterator<Item = &Object> {
|
||||
iter::chain(iter::once(&self.root), self.libraries.values())
|
||||
}
|
||||
|
||||
fn open_dependencies_of(&mut self, object_id: u32) -> Result<(), Error> {
|
||||
let object = match object_id {
|
||||
0 => &self.root,
|
||||
_ => self.libraries.get(&object_id).ok_or(Error::NotLoaded)?,
|
||||
};
|
||||
let needed = object.needed().cloned().collect::<Vec<_>>();
|
||||
for needed in needed {
|
||||
let path = search::find_library(needed.as_str())?;
|
||||
if self.library_path_map.contains_key(&path) {
|
||||
continue;
|
||||
}
|
||||
let mut object = Object::open(&path)?;
|
||||
let mut object = Object::open(&path, false)?;
|
||||
self.last_object_id += 1;
|
||||
let id = self.last_object_id;
|
||||
object.id = id;
|
||||
self.libraries.insert(id, object);
|
||||
self.library_path_map.insert(path, id);
|
||||
|
||||
self.open_dependencies_of(id)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn open_root_dependencies(&mut self) -> Result<(), Error> {
|
||||
self.open_dependencies_of(0)
|
||||
}
|
||||
|
||||
pub fn load_all(&mut self) -> Result<Option<TlsImage>, Error> {
|
||||
// Load all objects somewhere, order doesn't matter
|
||||
self.root.load()?;
|
||||
@ -278,4 +329,37 @@ impl ObjectSet {
|
||||
_ => self.libraries.get_mut(&id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_address(&self, address: usize) -> Option<SymbolByAddress> {
|
||||
// Lookup in stuff exported from .dynsym
|
||||
for (_, symbol) in self.state.symbol_table.iter() {
|
||||
if address >= symbol.value && address - symbol.value < symbol.size {
|
||||
let object = self.get(symbol.object_id).unwrap();
|
||||
let object_base = object.load_range.as_ref().map(|r| r.start).unwrap_or(0);
|
||||
return Some(SymbolByAddress {
|
||||
object_name: object.c_name.as_c_str(),
|
||||
object_base,
|
||||
symbol_name: symbol.c_name.as_c_str(),
|
||||
symbol_base: symbol.value
|
||||
});
|
||||
}
|
||||
}
|
||||
// Fall back to main object's .strtab
|
||||
for object in self.objects() {
|
||||
if let Some(range) = object.load_range.clone() {
|
||||
if range.contains(&address) {
|
||||
let address = address - range.start;
|
||||
let (name, base) = object.lookup_address(address)?;
|
||||
return Some(SymbolByAddress {
|
||||
object_name: object.c_name.as_c_str(),
|
||||
object_base: range.start,
|
||||
symbol_name: name,
|
||||
symbol_base: base,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -14,31 +14,38 @@ pub struct Dl_info {
|
||||
pub dli_saddr: *mut c_void,
|
||||
}
|
||||
|
||||
#[linkage = "weak"]
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn dlclose(_dl: *mut c_void) -> c_int {
|
||||
todo!()
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[linkage = "weak"]
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn dlerror() -> *mut c_char {
|
||||
todo!()
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[linkage = "weak"]
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn dlopen(_path: *const c_char, _flags: c_int) -> *mut c_void {
|
||||
todo!()
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[linkage = "weak"]
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn dlsym(_dl: *mut c_void, _name: *const c_char) -> *mut c_void {
|
||||
todo!()
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
// Non-POSIX
|
||||
#[linkage = "weak"]
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn dladdr(_addr: *mut c_void, _info: *mut Dl_info) -> c_int {
|
||||
-1
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[linkage = "weak"]
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn dladdr1(
|
||||
_addr: *mut c_void,
|
||||
@ -46,5 +53,5 @@ unsafe extern "C" fn dladdr1(
|
||||
_extra_info: *mut *mut c_void,
|
||||
_flags: c_int,
|
||||
) -> c_int {
|
||||
todo!()
|
||||
unreachable!()
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user