use rane::ffi::*;
use std::ffi::{c_void, CStr, CString};
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== ANEC IR path deep probe ===\n");
for name in &["AppleNeuralEngine", "ANECompiler", "ANEServices"] {
let path = format!("/System/Library/PrivateFrameworks/{name}.framework/{name}");
let c = CString::new(path).unwrap();
unsafe {
dlopen(c.as_ptr(), RTLD_NOW);
}
}
println!("=== 1. kANEF* string constants ===");
let const_names = [
"kANEFModelANECIRValue",
"kANEFModelCoreMLValue",
"kANEFModelMILValue",
"kANEFModelMLIRValue",
"kANEFModelLLIRBundleValue",
"kANEFModelPreCompiledValue",
"kANEFIsInMemoryModelTypeKey",
"kANEFInMemoryModelIsCachedKey",
"kANEFEspressoFileResourcesKey",
"kANEFModelDescriptionKey",
"kANEFModelIdentityStrKey",
"kANEFModelHasCacheURLIdentifierKey",
"kANEFModelCacheIdentifierUsingSourceURLKey",
"kANEFModelInstanceParameters",
"kANEFModelIsEncryptedKey",
"kANEFBaseModelIdentifierKey",
"kANEFKeepModelMemoryWiredKey",
"kANEFModelInput16KAlignmentArrayKey",
"kANEFModelInputSymbolIndexArrayKey",
"kANEFModelInputSymbolsArrayKey",
"kANEFModelOutputSymbolIndexArrayKey",
"kANEFModelOutputSymbolsArrayKey",
"kANEFModelOutput16KAlignmentArrayKey",
"kANEFModelLoadPerformanceStatsKey",
"kANEFModelType",
];
unsafe {
for name in &const_names {
let c = CString::new(*name).unwrap();
let p = dlsym(std::ptr::null_mut(), c.as_ptr()) as *const ObjcId;
if !p.is_null() && !(*p).is_null() {
println!(" {name} = \"{}\"", nsstring_to_str(*p));
} else {
println!(" {name} = NOT FOUND");
}
}
}
let ic = 16usize;
let oc = 16usize;
let data_off: u64 = 64;
let qdata_size = ic * oc;
let scale_size = ic * 2;
let total = 64 + qdata_size + scale_size + ic;
let mut wblob = vec![0u8; total];
for b in &mut wblob[64..64 + qdata_size] {
*b = 1;
}
for i in 0..ic {
let off = 64 + qdata_size + i * 2;
wblob[off + 1] = 0x3C;
}
let scale_off = data_off + qdata_size as u64;
let zp_off = scale_off + scale_size as u64;
let mil_text = format!(
concat!(
"program(1, 0)\nfunc main<ios16>(tensor<fp16, [1, {ic}, 1, 1]> x)",
" -> (tensor<fp16, [1, {oc}, 1, 1]>) {{\n block0() {{\n",
" tensor<fp16, [{ic},{oc}]> wf = constexpr_affine_dequantize()",
"[axis = int32(0),",
" quantized_data = tensor<int8, [{ic},{oc}]>(BLOBFILE(path = string(\"@model_path/weights/weights.bin\"), offset = uint64({data_off}))),",
" scale = tensor<fp16, [{ic}]>(BLOBFILE(path = string(\"@model_path/weights/weights.bin\"), offset = uint64({scale_off}))),",
" zero_point = tensor<int8, [{ic}]>(BLOBFILE(path = string(\"@model_path/weights/weights.bin\"), offset = uint64({zp_off})))];\n",
" tensor<fp16, [1, {oc}, 1, 1]> y = linear()[alpha = fp32(1), beta = fp32(0), weight = wf, x = x];\n",
" }} -> (y)\n}}\n"
),
ic = ic, oc = oc, data_off = data_off, scale_off = scale_off, zp_off = zp_off
);
unsafe {
type DataFn = unsafe extern "C" fn(ObjcId, ObjcSel, *const u8, u64) -> ObjcId;
type DictFn = unsafe extern "C" fn(ObjcId, ObjcSel) -> ObjcId;
type DescFn3 = unsafe extern "C" fn(ObjcId, ObjcSel, ObjcId, ObjcId, ObjcId) -> ObjcId;
type ModelFn = unsafe extern "C" fn(ObjcId, ObjcSel, ObjcId) -> ObjcId;
type StrFn = unsafe extern "C" fn(ObjcId, ObjcSel) -> ObjcId;
type Utf8Fn = unsafe extern "C" fn(ObjcId, ObjcSel) -> *const std::ffi::c_char;
type ModelFn2 = unsafe extern "C" fn(ObjcId, ObjcSel, ObjcId, u8) -> ObjcId;
type CompileFn = unsafe extern "C" fn(ObjcId, ObjcSel, u32, ObjcId, *mut ObjcId) -> bool;
type DictMakeFn =
unsafe extern "C" fn(ObjcId, ObjcSel, *const ObjcId, *const ObjcId, u64) -> ObjcId;
let cls_data = cls("NSData");
let cls_dict = cls("NSDictionary");
let cls_desc = cls("_ANEInMemoryModelDescriptor");
let cls_model = cls("_ANEInMemoryModel");
let cls_str = cls("NSString");
let dataf: DataFn = std::mem::transmute(objc_msgSend as *const c_void);
let dictf: DictFn = std::mem::transmute(objc_msgSend as *const c_void);
let descf: DescFn3 = std::mem::transmute(objc_msgSend as *const c_void);
let modelf: ModelFn = std::mem::transmute(objc_msgSend as *const c_void);
let strf: StrFn = std::mem::transmute(objc_msgSend as *const c_void);
let utf8f: Utf8Fn = std::mem::transmute(objc_msgSend as *const c_void);
let modelf2: ModelFn2 = std::mem::transmute(objc_msgSend as *const c_void);
let compilef: CompileFn = std::mem::transmute(objc_msgSend as *const c_void);
let dkv: DictMakeFn = std::mem::transmute(objc_msgSend as *const c_void);
let bytes = mil_text.as_bytes();
let ns_text = dataf(
cls_data as ObjcId,
sel("dataWithBytes:length:"),
bytes.as_ptr(),
bytes.len() as u64,
);
let empty = dictf(cls_dict as ObjcId, sel("dictionary"));
let desc_base = descf(
cls_desc as ObjcId,
sel("modelWithMILText:weights:optionsPlist:"),
ns_text,
empty,
std::ptr::null_mut(),
);
let model_base = modelf(
cls_model as ObjcId,
sel("inMemoryModelWithDescriptor:"),
desc_base,
);
let hex_id = strf(model_base, sel("hexStringIdentifier"));
let hex_str = {
let c = utf8f(hex_id, sel("UTF8String"));
CStr::from_ptr(c).to_string_lossy().into_owned()
};
let tmp_dir = format!("/tmp/{hex_str}");
println!("\ntmp_dir: {tmp_dir}");
std::fs::create_dir_all(format!("{tmp_dir}/weights")).unwrap();
std::fs::write(format!("{tmp_dir}/model.mil"), &mil_text).unwrap();
std::fs::write(format!("{tmp_dir}/weights/weights.bin"), &wblob).unwrap();
println!("\n=== 2. compilerOptionsFileName ===");
let opts_fname = strf(model_base, sel("compilerOptionsFileName"));
println!(
" MIL model compilerOptionsFileName = {}",
nsstring_to_str(opts_fname)
);
let desc_flip = descf(
cls_desc as ObjcId,
sel("modelWithMILText:weights:optionsPlist:"),
ns_text,
empty,
std::ptr::null_mut(),
);
let p = (desc_flip as *mut u8).add(8);
*p = 0; let model_flip = modelf(
cls_model as ObjcId,
sel("inMemoryModelWithDescriptor:"),
desc_flip,
);
let opts_fname_flip = strf(model_flip, sel("compilerOptionsFileName"));
println!(
" ANECIR model compilerOptionsFileName = {}",
nsstring_to_str(opts_fname_flip)
);
let local_path = strf(model_base, sel("localModelPath"));
println!(" MIL localModelPath = {}", nsstring_to_str(local_path));
let local_path_flip = strf(model_flip, sel("localModelPath"));
println!(
" ANECIR localModelPath = {}",
nsstring_to_str(local_path_flip)
);
println!("\n=== 3. modelWithNetworkDescription: probes ===");
let make_nsstr = |s: &str| -> ObjcId {
let c = CString::new(s).unwrap();
type StrFn2 = unsafe extern "C" fn(ObjcId, ObjcSel, *const std::ffi::c_char) -> ObjcId;
let sf2: StrFn2 = std::mem::transmute(objc_msgSend as *const c_void);
sf2(cls_str as ObjcId, sel("stringWithUTF8String:"), c.as_ptr())
};
let make_dict1 = |k: ObjcId, v: ObjcId| -> ObjcId {
let keys = [k];
let vals = [v];
dkv(
cls_dict as ObjcId,
sel("dictionaryWithObjects:forKeys:count:"),
vals.as_ptr(),
keys.as_ptr(),
1u64,
)
};
let candidate_keys = [
"NetworkSourceFileName",
"NetworkSourcePath",
"kANEFModelDescriptionKey",
];
for key_name in &candidate_keys {
let k = make_nsstr(key_name);
let v = make_nsstr("model.espresso.net");
let nd = make_dict1(k, v);
let desc_nd = descf(
cls_desc as ObjcId,
sel("modelWithNetworkDescription:weights:optionsPlist:"),
nd,
empty,
std::ptr::null_mut(),
);
if desc_nd.is_null() {
println!(" key={key_name}: desc=null");
continue;
}
let is_mil = *(desc_nd as *const u8).add(8);
let hex = strf(desc_nd, sel("hexStringIdentifier"));
let hex_s = nsstring_to_str(hex);
println!(" key={key_name}: desc={desc_nd:p} isMILModel={is_mil} hex={hex_s}");
let model_nd = modelf(
cls_model as ObjcId,
sel("inMemoryModelWithDescriptor:"),
desc_nd,
);
if model_nd.is_null() {
println!(" model=null");
continue;
}
let opts = modelf2(
model_nd,
sel("compilerOptionsWithOptions:isCompiledModelCached:"),
empty,
0,
);
println!(" opts = {}", objc_desc(opts));
let hex_nd = strf(model_nd, sel("hexStringIdentifier"));
let hex_nd_s = nsstring_to_str(hex_nd);
let nd_dir = format!("/tmp/{hex_nd_s}");
std::fs::create_dir_all(format!("{nd_dir}/weights")).ok();
std::fs::write(format!("{nd_dir}/model.espresso.net"), b"{}").ok();
std::fs::write(format!("{nd_dir}/weights/weights.bin"), &wblob).ok();
let mut err: ObjcId = std::ptr::null_mut();
let ok = compilef(
model_nd,
sel("compileWithQoS:options:error:"),
21,
empty,
&mut err,
);
if ok {
println!(" *** COMPILE SUCCESS! ***");
} else {
let e = nserror_string(err).unwrap_or_default();
println!(" error: {}", &e[..e.len().min(300)]);
}
std::fs::remove_dir_all(&nd_dir).ok();
}
println!("\n=== 4. kANEFEspressoFileResourcesKey probe ===");
let espc = CString::new("kANEFEspressoFileResourcesKey").unwrap();
let esp_ptr = dlsym(std::ptr::null_mut(), espc.as_ptr()) as *const ObjcId;
if !esp_ptr.is_null() && !(*esp_ptr).is_null() {
let esp_key = *esp_ptr;
println!(
" kANEFEspressoFileResourcesKey = \"{}\"",
nsstring_to_str(esp_key)
);
let v_espresso = make_nsstr("model.espresso.net");
let nd = make_dict1(esp_key, v_espresso);
let desc_esp = descf(
cls_desc as ObjcId,
sel("modelWithNetworkDescription:weights:optionsPlist:"),
nd,
empty,
std::ptr::null_mut(),
);
if desc_esp.is_null() {
println!(" espressoKey desc=null");
} else {
let is_mil = *(desc_esp as *const u8).add(8);
println!(" espressoKey desc={desc_esp:p} isMILModel={is_mil}");
let model_esp = modelf(
cls_model as ObjcId,
sel("inMemoryModelWithDescriptor:"),
desc_esp,
);
if !model_esp.is_null() {
let opts = modelf2(
model_esp,
sel("compilerOptionsWithOptions:isCompiledModelCached:"),
empty,
0,
);
println!(" opts = {}", objc_desc(opts));
}
}
} else {
println!(" kANEFEspressoFileResourcesKey: not in main process โ checking via ANEServices dlsym");
let svc_path =
CString::new("/System/Library/PrivateFrameworks/ANEServices.framework/ANEServices")
.unwrap();
dlopen(svc_path.as_ptr(), RTLD_NOW);
let p2 = dlsym(std::ptr::null_mut(), espc.as_ptr()) as *const ObjcId;
if !p2.is_null() && !(*p2).is_null() {
println!(
" found after ANEServices load: \"{}\"",
nsstring_to_str(*p2)
);
} else {
println!(" still not found");
}
}
println!("\n=== 5. Write model.espresso.net to flipped model dir ===");
std::fs::write(format!("{tmp_dir}/model.espresso.net"), b"{}").ok();
println!("\n Testing NSData as networkDescription...");
type DescFnData = unsafe extern "C" fn(ObjcId, ObjcSel, ObjcId, ObjcId, ObjcId) -> ObjcId;
let descfd: DescFnData = std::mem::transmute(objc_msgSend as *const c_void);
let dummy_data = dataf(
cls_data as ObjcId,
sel("dataWithBytes:length:"),
b"{}".as_ptr(),
2u64,
);
let desc_data = descfd(
cls_desc as ObjcId,
sel("modelWithNetworkDescription:weights:optionsPlist:"),
dummy_data,
empty,
std::ptr::null_mut(),
);
if desc_data.is_null() {
println!(" NSData desc=null (expected โ it crashes or returns nil)");
} else {
let is_mil = *(desc_data as *const u8).add(8);
println!(" NSData desc={desc_data:p} isMILModel={is_mil}");
let model_data = modelf(
cls_model as ObjcId,
sel("inMemoryModelWithDescriptor:"),
desc_data,
);
if !model_data.is_null() {
let opts = modelf2(
model_data,
sel("compilerOptionsWithOptions:isCompiledModelCached:"),
empty,
0,
);
println!(" opts = {}", objc_desc(opts));
}
}
}
Ok(())
}
unsafe fn nsstring_to_str(obj: ObjcId) -> String {
if obj.is_null() {
return "(null)".to_string();
}
type Utf8Fn = unsafe extern "C" fn(ObjcId, ObjcSel) -> *const std::ffi::c_char;
let uf: Utf8Fn = std::mem::transmute(objc_msgSend as *const c_void);
let p = uf(obj, sel("UTF8String"));
if p.is_null() {
return "(null utf8)".to_string();
}
CStr::from_ptr(p).to_string_lossy().into_owned()
}
unsafe fn objc_desc(obj: ObjcId) -> String {
if obj.is_null() {
return "(null)".into();
}
type StrFn = unsafe extern "C" fn(ObjcId, ObjcSel) -> ObjcId;
type Utf8Fn = unsafe extern "C" fn(ObjcId, ObjcSel) -> *const std::ffi::c_char;
let sf: StrFn = std::mem::transmute(objc_msgSend as *const c_void);
let uf: Utf8Fn = std::mem::transmute(objc_msgSend as *const c_void);
let d = sf(obj, sel("description"));
if d.is_null() {
return "(no desc)".into();
}
let c = uf(d, sel("UTF8String"));
if c.is_null() {
return "(null utf8)".into();
}
CStr::from_ptr(c).to_string_lossy().into_owned()
}