diff --git a/crates/vestal/src/deps.rs b/crates/vestal/src/deps.rs deleted file mode 100644 index 324beeb..0000000 --- a/crates/vestal/src/deps.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::*; - -impl Dll { - - pub fn collect_deps (&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 = descriptor.get_name(pe.as_ref())?.as_str()?.to_lowercase(); - let imp = descriptor.get_imports(pe.as_ref())?; - let iat = descriptor.get_first_thunk(pe.as_ref())?; - let ilt = descriptor.get_original_first_thunk(pe.as_ref())?; - let lut = descriptor.get_lookup_thunks(pe.as_ref())?; - let unwrap_thunk = |thunk: &Thunk, name|match thunk { - Thunk::Thunk32(t) => panic!("32 bit {name}"), - Thunk::Thunk64(t) => t.0 - }; - let mut buffer = vec![]; - for (index, (import, thunk, orig, lookup)) in izip!( - imp, - iat.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "IAT thunk"))), - ilt.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "ILT (orig) thunk"))), - lut.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "lookup thunk"))), - ).enumerate() { - buffer.push((index, import, thunk, orig, lookup)); - } - for (index, import, thunk, orig, lookup) in buffer { - let (new_modules, new_methods) = self.collect_dep( - module_name.as_str(), - descriptor, - index, - import, - thunk, - orig, - lookup, - verbose - )?; - modules += new_modules; - methods += new_methods; - } - } - if verbose { - println!(" (deps-modules {modules})"); - println!(" (deps-methods {methods})"); - for (module, methods) in self.deps_by_library.iter() { - print!(" ({module}"); - for (method, addr) in methods.iter() { - print!("\n (0x{addr:08x} {method})") - } - println!(")"); - } - } - Ok((modules, methods)) - } - - pub fn collect_dep ( - &mut self, - module_name: &str, - descriptor: &ImageImportDescriptor, - index: usize, - import: ImportData, - thunk: String, - orig: String, - lookup: String, - verbose: bool, - ) -> Usually<(usize, usize)> { - let mut modules = 0; - let mut methods = 0; - let call_via = descriptor.first_thunk.0 + index as u32 * 8; - let method = match import { - ImportData::Ordinal(x) => format!("___VESTAL_ORDINAL_{x}"), - ImportData::ImportByName(name) => format!("{name}"), - }; - let module_name: Arc = module_name.clone().into(); - if !self.deps_by_library.contains_key(&module_name) { - self.deps_by_library.insert(module_name.clone(), Default::default()); - modules += 1; - } - let module = self.deps_by_library.get_mut(&module_name).unwrap(); - let method: Arc = method.clone().into(); - if module.contains_key(&method) { - panic!("duplicate method {method} in {module_name}"); - } - module.insert(method.clone(), call_via); - if self.deps_by_address.contains_key(&call_via) { - panic!("duplicate address {call_via} from {module_name}"); - } - self.deps_by_address.insert(call_via, (module_name.clone(), method.clone())); - methods += 1; - if verbose { - println!(" (import 0x{call_via:08x} {module_name}::{method})"); - } - Ok((modules, methods)) - } - -} diff --git a/crates/vestal/src/main.rs b/crates/vestal/src/main.rs index fbd12d4..5be0262 100644 --- a/crates/vestal/src/main.rs +++ b/crates/vestal/src/main.rs @@ -1,87 +1,105 @@ #![feature(slice_split_once)] mod util; pub(crate) use self::util::*; -mod deps; pub(crate) use self::deps::*; mod call; pub(crate) use self::call::*; mod show; pub(crate) use self::show::*; mod bang; +/// Takes a 64-bit VST2 in PE format and attempts to +/// relink it with Wine and a custom shim to make it +/// into a standalone, self-contained 64-bit ELF JACK device. fn main () -> Usually<()> { use clap::{arg, command, value_parser, ArgAction, Command}; - let matches = command!() - .arg(arg!([path] "Path to VST DLL").value_parser(value_parser!(PathBuf))) - //.arg(arg!(-s --stub "Provide a stub import").required(false)) - .arg(arg!(-i --inspect "Show info, don't run").required(false)) - .arg(arg!(-v --verbose "Show a lot of info").required(false)) - .get_matches(); - let path = matches.get_one::("path").unwrap_or_else(||panic!("pass path to VST DLL")); + // Parse command line arguments. + let matches = cli().get_matches(); + let path = matches.get_one::("path").unwrap_or_else(||panic!("pass path to VST DLL")); let verbose = *(matches.get_one::("verbose").unwrap_or(&false)); - let mut rebuilder = Vestal::new(&[ + // Construct a rebuilder. + let mut rebuilder = Vestal::new(verbose, &[ + // TODO allow user to specify search paths, system path, wineprefix... std::env::current_dir()?, canonicalize(path.clone().parent().expect("invalid parent path"))?, "/home/user/Lab/Cosmo/wineprefix/drive_c/windows/system32".into(), ]); - println!(); - for path in rebuilder.paths.iter() { - println!("(search {path:?})") - } - let path = rebuilder.find(path.to_str().expect("path must be unicode"), false)? - .unwrap_or_else(||panic!("Could not find: {path:?}")); - let name = rebuilder.load(&path, true, verbose)?; - let main = rebuilder.dlls.get(&name).unwrap(); - rebuilder.resolve_calls(&main, true, verbose)?; + // Resolve input path. + let path = path.to_str().expect("path must be unicode"); + let path = rebuilder.find(path)?.unwrap_or_else(||panic!("# not found: {path:?}")); + // Load with dependencies. + let main = rebuilder.load_recurse(&path)?; + rebuilder.resolve_recurse(&main)?; Ok(()) } +/// Define command line arguments. +fn cli () -> clap::Command { + use clap::{arg, command, value_parser, ArgAction, Command}; + command!() + .arg(arg!([path] "Path to VST DLL").value_parser(value_parser!(PathBuf))) + //.arg(arg!(-s --stub "Provide a stub import").required(false)) + .arg(arg!(-i --inspect "Show info, don't run").required(false)) + .arg(arg!(-v --verbose "Show a lot of info").required(false)) +} + +/// Application state #[derive(Debug, Default)] struct Vestal { + /// More detailed output. + verbose: bool, /// Search paths paths: BTreeSet>, - /// All DLLs in scope - dlls: BTreeMap, Arc>, /// Paths visited visited: BTreeSet>, + /// All DLLs in scope + dlls: BTreeMap, Arc>, } impl Vestal { - fn new (paths: &[impl AsRef]) -> Self { - Self { - paths: paths.iter().map(|x|Arc::new(x.as_ref().into())).collect(), - ..Default::default() + /// Construct a rebuilder + fn new (verbose: bool, paths: &[impl AsRef]) -> Self { + let paths: BTreeSet<_> = paths.iter().map(|x|Arc::new(x.as_ref().into())).collect(); + if verbose { + // Report search paths + for path in paths.iter() { + println!("(search {path:?})") + } } + Self { verbose, paths, ..Default::default() } } - fn load (&mut self, path: &impl AsRef, recurse: bool, verbose: bool) -> Usually> { + /// Load all dependencies recursively, starting from the given path. + fn load_recurse (&mut self, path: &impl AsRef) -> Usually> { + let dll = self.load(path)?; + for dep in dll.deps_by_library.keys() { + let dep_path = self.find(dep)?.unwrap_or_else(||panic!("not found: {dep:?}")); + if !self.visited.contains(&dep_path) { + self.load_recurse(&dep_path)?; + } + } + Ok(dll) + } + fn load (&mut self, path: &impl AsRef) -> Usually> { let path: Arc = Arc::from(PathBuf::from(path.as_ref())); if self.visited.contains(&path) { let name = path.file_name().expect("no file name"); let name: Arc = name.to_str().map(Arc::from).expect("non-unicode filename"); - return Ok(name) + return Ok(self.dlls.get(&name).unwrap().clone()) } - if verbose { + if self.verbose { println!("(load {path:?})"); } self.visited.insert(path.clone()); - let dll = Arc::new(Dll::new(&Arc::new(PathBuf::from(path.as_ref())), verbose)?); + let dll = Arc::new(Dll::new(&Arc::new(PathBuf::from(path.as_ref())), self.verbose)?); self.dlls.insert(dll.name.clone(), dll.clone()); - if recurse { - for dep in dll.deps_by_library.keys() { - if let Some(dep) = self.find(dep, verbose)? { - self.load(&dep, recurse, verbose)?; - } else { - panic!("not found: {dep:?}"); - } - } - } - Ok(dll.name.clone()) + Ok(dll) } - fn find (&self, name: &str, verbose: bool) -> Usually> { + + fn find (&self, name: &str) -> Usually> { for base in self.paths.iter() { let mut path = base.as_ref().clone(); path.push(name.to_lowercase()); - if verbose { + if self.verbose { println!("# looking for {name} at {path:?}"); } if std::fs::exists(&path)? { - if verbose { + if self.verbose { println!("# found {name} at {path:?}"); } @@ -90,67 +108,110 @@ impl Vestal { } Ok(None) } - fn resolve_calls (&self, dll: &Dll, recurse: bool, verbose: bool) -> Usually<()> { + + fn resolve (&self, dll: &Dll) -> Usually<()> { println!("{:11}{BOLD}{}{RESET}", "", dll.name); for (addr, call) in dll.calls_by_source.iter() { - self.resolve_call(dll, *addr, call, recurse, verbose)?; + self.resolve_call(dll, *addr, call)?; } Ok(()) } - fn resolve_call ( - &self, - dll: &Dll, - addr: u32, - call: &Arc, - recurse: bool, - verbose: bool, - ) -> Usually<()> { + + fn resolve_recurse (&self, dll: &Dll) -> Usually<()> { + println!("{:11}{BOLD}{}{RESET}", "", dll.name); + for (addr, call) in dll.calls_by_source.iter() { + self.resolve_call_recurse(dll, *addr, call)?; + } + Ok(()) + } + + fn resolve_call (&self, dll: &Dll, addr: u32, call: &Arc) -> Usually<()> { let addr = (addr - dll.code_base) as usize; dll.show_call_site(addr, call.length, 1); - Show::call_module_method(Some(call)); + call.show_call(&dll.name); if let Some(method) = dll.parse_call(call) { let module_name = call.module.as_ref().unwrap(); let method_name = call.method.as_ref().unwrap(); - let path = self.find(module_name, false)? - .unwrap_or_else(||panic!("# not found: {module_name}")); - let name = path.file_name() - .expect("no file name"); - let name: Arc = name.to_str().map(Arc::from) - .expect("non-unicode filename"); - let dll = self.dlls.get(&name) - .unwrap_or_else(||panic!("# not found: {name}")); - match dll.exports.get(method_name) { - Some(ThunkData::Function(rva)) => { - let addr = (rva.0 - dll.code_base) as usize; - if recurse { - self.resolve_call_recurse(dll, addr, module_name, method_name)?; + let path = self.find(module_name)?.unwrap_or_else(||panic!("# not found: {module_name}")); + let name = path.file_name().expect("no file name"); + let name: Arc = name.to_str().map(Arc::from).expect("non-unicode filename"); + let dll = self.dlls.get(&name).unwrap_or_else(||panic!("# not found: {name}")); + } + Ok(()) + } + + fn resolve_call_recurse (&self, dll: &Dll, addr: u32, call: &Arc) -> Usually<()> { + let addr = (addr - dll.code_base) as usize; + dll.show_call_site(addr, call.length, 1); + call.show_call(&dll.name); + if let Some(method) = dll.parse_call(call) { + let module_name = call.module.as_ref().unwrap(); + let method_name = call.method.as_ref().unwrap(); + let path = self.find(module_name)?.unwrap_or_else(||panic!("# not found: {module_name}")); + let name = path.file_name().expect("no file name"); + let name: Arc = name.to_str().map(Arc::from).expect("non-unicode filename"); + let dll = self.dlls.get(&name).unwrap_or_else(||panic!("# not found: {name}")); + let export = dll.exports.get(method_name); + if let Some(ThunkData::Function(rva)) = export { + self.resolve_call_recurse_into( + //dll, + //(rva.0 - dll.code_base) as usize, + module_name, + method_name, + )?; + } else if let Some(ThunkData::ForwarderString(rva)) = export { + let mut addr = (rva.0 - dll.code_base) as usize; + let mut forward = vec![]; + while let Some(c) = dll.pe.as_slice().get(addr) { + if *c == 0x00 { + break } - }, - None => panic!("# not found: {method_name}"), - thunk => panic!("# unsupported {thunk:?}"), + forward.push(*c); + addr += 1; + } + let forward = String::from_utf8(forward)?; + if let Some((module, method)) = forward.as_str().split_once(".") { + self.resolve_call_recurse_into( + //dll, + //addr, + &format!("{}.dll", module.to_lowercase()).into(), + &method.into(), + )?; + } else { + panic!("# invalid forward {}", &forward); + } + } else if let Some(thunk) = export { + panic!("# unsupported {thunk:?}"); + } else { + panic!("# not found: {method_name}"); } } Ok(()) } - fn resolve_call_recurse ( + + fn resolve_call_recurse_into ( &self, - dll: &Dll, - address: usize, + //dll: &Dll, + //address: usize, module_name: &Arc, method_name: &Arc, ) -> Usually<()> { - let mut decoder = Decoder::with_ip(64, &dll.text_section[address..], 0, DecoderOptions::NONE); - while decoder.can_decode() { - let position = decoder.position(); - if let Some(call) = dll.collect_call(&mut decoder, true)? { - println!("(call {call:#?}"); - } - if dll.text_section[address + position] == 0xc3 { - break - } - } + let module = self.dlls.get(module_name).unwrap_or_else(||panic!("# no dll {module_name}")); + let method = module.exports.get(method_name).unwrap_or_else(||panic!("# no export {method_name}")); + //println!("{} {} {:?}", module_name, method_name, method); + //let mut decoder = Decoder::with_ip(64, &dll.text_section[address..], 0, DecoderOptions::NONE); + //while decoder.can_decode() { + //let position = decoder.position(); + //if let Some(call) = dll.collect_call(&mut decoder, true)? { + //println!("(call {call:#?}"); + //} + //if dll.text_section[address + position] == 0xc3 { + //break + //} + //} Ok(()) } + } struct Dll { @@ -182,15 +243,166 @@ struct Dll { exports: BTreeMap, ThunkData>, } +impl Dll { + + fn new (path: &Arc, verbose: bool) -> Usually { + if verbose { + println!("\n(load {BOLD}{path:?}{RESET})"); + } + let (name, pe, data, bang) = Self::read_pe(path)?; + let (text_section, text_section_start, text_section_size) = Self::read_text_section(&pe)?; + let mut dll = Self { + bang, + name: name.clone(), + path: path.clone(), + text_section, + text_section_start, + text_section_size, + code_base: match pe.get_valid_nt_headers()? { + NTHeaders::NTHeaders32(h32) => panic!("32 bit headers"), + NTHeaders::NTHeaders64(h64) => h64.optional_header.base_of_code.0, + }, + pe, + exports: Default::default(), + deps_by_library: Default::default(), + deps_by_address: Default::default(), + calls_by_source: Default::default(), + calls_by_target: Default::default(), + }; + let (_modules_count, _methods_count) = dll.collect_deps(verbose)?; + let _calls = dll.collect_calls(verbose)?; + let _exports = dll.collect_exports(verbose)?; + println!("{dll:?}"); + Ok(dll) + } + + fn read_pe (path: &Arc) -> Usually<(Arc, Arc, Arc<[u8]>, Arc<[u8]>)> { + let name = path.as_ref().file_name().expect("no file name"); + let name: Arc = name.to_str().map(Arc::from).expect("non-unicode filename"); + let (bang, data) = crate::bang::slice_shebang(read(path.as_path())?.as_slice()); + let pe = Arc::new(VecPE::from_disk_data(data.clone())); + Ok((name, pe, data, bang)) + } + + fn read_text_section (pe: &VecPE) -> Usually<(Arc<[u8]>, usize, usize)> { + let code = pe.get_section_by_name(".text")?; + let start = code.pointer_to_raw_data.0 as usize; + let size = code.size_of_raw_data as usize; + let text = &pe.as_slice()[start..start+size]; + Ok((text.into(), start, size)) + } + + fn collect_exports (&mut self, _verbose: bool) -> 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()) + } + + pub fn collect_deps (&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; + let unwrap_thunk = |thunk: &Thunk, name|match thunk { + Thunk::Thunk32(t) => panic!("32 bit {name}"), + Thunk::Thunk64(t) => t.0 + }; + for descriptor in directory.descriptors.iter() { + let name = descriptor.get_name(pe.as_ref())?.as_str()?.to_lowercase(); + let imp = descriptor.get_imports(pe.as_ref())?; + let iat = descriptor.get_first_thunk(pe.as_ref())?; + let ilt = descriptor.get_original_first_thunk(pe.as_ref())?; + let lut = descriptor.get_lookup_thunks(pe.as_ref())?; + let mut buffer = vec![]; + for (index, (import, thunk, orig, lookup)) in izip!( + imp, + iat.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "IAT thunk"))), + ilt.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "ILT (orig) thunk"))), + lut.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "lookup thunk"))), + ).enumerate() { + buffer.push((index, import, thunk, orig, lookup)); + } + for (index, import, thunk, orig, lookup) in buffer { + let (new_modules, new_methods) = self.collect_dep( + name.as_str(), + descriptor, + index, + import, + thunk, + orig, + lookup, + verbose + )?; + modules += new_modules; + methods += new_methods; + } + } + if verbose { + println!(" (deps-modules {modules})"); + println!(" (deps-methods {methods})"); + for (module, methods) in self.deps_by_library.iter() { + print!(" ({module}"); + for (method, addr) in methods.iter() { + print!("\n (0x{addr:08x} {method})") + } + println!(")"); + } + } + Ok((modules, methods)) + } + + pub fn collect_dep ( + &mut self, + module_name: &str, + descriptor: &ImageImportDescriptor, + index: usize, + import: ImportData, + thunk: String, + orig: String, + lookup: String, + verbose: bool, + ) -> Usually<(usize, usize)> { + let mut modules = 0; + let mut methods = 0; + let call_via = descriptor.first_thunk.0 + index as u32 * 8; + let method = match import { + ImportData::Ordinal(x) => format!("___VESTAL_ORDINAL_{x}"), + ImportData::ImportByName(name) => format!("{name}"), + }; + let module_name: Arc = module_name.clone().into(); + if !self.deps_by_library.contains_key(&module_name) { + self.deps_by_library.insert(module_name.clone(), Default::default()); + modules += 1; + } + let module = self.deps_by_library.get_mut(&module_name).unwrap(); + let method: Arc = method.clone().into(); + if module.contains_key(&method) { + panic!("duplicate method {method} in {module_name}"); + } + module.insert(method.clone(), call_via); + if self.deps_by_address.contains_key(&call_via) { + panic!("duplicate address {call_via} from {module_name}"); + } + self.deps_by_address.insert(call_via, (module_name.clone(), method.clone())); + methods += 1; + if verbose { + println!(" (import 0x{call_via:08x} {module_name}::{method})"); + } + Ok((modules, methods)) + } + +} + impl std::fmt::Debug for Dll { fn fmt (&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> { - let deps = format!("deps (lib {}) (addr {:3})", + let deps = format!("(deps lib {:>4} addr {:>4})", self.deps_by_library.len(), self.deps_by_address.len()); - let calls = format!("calls (src {:4}) (tgt {:4})", + let calls = format!("(calls src {:>4} tgt {:>4})", self.calls_by_source.len(), self.calls_by_target.len()); - let exports = format!("exp {}", + let exports = format!("(exp {:>5})", self.exports.len()); write!(f, "(dll {BOLD}{UNDERLINE}{:15}{RESET} [0x{:>08x}] (img 0x{:>08x} -> mem 0x{:>08x}) {deps} {calls} {exports})", &self.name, @@ -199,52 +411,3 @@ impl std::fmt::Debug for Dll { self.code_base) } } - -impl Dll { - fn new (path: &Arc, verbose: bool) -> Usually { - if verbose { - println!("\n(load {BOLD}{path:?}{RESET})"); - } - let (name, pe, data, bang) = Self::read_pe(path)?; - let code = pe.get_section_by_name(".text")?; - let start = code.pointer_to_raw_data.0 as usize; - let size = code.size_of_raw_data as usize; - let text = &data[start..start+size]; - let mut dll = Self { - bang, - name: name.clone(), - path: path.clone(), - exports: Default::default(), - deps_by_library: Default::default(), - deps_by_address: Default::default(), - calls_by_source: Default::default(), - calls_by_target: Default::default(), - text_section: Arc::from(text), - text_section_start: start, - text_section_size: size, - code_base: match pe.get_valid_nt_headers()? { - NTHeaders::NTHeaders32(h32) => panic!("32 bit headers"), - NTHeaders::NTHeaders64(h64) => h64.optional_header.base_of_code.0, - }, - pe, - }; - let (_modules_count, _methods_count) = dll.collect_deps(verbose)?; - let _calls = dll.collect_calls(verbose)?; - println!("{dll:?}"); - let _exports = dll.collect_exports(verbose)?; - Ok(dll) - } - fn read_pe (path: &Arc) -> Usually<(Arc, Arc, Arc<[u8]>, Arc<[u8]>)> { - let name = path.as_ref().file_name().expect("no file name"); - let name: Arc = name.to_str().map(Arc::from).expect("non-unicode filename"); - let (bang, data) = crate::bang::slice_shebang(read(path.as_path())?.as_slice()); - let pe = Arc::new(VecPE::from_disk_data(data.clone())); - Ok((name, pe, data, bang)) - } - fn collect_exports (&mut self, _verbose: bool) -> Usually { - let directory = ImageExportDirectory::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()) - } -} diff --git a/crates/vestal/src/show.rs b/crates/vestal/src/show.rs index fc13679..a8330fa 100644 --- a/crates/vestal/src/show.rs +++ b/crates/vestal/src/show.rs @@ -61,7 +61,22 @@ impl Dll { write!(&mut output, " \n"); } } - print!("{output}"); + print!("\n{output}"); + } +} + +impl Call { + pub fn show_call (&self, name: &Arc) { + let module = self.module.as_ref(); + let method = self.method.as_ref(); + if module.is_some() || method.is_some() { + println!("╰--------> {name} -> {GREEN}{}{}{}{RESET}", + module.map(|x|x.as_ref()).unwrap_or(&""), + if module.is_some() && method.is_some() { "::" } else { "" }, + method.map(|x|x.as_ref()).unwrap_or(&"")); + } else { + println!("╰--------> {name} -> {RED}(unresolved){RESET} {self:?}"); + } } } @@ -78,18 +93,6 @@ impl Show { call.map(|call|format!(" (target {})", Show::num(call.target as usize))) .unwrap_or(String::new())); } - pub fn call_module_method (call: Option<&Arc>) { - let module = call.map(|call|call.module.as_ref()).flatten(); - let method = call.map(|call|call.method.as_ref()).flatten(); - if module.is_some() || method.is_some() { - println!("╰--------> {GREEN}{}{}{}{RESET}", - module.map(|x|x.as_ref()).unwrap_or(&""), - if module.is_some() && method.is_some() { "::" } else { "" }, - method.map(|x|x.as_ref()).unwrap_or(&"")); - } else { - println!("╰--------> {RED}(unresolved){RESET} {call:?}"); - } - } pub fn call_dasm (bytes: &[u8], rip: usize) -> Arc { let mut decoder = Decoder::with_ip(64, bytes, 0x1000 + rip as u64, DecoderOptions::NONE); while decoder.can_decode() {