Skip to content

Instantly share code, notes, and snippets.

@phrohdoh
Last active August 28, 2017 12:12
Show Gist options
  • Save phrohdoh/3728e825f92f6ee2660073ca4af0bc39 to your computer and use it in GitHub Desktop.
Save phrohdoh/3728e825f92f6ee2660073ca4af0bc39 to your computer and use it in GitHub Desktop.
Run a Mono/CIL executable via Rust
[package]
authors = ["Taryn Hill <[email protected]>"]
name = "libdotnet"
version = "0.1.0"
[dependencies]
libc = "0.2.29"
libloading = "0.4.0"
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