alemi
ecae892afb
initial implementation of remote tracing via tcp as seen in fasterthanlime, needs way more work to be reliable
110 lines
3.1 KiB
Rust
110 lines
3.1 KiB
Rust
use std::error::Error;
|
|
|
|
use libloading::os::unix::{Library, Symbol};
|
|
use retour::Function;
|
|
|
|
use tracing::warn;
|
|
|
|
use crate::tricks::find_argv0;
|
|
|
|
|
|
pub fn find_symbol<T : Function>(name: &str) -> Result<Option<T>, Box<dyn Error>> {
|
|
// try to find it among exported symbols
|
|
let this = Library::this(); // TODO don't reopen it every time
|
|
let sym : Result<Symbol<T>, libloading::Error> = unsafe { this.get(name.as_bytes()) };
|
|
if let Ok(s) = sym {
|
|
return Ok(Some(*s));
|
|
}
|
|
|
|
// try to read it from executable's elf
|
|
match find_argv0() {
|
|
None => warn!("could not find argv0 for process"),
|
|
Some(exec) => match procmaps::map_addr_path(&exec)? {
|
|
None => warn!("could not find base addr of process image"),
|
|
Some((base, path)) => match exec::offset_in_elf(&path, &name)? {
|
|
None => warn!("could not locate requested symbol in ELF"),
|
|
Some(offset) => {
|
|
let addr : *const () = (base + offset) as *const ();
|
|
return Ok(Some(unsafe { Function::from_ptr(addr) } ));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(None)
|
|
}
|
|
|
|
|
|
pub mod exec {
|
|
use std::path::Path;
|
|
|
|
use elf::{ParseError, ElfBytes, endian::AnyEndian};
|
|
use tracing::{warn, debug};
|
|
|
|
pub fn offset_in_elf(path: &Path, symbol: &str) -> Result<Option<usize>, ParseError> {
|
|
let exec_data = std::fs::read(path)?;
|
|
let headers = ElfBytes::<AnyEndian>::minimal_parse(&exec_data)?;
|
|
let common = headers.find_common_data()?;
|
|
|
|
// first try with hash table
|
|
match common.sysv_hash {
|
|
None => warn!("missing symbols hash table in ELF"),
|
|
Some(sysv_hash) => match common.dynsyms {
|
|
None => warn!("missing dynamic symbols in ELF"),
|
|
Some(dynsyms) => match common.dynsyms_strs {
|
|
None => warn!("missing string tab for dynamic symbols in ELF"),
|
|
Some(dynsyms_strs) => match sysv_hash.find(symbol.as_bytes(), &dynsyms, &dynsyms_strs)? {
|
|
None => debug!("could not find symbol {} in ELF hashmap", symbol),
|
|
Some((_id, sym)) => return Ok(Some(sym.st_value as usize)),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// fall back to iterating symbols table
|
|
match common.symtab {
|
|
None => warn!("missing symbols table in ELF"),
|
|
Some(symtab) => match common.symtab_strs {
|
|
None => warn!("missing names for symbols in ELF"),
|
|
Some(symtab_strs) => {
|
|
for sym in symtab {
|
|
let name = symtab_strs.get(sym.st_name as usize)?;
|
|
if name == symbol {
|
|
return Ok(Some(sym.st_value as usize));
|
|
}
|
|
}
|
|
debug!("no symbol matched '{}'", symbol);
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
pub mod procmaps {
|
|
use std::path::PathBuf;
|
|
|
|
use proc_maps::get_process_maps;
|
|
use tracing::{debug, warn};
|
|
|
|
use crate::tricks::fmt_path;
|
|
|
|
pub fn map_addr_path(name: &str) -> std::io::Result<Option<(usize, PathBuf)>> {
|
|
let proc_maps = get_process_maps(std::process::id() as i32)?;
|
|
|
|
for map in proc_maps {
|
|
debug!("map > 0x{:08X} {} [{:x}] - {} [{}]", map.start(), map.flags, map.offset, map.inode, fmt_path(map.filename()));
|
|
if map.is_exec() {
|
|
if let Some(path) = map.filename() {
|
|
if path.ends_with(name) {
|
|
return Ok(Some((map.start() - map.offset, map.filename().unwrap().to_path_buf())));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
warn!("could not find address of '{}'", name);
|
|
|
|
Ok(None)
|
|
}
|
|
}
|