From 6660e28287cd7c050266bf95685be7807158bc7b Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sun, 23 Feb 2025 21:28:19 +0200 Subject: [PATCH] more refactor for the refactor gods --- crates/.scratch.rs | 436 ++++++++++++++++++++++++++++++++ crates/vestal/src/.scratch.rs | 388 ---------------------------- crates/vestal/src/call_sites.rs | 5 +- crates/vestal/src/exports.rs | 6 +- crates/vestal/src/imports.rs | 31 ++- crates/vestal/src/main.rs | 74 +----- crates/vestal/src/show.rs | 8 +- 7 files changed, 476 insertions(+), 472 deletions(-) create mode 100644 crates/.scratch.rs delete mode 100644 crates/vestal/src/.scratch.rs diff --git a/crates/.scratch.rs b/crates/.scratch.rs new file mode 100644 index 0000000..ad6c3ff --- /dev/null +++ b/crates/.scratch.rs @@ -0,0 +1,436 @@ +use crate::*; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#[derive(Default, Debug)] +struct Vestal { + search_paths: Vec, + paths_visited: BTreeSet>, + path_to_bang: BTreeMap, Arc<[u8]>>, + path_to_data: BTreeMap, Arc<[u8]>>, + path_to_pe: BTreeMap, Arc>, + path_to_rpe: BTreeMap, Arc>, + addr_to_import: BTreeMap, + //path_to_exports: BTreeMap, Vec>, + //path_to_imports: BTreeMap, Vec>, +} +impl Vestal { + fn enter (&mut self, path: &PathBuf) -> Usually<()> { + let mut total = 0usize; + self.load(&path)?; + for (dll_path, dll) in self.path_to_pe.iter() { + self.show_dll(dll_path)?; + let dll_data = self.path_to_data.get(dll_path).unwrap(); + let len = dll_data.len(); + println!(" (bytes {len} 0x{len:x})"); + self.show_calls(dll_path, dll_path.as_ref() == path)?; + if dll_path.as_ref() == path { + println!("{:?}", dll_data.hex_dump()); + let text = dll.get_section_by_name(".text")?; + println!("\n{:#?}", text); + let start = text.pointer_to_raw_data.0 as usize; + let size = text.size_of_raw_data as usize; + let vsize = text.virtual_size as usize; + println!("\n{:?}", &dll_data[start..start+size].hex_dump()); + println!("\n{:?}", &dll_data[start..start+vsize].hex_dump()); + let elf = crate::link::create_elf(&dll_data[start..start+vsize]); + println!("\n{:?}", &elf.hex_dump()); + println!("\n{:?}", &elf.len()); + let mut options = std::fs::OpenOptions::new(); + options.write(true).create(true).mode(0o755); + let mut file = options.open("output")?; + file.write_all(&elf)?; + println!("\nDone."); + } + total += len; + //println!("{:?}", dll_data.hex_dump()); + } + //self.show_addr_to_import(); + println!("(bytes-total {total} (0x{total:x})"); + Ok(()) + } +} + +impl Vestal { + pub fn resolve (&self, name: &str) -> Usually> { + for base in self.search_paths.iter() { + let mut path = base.clone(); + path.push(name.to_lowercase()); + if std::fs::exists(&path)? { + return Ok(Some(canonicalize(&path)?)) + } + } + Ok(None) + } + pub fn load (&mut self, path: &PathBuf) -> Usually<()> { + if !self.paths_visited.contains(path) { + let path = Arc::new(path.clone()); + println!("\n(load {path:?})"); + self.paths_visited.insert(path.clone()); + let data = self.load_bang_data(&path)?; + let dll = self.load_pe(&path, &data)?; + //let rdll = self.load_rpe(&path, &dll, 0)?; + let imports = self.load_imports(&path, &dll); + let exports = self.load_exports(&path, &dll); + } + Ok(()) + } + pub fn load_imports (&mut self, path: &PathBuf, dll: &VecPE) -> Usually<()> { + let mut import_map = BTreeMap::new(); + let directory = ImportDirectory::parse(dll)?; + for descriptor in directory.descriptors { + let dep = descriptor.get_name(dll)?.as_str()?; + let iat = descriptor.get_first_thunk(dll)?; + let ilt = descriptor.get_original_first_thunk(dll)?; + let lut = descriptor.get_lookup_thunks(dll)?; + let resolved = Arc::new(self.resolve(&dep)?.expect("no path for {name}")); + print!(" (module {BOLD}{dep:?}{RESET} N=0x{:>08x} IAT=0x{:>08x} ILT=0x{:>08x}\n {resolved:?}", + &descriptor.name.0, + &descriptor.first_thunk.0, + &descriptor.original_first_thunk.0); + let mut imports = Vec::new(); + for (index, (thunk, orig, lookup, import)) in izip!( + iat.iter().map(|thunk|format!("0x{:08x}", match thunk { + Thunk::Thunk32(t) => panic!("32 bit thunk"), + Thunk::Thunk64(t) => t.0 + })), + ilt.iter().map(|thunk|format!("0x{:08x}", match thunk { + Thunk::Thunk32(t) => panic!("32 bit original thunk"), + Thunk::Thunk64(t) => t.0 + })), + lut.iter().map(|thunk|format!("0x{:08x}", match thunk { + Thunk::Thunk32(t) => panic!("32 bit original thunk"), + Thunk::Thunk64(t) => t.0 + })), + descriptor.get_imports(dll)? + ).enumerate() { + let call_via = descriptor.first_thunk.0 + index as u32 * 8; + let name = match import { + ImportData::Ordinal(x) => { + //print!("\n (import-ordinal {BOLD}0x{:>08x}{RESET} IAT={} ILT={} LU={} 0x{:>04x})", + //call_via, thunk, orig, lookup, x); + format!("___VESTAL___ORD___{x}___") + }, + ImportData::ImportByName(name) => { + //print!("\n (import-by-name {BOLD}0x{:>08x}{RESET} IAT={} ILT={} LU={} {:?})", + //call_via, thunk, orig, lookup, name); + format!("{name}") + }, + }; + imports.push((thunk, orig, import)); + if let Some(existing) = self.addr_to_import.get(&call_via) { + panic!("addr space overlap at 0x{call_via:x}: {}::{} vs {}::{}", + existing.0, + existing.1, + dep.to_string(), + name); + } + self.addr_to_import.insert(call_via, (dep.to_string(), name)); + } + import_map.insert(dep, (resolved, imports)); + println!(")") + } + for (name, (path, imports)) in import_map.iter() { + self.load(path)?; + } + Ok(()) + } + pub fn load_exports (&mut self, path: &PathBuf, dll: &VecPE) -> Usually<()> { + Ok(()) + } + pub fn load_pe (&mut self, path: &Arc, data: &Arc<[u8]>) -> Usually> { + let pe = Arc::new(VecPE::from_disk_data(data)); + self.path_to_pe.insert(path.clone(), pe.clone()); + Ok(pe) + } + pub fn load_rpe (&mut self, path: &Arc, data: &VecPE, base: u64) -> Usually> { + let rdir = RelocationDirectory::parse(data)?; + let mut rpe = data.clone(); + rdir.relocate(&mut rpe, base)?; + let rpe = Arc::new(rpe); + self.path_to_rpe.insert(path.clone(), rpe.clone()); + Ok(rpe) + } +} +use crate::*; +use exe::pe::{VecPE, PtrPE}; +#[derive(Default)] +pub struct PEContext { + dlls: HashMap, +} +impl PEContext { + pub fn load (&mut self, name: &str, path: &PathBuf) -> Usually<()> { + let name = name.to_lowercase(); + if self.dlls.contains_key(&name) { + return Ok(()) + } + println!("\n(:= \"{}\" {:?})", name, path); + let dll = Dll::new(name.as_str(), path)?; + println!("(:= \"{}\" {:p}+{})", name, &dll.pe.buf, dll.pe.buf.len()); + let mut imports = vec![]; + for export in dll.goblin()?.imports.iter() { + //println!(" - {:8} + {:?} = {:?}", &export.rva, &export.offset, &export.name); + } + for import in dll.goblin()?.imports.iter() { + let dll = import.dll.to_lowercase(); + println!(" (-> {} {})", &dll, &import.name); + imports.push(dll); + } + self.dlls.insert(name.to_string(), dll); + for name in imports.iter() { + let path = format!("/home/user/Lab/Cosmo/wineprefix/drive_c/windows/system32/{}", name); + self.load(name, &PathBuf::from(path))?; + } + Ok(()) + } +} +struct Dll { + name: String, + path: PathBuf, + bang: Vec, + pe: PE, +} +impl Dll { + fn new (name: &str, path: &PathBuf) -> Usually { + let (bang, data) = slice_shebang(read(path)?.as_slice()); + let mut pe = PE::from_bytes(data.as_slice())?; + apply_relocations(&mut pe)?; + Ok(Self { + bang, + name: name.to_string(), + path: path.into(), + pe, + }) + } + fn goblin (&self) -> Usually { + Ok(goblin::pe::PE::parse(self.pe.buf.as_slice())?) + } +} +use std::ffi::c_void; +//typedef VstIntPtr (VSTCALLBACK *audioMasterCallback) (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); +#[repr(C)] pub struct AEffect { + magic: [u8; 4], + /// ```c + /// typedef VstIntPtr (VSTCALLBACK *AEffectDispatcherProc) (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); + /// ``` + dispatcher: fn(*const AEffect, i32, i32, i32, *mut c_void, f32) -> usize, + /// ```c + /// typedef void (VSTCALLBACK *AEffectProcessProc) (AEffect* effect, float** inputs, float** outputs, VstInt32 sampleFrames); + /// typedef void (VSTCALLBACK *AEffectProcessDoubleProc) (AEffect* effect, double** inputs, double** outputs, VstInt32 sampleFrames); + /// ``` + process: usize, + /// ```c + /// typedef void (VSTCALLBACK *AEffectSetParameterProc) (AEffect* effect, VstInt32 index, float parameter); + /// ``` + set_parameter: usize, + /// ```c + /// typedef float (VSTCALLBACK *AEffectGetParameterProc) (AEffect* effect, VstInt32 index); + /// ``` + get_parameter: usize, + num_programs: i32, + num_params: i32, + num_inputs: i32, + num_outputs: i32, + flags: i32, + resvd1: usize, + resvd2: usize, + initial_delay: i32, + real_qualities: i32, + off_qualities: i32, + io_ratio: f32, + object: usize, + user: usize, + unique_id: i32, + version: i32, + process_replacing: usize, + process_double_replacing: usize, + _future: [u8;56], +} +impl AEffect { + fn null_dispatcher (_: *const AEffect, _: i32, _: i32, _: i32, _: *mut c_void, _: f32) -> usize { + 0 + } + fn host_callback ( + _: *const AEffect, opcode: i32, index: i32, value: i32, ptr: *mut c_void, opt: f32 + ) -> usize { + 0 + } + pub fn run (address: usize) -> Self { + let effect = Self::default(); + // call(address, host_callback) + effect + } +} +impl Default for AEffect { + fn default () -> Self { + Self { + magic: [ b'V', b's', b't', b'P'], + dispatcher: Self::null_dispatcher, + process: 0, + set_parameter: 0, + get_parameter: 0, + num_programs: 0, + num_params: 0, + num_inputs: 0, + num_outputs: 0, + flags: 0, + resvd1: 0, + resvd2: 0, + initial_delay: 0, + real_qualities: 0, + off_qualities: 0, + io_ratio: 0.0, + object: 0, + user: 0, + unique_id: 0, + version: 0, + process_replacing: 0, + process_double_replacing: 0, + _future: [0;56], + } + } +} + if let Some(link) = self.decode_link(&mut decoder, verbose)? { + links += 1; + self.call_sites.insert(link.source, link.clone()); + } + } + } + + fn decode_link (self: Arc, decoder: &mut Decoder, verbose: bool) -> Usually>> { + let position = decoder.position(); + let instruction = decoder.decode(); + let opcodes = &self.code[position..position+instruction.len()]; + if CallSite::matches(&instruction) && !CallSite::skip(opcodes) { + let offset = (position + self.code_start) as u32; + let source = self.pe.offset_to_rva(Offset(offset))?.0; + if let Some(target) = Self::target(opcodes, source) { + let module: Arc = Module::default().into(); + let method: Arc = "TODO".into(); + if verbose { + let offset = format!("0x{offset:08x}"); + let source = format!("0x{source:08x}"); + let instruct = format!("{BOLD}{instruction:30}{RESET}"); + let target = format!("0x{target:08x}"); + let external = format!("{module}::{method}"); + println!("({BOLD}{offset}{RESET} {source} {instruct} {target} {external}"); + } + return Ok(Some(Arc::new(CallSite { + caller: self.clone(), + offset, + source, + length: opcodes.len(), + target, + module, + method, + }))) + } + } + Ok(None) + } + + fn dep_name (&self, target: u32) -> (Option>, Option>, Arc) { + (None, None, "".into()) + let deps = Some(&self.deps_by_address); + let module = deps.and_then(|deps|deps.get(&target)).map(|dep|dep.0.clone()); + let method = deps.and_then(|deps|deps.get(&target)).map(|dep|dep.1.clone()); + let external = format!("{}::{}", + module.as_ref().map(|x|x.as_ref()).unwrap_or("???"), + method.as_ref().map(|x|x.as_ref()).unwrap_or("???")); + (module, method, external.into()) + } + + fn links_collect (&mut self, verbose: bool) -> Usually { + let mut decoder = Decoder::with_ip(64, self.code.as_ref(), 0, 0); + let mut links = 0; + while decoder.can_decode() { + if let Some(link) = self.decode_link(&mut decoder, verbose)? { + links += 1; + //self.links_by_source.insert(link.source, link.clone()); + //if !self.links_by_target.contains_key(&link.target) { + //self.links_by_target.insert(link.target, Default::default()); + //} + //self.links_by_target.get_mut(&link.target).unwrap().push(link); + } + } + if verbose { + println!(" (link-sites {links})"); + for (target, sites) in self.links_by_target.iter() { + let (_, _, external) = self.dep_name(*target); + println!(" ({:>5}x link 0x{target:08x} {external})", sites.len()); + } + } + Ok(links) + } + / Collect all imported dependencies. + fn deps_collect (&mut self, verbose: bool) -> Usually<(usize, usize)> { + let pe = self.pe.clone(); + let directory = ImportDirectory::parse(pe.as_ref())?; + let mut modules = 0; + let mut methods = 0; + for descriptor in directory.descriptors.iter() { + let (module_name, buffer) = import_collect(pe.as_ref(), descriptor)?; + for (index, import_name) in buffer { + let link_via = descriptor.first_thunk.0 + index as u32 * 8; + let (new_modules, new_methods) = self.dep_collect( + link_via, &module_name, &import_name, verbose + )?; + modules += new_modules; + methods += new_methods; + } + } + if verbose { + println!(" (deps-modules {modules})"); + println!(" (deps-methods {methods})"); + self.show_deps_by_library(); + } + Ok((modules, methods)) + } + / Relink a VST. + fn run (&mut self, path: impl AsRef) -> Usually<()> { + let path = path.as_ref().to_str().expect("path must be unicode"); + let path = self.find(path)?.unwrap_or_else(||panic!("# not found: {path:?}")); + let main = self.load(&path)?; + Ok(()) + } + / Load all dependencies recursively, starting from the given path. + fn load_all (&mut self, path: &impl AsRef) -> Usually> { + let name = to_dll_name(path); + let path: Arc = Arc::from(PathBuf::from(path.as_ref())); + if self.modules.contains_key(&name) { + let module = self.modules.get(&name).unwrap().clone(); + return Ok(module) + } + Log::load(self.verbose, &path); + let module = Arc::new(Module::new(&path, self.verbose)?.load(self.verbose)?); + self.modules.insert(module.name.clone(), module.clone()); + Ok(module) + } + fn resolve (&self, dll: &Module, addr: u32, link: &Arc) -> Usually<()> { + let addr = (addr - dll.code_base) as usize; + dll.show_call_site(addr, link.length, 1); + link.show(); + Ok(()) + } + fn resolve_recursively (&self, dll: &Module, addr: u32, link: &Arc) -> Usually<()> { + self.resolve(dll, addr, link)?; + let module = &link.target.name; + let export = &link.method; + if let Some((module, export)) = match self.modules + .get(module) + .map(|module|module.exports.get(export)) + { + Some(Some(ThunkData::Function(rva))) => Some((module.clone(), export.clone())), + Some(Some(ThunkData::ForwarderString(rva))) => dll.resolve_forward(&rva)?, + Some(Some(thunk)) => panic!("# unsupported {thunk:?}"), + Some(None) => panic!("# export not resolved: {export}"), + None => panic!("# module not resolved: {module}"), + } { + let module = self.modules.get(&module).unwrap_or_else(||panic!("# no module {module}")); + let method = module.exports.get(&export).unwrap_or_else(||panic!("# no method {export}")); + println!("{module:?} {export} {method:?}"); + } else { + panic!("# unresolved link at {:?} [{addr}]", &dll.name) + } + Ok(()) + } diff --git a/crates/vestal/src/.scratch.rs b/crates/vestal/src/.scratch.rs deleted file mode 100644 index 3767e0f..0000000 --- a/crates/vestal/src/.scratch.rs +++ /dev/null @@ -1,388 +0,0 @@ -use crate::*; - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -//#[derive(Default, Debug)] -//struct Vestal { - //search_paths: Vec, - //paths_visited: BTreeSet>, - //path_to_bang: BTreeMap, Arc<[u8]>>, - //path_to_data: BTreeMap, Arc<[u8]>>, - //path_to_pe: BTreeMap, Arc>, - //path_to_rpe: BTreeMap, Arc>, - //addr_to_import: BTreeMap, - ////path_to_exports: BTreeMap, Vec>, - ////path_to_imports: BTreeMap, Vec>, -//} -//impl Vestal { - //fn enter (&mut self, path: &PathBuf) -> Usually<()> { - //let mut total = 0usize; - //self.load(&path)?; - //for (dll_path, dll) in self.path_to_pe.iter() { - //self.show_dll(dll_path)?; - //let dll_data = self.path_to_data.get(dll_path).unwrap(); - //let len = dll_data.len(); - //println!(" (bytes {len} 0x{len:x})"); - //self.show_calls(dll_path, dll_path.as_ref() == path)?; - //if dll_path.as_ref() == path { - //println!("{:?}", dll_data.hex_dump()); - //let text = dll.get_section_by_name(".text")?; - //println!("\n{:#?}", text); - //let start = text.pointer_to_raw_data.0 as usize; - //let size = text.size_of_raw_data as usize; - //let vsize = text.virtual_size as usize; - //println!("\n{:?}", &dll_data[start..start+size].hex_dump()); - //println!("\n{:?}", &dll_data[start..start+vsize].hex_dump()); - //let elf = crate::link::create_elf(&dll_data[start..start+vsize]); - //println!("\n{:?}", &elf.hex_dump()); - //println!("\n{:?}", &elf.len()); - //let mut options = std::fs::OpenOptions::new(); - //options.write(true).create(true).mode(0o755); - //let mut file = options.open("output")?; - //file.write_all(&elf)?; - //println!("\nDone."); - //} - //total += len; - ////println!("{:?}", dll_data.hex_dump()); - //} - ////self.show_addr_to_import(); - //println!("(bytes-total {total} (0x{total:x})"); - //Ok(()) - //} -//} - -//impl Vestal { - //pub fn resolve (&self, name: &str) -> Usually> { - //for base in self.search_paths.iter() { - //let mut path = base.clone(); - //path.push(name.to_lowercase()); - //if std::fs::exists(&path)? { - //return Ok(Some(canonicalize(&path)?)) - //} - //} - //Ok(None) - //} - //pub fn load (&mut self, path: &PathBuf) -> Usually<()> { - //if !self.paths_visited.contains(path) { - //let path = Arc::new(path.clone()); - //println!("\n(load {path:?})"); - //self.paths_visited.insert(path.clone()); - //let data = self.load_bang_data(&path)?; - //let dll = self.load_pe(&path, &data)?; - ////let rdll = self.load_rpe(&path, &dll, 0)?; - //let imports = self.load_imports(&path, &dll); - //let exports = self.load_exports(&path, &dll); - //} - //Ok(()) - //} - //pub fn load_imports (&mut self, path: &PathBuf, dll: &VecPE) -> Usually<()> { - //let mut import_map = BTreeMap::new(); - //let directory = ImportDirectory::parse(dll)?; - //for descriptor in directory.descriptors { - //let dep = descriptor.get_name(dll)?.as_str()?; - //let iat = descriptor.get_first_thunk(dll)?; - //let ilt = descriptor.get_original_first_thunk(dll)?; - //let lut = descriptor.get_lookup_thunks(dll)?; - //let resolved = Arc::new(self.resolve(&dep)?.expect("no path for {name}")); - //print!(" (module {BOLD}{dep:?}{RESET} N=0x{:>08x} IAT=0x{:>08x} ILT=0x{:>08x}\n {resolved:?}", - //&descriptor.name.0, - //&descriptor.first_thunk.0, - //&descriptor.original_first_thunk.0); - //let mut imports = Vec::new(); - //for (index, (thunk, orig, lookup, import)) in izip!( - //iat.iter().map(|thunk|format!("0x{:08x}", match thunk { - //Thunk::Thunk32(t) => panic!("32 bit thunk"), - //Thunk::Thunk64(t) => t.0 - //})), - //ilt.iter().map(|thunk|format!("0x{:08x}", match thunk { - //Thunk::Thunk32(t) => panic!("32 bit original thunk"), - //Thunk::Thunk64(t) => t.0 - //})), - //lut.iter().map(|thunk|format!("0x{:08x}", match thunk { - //Thunk::Thunk32(t) => panic!("32 bit original thunk"), - //Thunk::Thunk64(t) => t.0 - //})), - //descriptor.get_imports(dll)? - //).enumerate() { - //let call_via = descriptor.first_thunk.0 + index as u32 * 8; - //let name = match import { - //ImportData::Ordinal(x) => { - ////print!("\n (import-ordinal {BOLD}0x{:>08x}{RESET} IAT={} ILT={} LU={} 0x{:>04x})", - ////call_via, thunk, orig, lookup, x); - //format!("___VESTAL___ORD___{x}___") - //}, - //ImportData::ImportByName(name) => { - ////print!("\n (import-by-name {BOLD}0x{:>08x}{RESET} IAT={} ILT={} LU={} {:?})", - ////call_via, thunk, orig, lookup, name); - //format!("{name}") - //}, - //}; - //imports.push((thunk, orig, import)); - //if let Some(existing) = self.addr_to_import.get(&call_via) { - //panic!("addr space overlap at 0x{call_via:x}: {}::{} vs {}::{}", - //existing.0, - //existing.1, - //dep.to_string(), - //name); - //} - //self.addr_to_import.insert(call_via, (dep.to_string(), name)); - //} - //import_map.insert(dep, (resolved, imports)); - //println!(")") - //} - //for (name, (path, imports)) in import_map.iter() { - //self.load(path)?; - //} - //Ok(()) - //} - //pub fn load_exports (&mut self, path: &PathBuf, dll: &VecPE) -> Usually<()> { - //Ok(()) - //} - //pub fn load_pe (&mut self, path: &Arc, data: &Arc<[u8]>) -> Usually> { - //let pe = Arc::new(VecPE::from_disk_data(data)); - //self.path_to_pe.insert(path.clone(), pe.clone()); - //Ok(pe) - //} - //pub fn load_rpe (&mut self, path: &Arc, data: &VecPE, base: u64) -> Usually> { - //let rdir = RelocationDirectory::parse(data)?; - //let mut rpe = data.clone(); - //rdir.relocate(&mut rpe, base)?; - //let rpe = Arc::new(rpe); - //self.path_to_rpe.insert(path.clone(), rpe.clone()); - //Ok(rpe) - //} -//} -use crate::*; -use exe::pe::{VecPE, PtrPE}; -#[derive(Default)] -pub struct PEContext { - dlls: HashMap, -} -impl PEContext { - pub fn load (&mut self, name: &str, path: &PathBuf) -> Usually<()> { - let name = name.to_lowercase(); - if self.dlls.contains_key(&name) { - return Ok(()) - } - println!("\n(:= \"{}\" {:?})", name, path); - let dll = Dll::new(name.as_str(), path)?; - println!("(:= \"{}\" {:p}+{})", name, &dll.pe.buf, dll.pe.buf.len()); - let mut imports = vec![]; - for export in dll.goblin()?.imports.iter() { - //println!(" - {:8} + {:?} = {:?}", &export.rva, &export.offset, &export.name); - } - for import in dll.goblin()?.imports.iter() { - let dll = import.dll.to_lowercase(); - println!(" (-> {} {})", &dll, &import.name); - imports.push(dll); - } - self.dlls.insert(name.to_string(), dll); - for name in imports.iter() { - let path = format!("/home/user/Lab/Cosmo/wineprefix/drive_c/windows/system32/{}", name); - self.load(name, &PathBuf::from(path))?; - } - Ok(()) - } -} -struct Dll { - name: String, - path: PathBuf, - bang: Vec, - pe: PE, -} -impl Dll { - fn new (name: &str, path: &PathBuf) -> Usually { - let (bang, data) = slice_shebang(read(path)?.as_slice()); - let mut pe = PE::from_bytes(data.as_slice())?; - apply_relocations(&mut pe)?; - Ok(Self { - bang, - name: name.to_string(), - path: path.into(), - pe, - }) - } - fn goblin (&self) -> Usually { - Ok(goblin::pe::PE::parse(self.pe.buf.as_slice())?) - } -} -use std::ffi::c_void; -//typedef VstIntPtr (VSTCALLBACK *audioMasterCallback) (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); -#[repr(C)] pub struct AEffect { - magic: [u8; 4], - /// ```c - /// typedef VstIntPtr (VSTCALLBACK *AEffectDispatcherProc) (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); - /// ``` - dispatcher: fn(*const AEffect, i32, i32, i32, *mut c_void, f32) -> usize, - /// ```c - /// typedef void (VSTCALLBACK *AEffectProcessProc) (AEffect* effect, float** inputs, float** outputs, VstInt32 sampleFrames); - /// typedef void (VSTCALLBACK *AEffectProcessDoubleProc) (AEffect* effect, double** inputs, double** outputs, VstInt32 sampleFrames); - /// ``` - process: usize, - /// ```c - /// typedef void (VSTCALLBACK *AEffectSetParameterProc) (AEffect* effect, VstInt32 index, float parameter); - /// ``` - set_parameter: usize, - /// ```c - /// typedef float (VSTCALLBACK *AEffectGetParameterProc) (AEffect* effect, VstInt32 index); - /// ``` - get_parameter: usize, - num_programs: i32, - num_params: i32, - num_inputs: i32, - num_outputs: i32, - flags: i32, - resvd1: usize, - resvd2: usize, - initial_delay: i32, - real_qualities: i32, - off_qualities: i32, - io_ratio: f32, - object: usize, - user: usize, - unique_id: i32, - version: i32, - process_replacing: usize, - process_double_replacing: usize, - _future: [u8;56], -} -impl AEffect { - fn null_dispatcher (_: *const AEffect, _: i32, _: i32, _: i32, _: *mut c_void, _: f32) -> usize { - 0 - } - fn host_callback ( - _: *const AEffect, opcode: i32, index: i32, value: i32, ptr: *mut c_void, opt: f32 - ) -> usize { - 0 - } - pub fn run (address: usize) -> Self { - let effect = Self::default(); - // call(address, host_callback) - effect - } -} -impl Default for AEffect { - fn default () -> Self { - Self { - magic: [ b'V', b's', b't', b'P'], - dispatcher: Self::null_dispatcher, - process: 0, - set_parameter: 0, - get_parameter: 0, - num_programs: 0, - num_params: 0, - num_inputs: 0, - num_outputs: 0, - flags: 0, - resvd1: 0, - resvd2: 0, - initial_delay: 0, - real_qualities: 0, - off_qualities: 0, - io_ratio: 0.0, - object: 0, - user: 0, - unique_id: 0, - version: 0, - process_replacing: 0, - process_double_replacing: 0, - _future: [0;56], - } - } -} - //if let Some(link) = self.decode_link(&mut decoder, verbose)? { - //links += 1; - //self.call_sites.insert(link.source, link.clone()); - //} - //} - //} - - //fn decode_link (self: Arc, decoder: &mut Decoder, verbose: bool) -> Usually>> { - //let position = decoder.position(); - //let instruction = decoder.decode(); - //let opcodes = &self.code[position..position+instruction.len()]; - //if CallSite::matches(&instruction) && !CallSite::skip(opcodes) { - //let offset = (position + self.code_start) as u32; - //let source = self.pe.offset_to_rva(Offset(offset))?.0; - //if let Some(target) = Self::target(opcodes, source) { - //let module: Arc = Module::default().into(); - //let method: Arc = "TODO".into(); - //if verbose { - //let offset = format!("0x{offset:08x}"); - //let source = format!("0x{source:08x}"); - //let instruct = format!("{BOLD}{instruction:30}{RESET}"); - //let target = format!("0x{target:08x}"); - //let external = format!("{module}::{method}"); - //println!("({BOLD}{offset}{RESET} {source} {instruct} {target} {external}"); - //} - //return Ok(Some(Arc::new(CallSite { - //caller: self.clone(), - //offset, - //source, - //length: opcodes.len(), - //target, - //module, - //method, - //}))) - //} - //} - //Ok(None) - //} - - //fn dep_name (&self, target: u32) -> (Option>, Option>, Arc) { - //(None, None, "".into()) - //let deps = Some(&self.deps_by_address); - //let module = deps.and_then(|deps|deps.get(&target)).map(|dep|dep.0.clone()); - //let method = deps.and_then(|deps|deps.get(&target)).map(|dep|dep.1.clone()); - //let external = format!("{}::{}", - //module.as_ref().map(|x|x.as_ref()).unwrap_or("???"), - //method.as_ref().map(|x|x.as_ref()).unwrap_or("???")); - //(module, method, external.into()) - //} - - //fn links_collect (&mut self, verbose: bool) -> Usually { - //let mut decoder = Decoder::with_ip(64, self.code.as_ref(), 0, 0); - //let mut links = 0; - //while decoder.can_decode() { - //if let Some(link) = self.decode_link(&mut decoder, verbose)? { - //links += 1; - ////self.links_by_source.insert(link.source, link.clone()); - ////if !self.links_by_target.contains_key(&link.target) { - ////self.links_by_target.insert(link.target, Default::default()); - ////} - ////self.links_by_target.get_mut(&link.target).unwrap().push(link); - //} - //} - //if verbose { - //println!(" (link-sites {links})"); - //for (target, sites) in self.links_by_target.iter() { - //let (_, _, external) = self.dep_name(*target); - //println!(" ({:>5}x link 0x{target:08x} {external})", sites.len()); - //} - //} - //Ok(links) - //} - /// Collect all imported dependencies. - //fn deps_collect (&mut self, verbose: bool) -> Usually<(usize, usize)> { - //let pe = self.pe.clone(); - //let directory = ImportDirectory::parse(pe.as_ref())?; - //let mut modules = 0; - //let mut methods = 0; - //for descriptor in directory.descriptors.iter() { - //let (module_name, buffer) = import_collect(pe.as_ref(), descriptor)?; - //for (index, import_name) in buffer { - //let link_via = descriptor.first_thunk.0 + index as u32 * 8; - //let (new_modules, new_methods) = self.dep_collect( - //link_via, &module_name, &import_name, verbose - //)?; - //modules += new_modules; - //methods += new_methods; - //} - //} - //if verbose { - //println!(" (deps-modules {modules})"); - //println!(" (deps-methods {methods})"); - //self.show_deps_by_library(); - //} - //Ok((modules, methods)) - //} diff --git a/crates/vestal/src/call_sites.rs b/crates/vestal/src/call_sites.rs index 36045d0..c693041 100644 --- a/crates/vestal/src/call_sites.rs +++ b/crates/vestal/src/call_sites.rs @@ -1,9 +1,8 @@ use crate::*; impl Module { /// Collect all calls that point to imports. - pub fn load_call_sites (&mut self) -> Usually { + pub fn load_call_sites (self: Arc) -> Usually> { let mut decoder = Decoder::with_ip(64, self.code.as_ref(), 0, 0); - let mut links = 0; while decoder.can_decode() { let position = decoder.position(); let instruction = decoder.decode(); @@ -13,7 +12,7 @@ impl Module { let source = self.pe.offset_to_rva(Offset(offset))?.0; } } - Ok(links) + Ok(self) } fn target (opcodes: &[u8], offset_rva: u32) -> Option { let rip = offset_rva + opcodes.len() as u32; diff --git a/crates/vestal/src/exports.rs b/crates/vestal/src/exports.rs index deae667..64afafd 100644 --- a/crates/vestal/src/exports.rs +++ b/crates/vestal/src/exports.rs @@ -1,10 +1,10 @@ use crate::*; impl Module { /// Collect all exported methods. - pub fn load_exports (&mut self) -> Usually { + pub fn load_exports (self: Arc) -> Usually> { let directory = ExportDirectory::parse(self.pe.as_ref())?; let export_map = directory.get_export_map(self.pe.as_ref())?; - self.exports = export_map.into_iter().map(|(k, v)|(k.into(), v)).collect(); - Ok(self.exports.len()) + *self.exports.write().unwrap() = export_map.into_iter().map(|(k, v)|(k.into(), v)).collect(); + Ok(self) } } diff --git a/crates/vestal/src/imports.rs b/crates/vestal/src/imports.rs index b03eaf2..aa29893 100644 --- a/crates/vestal/src/imports.rs +++ b/crates/vestal/src/imports.rs @@ -1,22 +1,29 @@ use crate::*; impl Module { /// Collect all imported modules and methods. - pub fn load_imports (&mut self) -> Usually<()> { + pub fn load_imports (self: Arc, recurse: bool) -> Usually> { let pe = self.pe.clone(); let directory = ImportDirectory::parse(pe.as_ref())?; for descriptor in directory.descriptors.iter() { - let (module, imports) = Self::parse_import(pe.as_ref(), descriptor)?; - Log::import(self.verbose, &module, &imports); - for (index, method) in imports { - let call_via = descriptor.first_thunk.0 + index as u32 * 8; - self.load_dependency(call_via, &module, &method); - self.load_import(call_via, &module, &method); + let (name, imports) = Self::parse_import(pe.as_ref(), descriptor)?; + Log::dep(self.verbose, &name, &imports); + let exists = self.namespace.read().unwrap().contains_key(&name); + println!("{exists} {name} {:?}", self.namespace.read().unwrap().keys().collect::>()); + if !exists { + let path = self.find(&name)?.expect("not found"); + let module = Arc::new(Self { + namespace: self.namespace.clone(), + search_paths: self.search_paths.clone(), + ..Self::from_path(&path, self.verbose)? + }); + self.namespace.write().unwrap().insert(module.name.clone(), module.clone()); + module.load(recurse); } } if self.verbose { self.show_dependencies(); } - Ok(()) + Ok(self) } /// Collect an imported dependency's descriptor thunks into a temporary [Vec]. @@ -24,14 +31,16 @@ impl Module { -> Usually<(Arc, Vec<(usize, Arc)>)> { let mut imports = vec![]; - let module = descriptor.get_name(pe)?.as_str()?.to_lowercase(); for (index, import) in descriptor.get_imports(pe)?.iter().enumerate() { imports.push((index, match import { ImportData::Ordinal(x) => format!("___VESTAL_ORDINAL_{x}"), ImportData::ImportByName(name) => format!("{name}"), }.into())); } - Ok((module.into(), imports)) + Ok(( + descriptor.get_name(pe)?.as_str()?.to_lowercase().into(), + imports + )) } fn load_import ( @@ -45,9 +54,9 @@ impl Module { fn load_dependency ( &mut self, - call_via: u32, module_name: &Arc, method_name: &Arc, + call_via: u32, ) -> (usize, usize) { let mut modules = 0; let mut methods = 0; diff --git a/crates/vestal/src/main.rs b/crates/vestal/src/main.rs index b55cd3b..70fdb05 100644 --- a/crates/vestal/src/main.rs +++ b/crates/vestal/src/main.rs @@ -44,7 +44,7 @@ pub struct Module { /// Call targets to methods from imported modules. pub imports: BTreeMap, Arc)>, /// Addresses of exported methods by name - pub exports: BTreeMap, ThunkData>, + pub exports: RwLock, ThunkData>>, /// Locations in `.text` section that need to be patched pub call_sites: BTreeMap>, /// More detailed output. @@ -73,11 +73,11 @@ fn main () -> Usually<()> { // Parse command line arguments. let args = cli().get_matches(); if let Some(path) = args.get_one::("path") { - Module::from_path(path, *(args.get_one::("verbose").unwrap_or(&false)))? + Arc::new(Module::from_path(path, *(args.get_one::("verbose").unwrap_or(&false)))?) .search(std::env::current_dir()?) .search(canonicalize(path.clone().parent().expect("invalid parent path"))?) .search("/home/user/Lab/Cosmo/wineprefix/drive_c/windows/system32") - .load()?; + .load(true)?; } else { println!("Pass a path to a VST DLL"); } @@ -112,18 +112,17 @@ impl Module { }) } /// Add a search path - fn search (mut self, path: impl AsRef) -> Self { + fn search (self: Arc, path: impl AsRef) -> Arc { Log::add(self.verbose, &path); self.search_paths.write().unwrap().insert(path.as_ref().to_path_buf().into()); self } /// Load the dependency tree, starting from this module. - fn load (mut self) -> Usually { - let _ = self.load_imports()?; - let _ = self.load_exports()?; - let _ = self.load_call_sites()?; - println!("{self:?}"); - Ok(self) + fn load (self: Arc, recurse: bool) -> Usually> { + Ok(self + .load_imports(recurse)? + .load_exports()? + .load_call_sites()?) } /// Search for DLL by name in search paths. fn find (&self, name: &impl AsRef) -> Usually> { @@ -132,68 +131,17 @@ impl Module { let mut path = base.as_ref().clone(); path.push(name.to_lowercase()); if self.verbose { - println!("# looking for {name} at {path:?}"); + println!("(find? {name} {path:?})"); } if std::fs::exists(&path)? { if self.verbose { - println!("# found {name} at {path:?}"); - + println!("(found {name} {path:?})"); } return Ok(Some(canonicalize(&path)?)) } } Ok(None) } - /// Relink a VST. - //fn run (&mut self, path: impl AsRef) -> Usually<()> { - //let path = path.as_ref().to_str().expect("path must be unicode"); - //let path = self.find(path)?.unwrap_or_else(||panic!("# not found: {path:?}")); - //let main = self.load(&path)?; - //Ok(()) - //} - /// Load all dependencies recursively, starting from the given path. - //fn load_all (&mut self, path: &impl AsRef) -> Usually> { - //let name = to_dll_name(path); - //let path: Arc = Arc::from(PathBuf::from(path.as_ref())); - //if self.modules.contains_key(&name) { - //let module = self.modules.get(&name).unwrap().clone(); - //return Ok(module) - //} - //Log::load(self.verbose, &path); - //let module = Arc::new(Module::new(&path, self.verbose)?.load(self.verbose)?); - //self.modules.insert(module.name.clone(), module.clone()); - //Ok(module) - //} - fn resolve (&self, dll: &Module, addr: u32, link: &Arc) -> Usually<()> { - let addr = (addr - dll.code_base) as usize; - dll.show_call_site(addr, link.length, 1); - link.show(); - Ok(()) - } - fn resolve_recursively (&self, dll: &Module, addr: u32, link: &Arc) -> Usually<()> { - self.resolve(dll, addr, link)?; - let module = &link.target.name; - let export = &link.method; - //if let Some((module, export)) = match self.modules - //.get(module) - //.map(|module|module.exports.get(export)) - //{ - //Some(Some(ThunkData::Function(rva))) => Some((module.clone(), export.clone())), - //Some(Some(ThunkData::ForwarderString(rva))) => dll.resolve_forward(&rva)?, - //Some(Some(thunk)) => panic!("# unsupported {thunk:?}"), - //Some(None) => panic!("# export not resolved: {export}"), - //None => panic!("# module not resolved: {module}"), - //} { - //let module = self.modules.get(&module).unwrap_or_else(||panic!("# no module {module}")); - //let method = module.exports.get(&export).unwrap_or_else(||panic!("# no method {export}")); - //println!("{module:?} {export} {method:?}"); - //} else { - //panic!("# unresolved link at {:?} [{addr}]", &dll.name) - //} - Ok(()) - } -} -impl Module { } fn to_dll_name (path: &impl AsRef) -> Arc { diff --git a/crates/vestal/src/show.rs b/crates/vestal/src/show.rs index 9d0242c..5e62158 100644 --- a/crates/vestal/src/show.rs +++ b/crates/vestal/src/show.rs @@ -24,9 +24,9 @@ impl Log { println!("(load {:?})", path.as_ref()); } } - pub fn import (show: bool, module: &Arc, exports: &[(usize, Arc)]) { + pub fn dep (show: bool, name: &impl AsRef, exports: &[(usize, Arc)]) { if show { - println!("(import {:?} {})", module, exports.len()); + println!("(dep {:?} {})", name.as_ref(), exports.len()); } } } @@ -59,7 +59,7 @@ impl std::fmt::Debug for Module { //self.links_by_source.len(), //self.links_by_target.len()); let exports = format!("(exp {:>5})", - self.exports.len()); + self.exports.read().unwrap().len()); write!(f, "(dll {BOLD}{UNDERLINE}{:15}{RESET} [0x{:>08x}] (img 0x{:>08x} -> mem 0x{:>08x}) {deps} {links} {exports})", &self.name, self.code_size, @@ -72,7 +72,7 @@ impl Module { pub fn show_dependencies (&self) { for (name, module) in self.dependencies.iter() { print!(" ({module:?}"); - for (method, addr) in module.exports.iter() { + for (method, addr) in module.exports.read().unwrap().iter() { //print!("\n (0x{addr:08x} {method})") } println!(")");