use std::collections::HashMap;
use std::path::Path;
use crate::emu::Emu;
use crate::loaders::macho::macho64::Macho64;
impl Emu {
pub fn load_macho64(&mut self, filename: &str) {
let mut macho = Macho64::parse(filename).expect("cannot parse macho64 binary");
macho.load(&mut self.maps);
if self.cfg.arch.is_aarch64() {
self.init_macos_aarch64();
} else if self.cfg.arch.is_x64() {
self.init_macos64();
} else {
panic!("unsupported Mach-O architecture: {:?}", self.cfg.arch);
}
self.set_pc(macho.entry);
log::info!("macho64: entry point set to 0x{:x}", macho.entry);
let libs = macho.get_libs();
log::info!("macho64: {} dependent dylibs: {:?}", libs.len(), libs);
let mut export_map: HashMap<String, u64> = HashMap::new();
for lib_path in &libs {
let lib_name = lib_path.rsplit('/').next().unwrap_or(lib_path);
let local_path = self.cfg.get_maps_folder(lib_name);
if Path::new(&local_path).exists() {
let (base, exports) = self.map_dylib_macho64(&local_path, lib_name);
log::info!(
"macho64: loaded dylib {} at 0x{:x} with {} exports",
lib_name,
base,
exports.len()
);
for (sym, addr) in exports {
macho.addr_to_symbol.insert(addr, sym.clone());
export_map.insert(sym, addr);
}
} else {
log::warn!(
"macho64: dylib not found: {} (looked at {})",
lib_name,
local_path
);
}
}
let (imports, binds) = macho.parse_chained_fixups();
for bind in &binds {
if let Some(imp) = imports.get(bind.import_ordinal as usize) {
if let Some(&resolved_addr) = export_map.get(&imp.name) {
log::info!(
"macho64: resolved {} -> 0x{:x} (GOT at 0x{:x})",
imp.name,
resolved_addr,
bind.got_vmaddr
);
self.maps.write_qword(bind.got_vmaddr, resolved_addr);
} else {
log::warn!(
"macho64: unresolved import {} (GOT at 0x{:x})",
imp.name,
bind.got_vmaddr
);
}
}
}
self.macho64 = Some(macho);
}
pub fn map_dylib_macho64(&mut self, filename: &str, lib_name: &str) -> (u64, Vec<(String, u64)>) {
use crate::loaders::macho::macho64::prot_to_permission;
let dylib = Macho64::parse(filename).expect("cannot parse dylib");
let total_size: u64 = dylib.segments.iter().map(|s| s.vmsize).sum();
let base = self
.maps
.lib64_alloc(total_size.max(0x4000))
.expect("cannot allocate space for dylib");
let base_name = lib_name.strip_suffix(".dylib").unwrap_or(lib_name);
for seg in &dylib.segments {
if seg.vmsize == 0 {
continue;
}
let perm = prot_to_permission(seg.initprot);
let seg_addr = base + seg.vmaddr; let map_name = format!("{}.{}", base_name, seg.name);
log::info!(
"macho64: mapping dylib segment '{}' at 0x{:x} size 0x{:x}",
map_name,
seg_addr,
seg.vmsize
);
let mem = self
.maps
.create_map(&map_name, seg_addr, seg.vmsize, perm)
.expect(&format!("cannot create map for dylib segment '{}'", map_name));
if !seg.data.is_empty() {
mem.force_write_bytes(seg_addr, &seg.data);
}
}
let exports: Vec<(String, u64)> = dylib
.get_exports()
.into_iter()
.map(|(name, offset)| (name, base + offset))
.collect();
(base, exports)
}
}