Last active
August 28, 2017 12:12
-
-
Save phrohdoh/3728e825f92f6ee2660073ca4af0bc39 to your computer and use it in GitHub Desktop.
Run a Mono/CIL executable via Rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
authors = ["Taryn Hill <[email protected]>"] | |
name = "libdotnet" | |
version = "0.1.0" | |
[dependencies] | |
libc = "0.2.29" | |
libloading = "0.4.0" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::ffi::{CString, NulError}; | |
extern crate libloading as lib; | |
extern crate libc; | |
use libc::{c_char, c_int}; | |
const MONO_FRAMEWORK_LIB: &'static str = "/Library/Frameworks/Mono.framework/Versions/Current/lib/"; | |
const MONO_FRAMEWORK_ETC: &'static str = "/Library/Frameworks/Mono.framework/Versions/Current/etc/"; | |
const LIBMONO_DYLIB_PATH: &'static str = "/Library/Frameworks/Mono.framework/Versions/Current/lib/libmono-2.0.dylib"; | |
const CIL_ASM_PATH: &'static str = "/Users/me/Desktop/test.exe"; | |
mod raw { | |
#[repr(u8)] | |
pub enum MonoDomain { __variant1, __variant2, } | |
#[repr(u8)] | |
pub enum MonoAssembly { __variant1, __variant2, } | |
} | |
#[allow(non_camel_case_types)] | |
type t_mono_set_dirs = fn(*const c_char, *const c_char); | |
#[allow(non_camel_case_types)] | |
type t_mono_jit_init = fn(*const c_char) -> *mut raw::MonoDomain; | |
#[allow(non_camel_case_types)] | |
type t_mono_domain_assembly_open = fn(*mut raw::MonoDomain, *const c_char) -> *mut raw::MonoAssembly; | |
#[allow(non_camel_case_types)] | |
type t_mono_jit_exec = fn(*mut raw::MonoDomain, *mut raw::MonoAssembly, c_int, *const *const c_char) -> c_int; | |
struct MonoDomain { raw: *mut raw::MonoDomain, } | |
struct MonoAssembly { raw: *mut raw::MonoAssembly, } | |
fn get_sym<'lib, FnT>(lib: &'lib lib::Library, name: &str) -> lib::Symbol<'lib, FnT> { | |
unsafe { lib.get(name.as_bytes()) }.expect(&format!("Failed to find sym {}", name)) | |
} | |
#[derive(Debug)] | |
enum MyError { | |
DataContainsNulByte, | |
FailedToCreateDomain, | |
FailedToOpenAssembly, | |
} | |
impl From<NulError> for MyError { | |
fn from(e: NulError) -> Self { | |
MyError::DataContainsNulByte | |
} | |
} | |
fn mono_set_dirs<'sym>(c_fn: &lib::Symbol<'sym, t_mono_set_dirs>, lib_path: &str, etc_path: &str) -> Result<(), MyError> { | |
let lib_path = CString::new(lib_path)?; | |
let etc_path = CString::new(etc_path)?; | |
c_fn(lib_path.as_ptr(), etc_path.as_ptr()); | |
Ok(()) | |
} | |
fn mono_jit_init<'sym>(c_fn: &lib::Symbol<'sym, t_mono_jit_init>, domain_name: &str) -> Result<MonoDomain, MyError> { | |
let domain_name = CString::new(domain_name)?; | |
let ptr_domain = c_fn(domain_name.as_ptr()); | |
if ptr_domain.is_null() { | |
return Err(MyError::FailedToCreateDomain); | |
} | |
Ok(MonoDomain { | |
raw: ptr_domain, | |
}) | |
} | |
fn mono_domain_assembly_open<'sym>(c_fn: &lib::Symbol<'sym, t_mono_domain_assembly_open>, domain: &MonoDomain, assembly_path: &str) -> Result<MonoAssembly, MyError> { | |
let assembly_path = CString::new(assembly_path)?; | |
let ptr_assembly = c_fn(domain.raw, assembly_path.as_ptr()); | |
if ptr_assembly.is_null() { | |
return Err(MyError::FailedToOpenAssembly); | |
} | |
Ok(MonoAssembly { | |
raw: ptr_assembly, | |
}) | |
} | |
fn mono_jit_exec<'sym>(c_fn: &lib::Symbol<'sym, t_mono_jit_exec>, domain: &MonoDomain, assembly: &MonoAssembly, args: &[&str]) -> Result<i32, MyError> { | |
let c_args = args.iter().map(|arg| CString::new(*arg).unwrap()).collect::<Vec<_>>(); | |
let c_arg_ptrs = c_args.iter().map(|arg| arg.as_ptr()).collect::<Vec<_>>(); | |
let c_ret_val = c_fn(domain.raw, assembly.raw, c_arg_ptrs.len() as c_int, c_arg_ptrs.as_ptr()); | |
Ok(c_ret_val as i32) | |
} | |
fn main() { | |
let lib_mono = lib::Library::new(LIBMONO_DYLIB_PATH).expect(&format!("Failed to load {}", LIBMONO_DYLIB_PATH)); | |
let sym_mono_set_dirs = get_sym::<t_mono_set_dirs>(&lib_mono, "mono_set_dirs"); | |
mono_set_dirs(&sym_mono_set_dirs, MONO_FRAMEWORK_LIB, MONO_FRAMEWORK_ETC).expect("Failed to set dirs!"); | |
let sym_mono_jit_init = get_sym::<t_mono_jit_init>(&lib_mono, "mono_jit_init"); | |
let domain = mono_jit_init(&sym_mono_jit_init, "DomainFromRust").expect("Failed to create domain!"); | |
let sym_mono_domain_assembly_open = get_sym::<t_mono_domain_assembly_open>(&lib_mono, "mono_domain_assembly_open"); | |
let assembly = mono_domain_assembly_open(&sym_mono_domain_assembly_open, &domain, CIL_ASM_PATH).expect(&format!("Failed to load {}!", CIL_ASM_PATH)); | |
let sym_mono_jit_exec = get_sym::<t_mono_jit_exec>(&lib_mono, stringify!(mono_jit_exec)); | |
let ret_val = mono_jit_exec(&sym_mono_jit_exec, &domain, &assembly, &[CIL_ASM_PATH, "This is a message from Rust passed to C#!"]).expect(&format!("Error while running {}'s Main method", CIL_ASM_PATH)); | |
println!("{}", ret_val); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment