├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── coreclr.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "cc" 3 | version = "1.0.15" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | 6 | [[package]] 7 | name = "libloading" 8 | version = "0.5.0" 9 | source = "registry+https://github.com/rust-lang/crates.io-index" 10 | dependencies = [ 11 | "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", 12 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 13 | ] 14 | 15 | [[package]] 16 | name = "rustclr" 17 | version = "0.1.0" 18 | dependencies = [ 19 | "libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 20 | ] 21 | 22 | [[package]] 23 | name = "winapi" 24 | version = "0.3.4" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | dependencies = [ 27 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 28 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 29 | ] 30 | 31 | [[package]] 32 | name = "winapi-i686-pc-windows-gnu" 33 | version = "0.4.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | 36 | [[package]] 37 | name = "winapi-x86_64-pc-windows-gnu" 38 | version = "0.4.0" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | 41 | [metadata] 42 | "checksum cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0ebb87d1116151416c0cf66a0e3fb6430cccd120fd6300794b4dfaa050ac40ba" 43 | "checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2" 44 | "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" 45 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 46 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 47 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustclr" 3 | version = "0.1.0" 4 | authors = ["Kevin Jones "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | libloading = "0.5" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Kevin Jones 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rust CoreCLR 2 | ============ 3 | 4 | 5 | This is a sample project for demonstrating how to host the .NET Core CLR into a process 6 | with Rust. -------------------------------------------------------------------------------- /src/coreclr.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashMap}; 2 | use std::ffi::{CString}; 3 | use std::{mem, ptr}; 4 | use std::io::{Error, ErrorKind, Result}; 5 | use std::marker::{PhantomData}; 6 | use std::ops::{Deref}; 7 | use std::os::raw::{c_int, c_char, c_uint, c_void}; 8 | use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; 9 | use std::vec::{Vec}; 10 | 11 | type CoreCLRInitialize = extern "C" fn (*const c_char, *const c_char, c_int, *const *mut c_char, *const *mut c_char, *mut *mut c_void, *mut c_uint) -> c_int; 12 | type CoreCLRCreateDelegate = extern "C" fn(*mut c_void, c_uint, *const c_char, *const c_char, *const c_char, *mut *mut c_void) -> c_int; 13 | type CoreCLRShutdown = extern "C" fn(*mut c_void, c_uint) -> c_int; 14 | 15 | pub struct ManagedDelegate<'clr, T : 'clr> { 16 | managed_pointer : *mut c_void, 17 | phantom: PhantomData<&'clr T> 18 | } 19 | 20 | impl<'clr, T> Deref for ManagedDelegate<'clr, T> { 21 | type Target = T; 22 | fn deref(&self) -> &T { 23 | unsafe { mem::transmute(&self.managed_pointer) } 24 | } 25 | } 26 | 27 | #[derive(Debug)] 28 | pub struct CoreCLR { 29 | library: libloading::Library, 30 | domain_id : c_uint, 31 | host_handle : *mut c_void 32 | } 33 | 34 | impl Drop for CoreCLR { 35 | fn drop(&mut self) { 36 | // If we were able to successfully call coreclr_initialize, it's reasonable to assume we should be able to 37 | // shut it down. Panicking if the entry point can't be found is the best we can do since the trait doesn't 38 | // let us do anything else. 39 | let shutdown_func: libloading::Symbol = unsafe { self.library.get(b"coreclr_shutdown").unwrap() }; 40 | let result = shutdown_func(self.host_handle, self.domain_id); 41 | debug_assert!(result >= 0); 42 | } 43 | } 44 | 45 | impl CoreCLR { 46 | pub fn create_delegate<'clr, T: 'clr>(&'clr self, assembly_name : &str, type_name : &str, method_name : &str) -> Result> { 47 | let create_delegate: libloading::Symbol = unsafe { self.library.get(b"coreclr_create_delegate")? }; 48 | let assembly_name_ptr = CString::new(assembly_name)?; 49 | let type_name_ptr = CString::new(type_name)?; 50 | let method_name_ptr = CString::new(method_name)?; 51 | let mut delegate_handle = ptr::null_mut(); 52 | let result = create_delegate( 53 | self.host_handle, 54 | self.domain_id, 55 | assembly_name_ptr.as_ptr(), 56 | type_name_ptr.as_ptr(), 57 | method_name_ptr.as_ptr(), 58 | &mut delegate_handle 59 | ); 60 | if result < 0 { 61 | Err(Error::from_raw_os_error(result)) 62 | } 63 | else { 64 | Ok(ManagedDelegate { managed_pointer: delegate_handle, phantom: PhantomData }) 65 | } 66 | } 67 | 68 | pub fn new(library_path : &str, exe_path : &str, app_domain_friendly_name : &str, properties : &HashMap<&str, &str>) -> Result { 69 | static INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT; 70 | if INITIALIZED.compare_and_swap(false, true, Ordering::Relaxed) { 71 | return Err(Error::new(ErrorKind::Other, "The CLR cannot be loaded more than once. See dotnet/coreclr#12266.")); 72 | } 73 | let library = libloading::Library::new(library_path)?; 74 | let exe_path_ptr = CString::new(exe_path)?; 75 | let app_domain_friendly_name_ptr = CString::new(app_domain_friendly_name)?; 76 | let mut keys = Vec::with_capacity(properties.len()); 77 | let mut values = Vec::with_capacity(properties.len()); 78 | for (&key, &value) in properties.iter() { 79 | keys.push(CString::new(key).unwrap().into_raw()); 80 | values.push(CString::new(value).unwrap().into_raw()); 81 | }; 82 | let mut host_handle = ptr::null_mut(); 83 | let mut domain_id = 0; 84 | let result = { 85 | let initializer: libloading::Symbol = unsafe { library.get(b"coreclr_initialize")? }; 86 | initializer( 87 | exe_path_ptr.as_ptr(), 88 | app_domain_friendly_name_ptr.as_ptr(), 89 | properties.len() as i32, 90 | keys.as_ptr(), 91 | values.as_ptr(), 92 | &mut host_handle, 93 | &mut domain_id 94 | ) 95 | }; 96 | 97 | // We need to call from_raw on our original C strings because into_raw 98 | // forgets them. If we don't, we leak the memory in keys and values. 99 | for key in keys.into_iter() { 100 | let _ = unsafe { CString::from_raw(key) }; 101 | }; 102 | for value in values.into_iter() { 103 | let _ = unsafe { CString::from_raw(value) }; 104 | }; 105 | if result < 0 { 106 | Err(Error::from_raw_os_error(result)) 107 | } 108 | else { 109 | Ok(CoreCLR { 110 | domain_id : domain_id, 111 | host_handle : host_handle, 112 | library : library 113 | }) 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod coreclr; 2 | 3 | use std::collections::{HashMap}; 4 | 5 | 6 | fn main() { 7 | 8 | let exe_path = ""; 9 | let app_domain = "rusty"; 10 | let assembly_name = "MyAssembly"; 11 | let type_name = "MyAssembly.MyType"; 12 | let entry_point = "MyEntryPointMethod"; 13 | let mut map = HashMap::new(); 14 | map.insert("APP_PATHS", exe_path); 15 | map.insert("TRUSTED_PLATFORM_ASSEMBLIES", ""); 16 | let coreclr = coreclr::CoreCLR::new("/the/path/to/libcoreclr.dylib", exe_path, app_domain, &map).unwrap(); 17 | let managed_function : coreclr::ManagedDelegate ()> = coreclr.create_delegate(assembly_name, type_name, entry_point).unwrap(); 18 | unsafe { managed_function() }; 19 | } 20 | --------------------------------------------------------------------------------