1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::ffi::{CStr, CString};
use std::ptr;
use super::{MonoClass, mono_handle};
use crate::{MonoError, MonoImageOpenStatus, Result, api};
struct FindContext<'a> {
target_name: &'a str,
result: Option<MonoImage>,
api_failed: bool,
}
mono_handle!(MonoImage);
impl MonoImage {
/// Finds a loaded assembly image by name.
///
/// # Errors
///
/// Returns [`MonoError::Uninitialized`] if the Mono API has not been initialized.
pub fn find(name: &str) -> Result<Option<Self>> {
let mut ctx = FindContext {
target_name: name,
result: None,
api_failed: false,
};
api()?.assembly_foreach(find_image_callback, ptr::addr_of_mut!(ctx).cast());
if ctx.api_failed {
return Err(MonoError::Uninitialized);
}
Ok(ctx.result)
}
/// Loads raw assembly bytes into a `MonoImage` without touching disk.
///
/// The bytes are always copied into Mono's internal heap, so `data` can be freed after
/// this call returns.
///
/// # Errors
///
/// Returns [`MonoError::ImageOpenFailed`] if Mono rejects the image data.
/// Returns [`MonoError::Uninitialized`] if the Mono API has not been initialized.
pub fn open_from_data(data: &mut [u8]) -> Result<Self> {
let data_len = u32::try_from(data.len())
.map_err(|_| MonoError::ImageOpenFailed(MonoImageOpenStatus::ImageInvalid))?;
let mut status: i32 = 0;
let ptr = api()?.image_open_from_data(
data.as_mut_ptr().cast(),
data_len,
1,
std::ptr::addr_of_mut!(status),
);
let s = MonoImageOpenStatus::from_raw(status);
if !s.is_ok() {
return Err(MonoError::ImageOpenFailed(s));
}
MonoImage::from_ptr(ptr).ok_or(MonoError::ImageOpenFailed(
MonoImageOpenStatus::ImageInvalid,
))
}
/// Returns the Mono-supplied error message for a raw `MonoImageOpenStatus` integer.
///
/// # Errors
///
/// Returns [`MonoError::Uninitialized`] if the Mono API has not been initialized.
pub fn open_status_message(status: i32) -> Result<String> {
let ptr = api()?.image_strerror(status);
if ptr.is_null() {
return Ok("unknown status".to_owned());
}
Ok(unsafe { CStr::from_ptr(ptr) }
.to_string_lossy()
.into_owned())
}
/// Resolves a class in this image by namespace and name.
///
/// # Errors
///
/// Returns [`MonoError::NullByteInName`] if `namespace` or `name` contain an interior null byte.
/// Returns [`MonoError::Uninitialized`] if the Mono API has not been initialized.
pub fn class_from_name(self, namespace: &str, name: &str) -> Result<Option<MonoClass>> {
let ns = CString::new(namespace).map_err(|_| MonoError::NullByteInName)?;
let nm = CString::new(name).map_err(|_| MonoError::NullByteInName)?;
let ptr = api()?.class_from_name(self.as_ptr(), ns.as_ptr(), nm.as_ptr());
Ok(MonoClass::from_ptr(ptr))
}
}
/// Callback for `mono_assembly_foreach` to find an image by name.
///
/// # Safety
///
/// `assembly` must be a valid `MonoAssembly*` and `user_data` must be a valid pointer to a
/// `FindContext`, both on a Mono-attached thread.
unsafe extern "C" fn find_image_callback(assembly: *mut c_void, user_data: *mut c_void) {
let Ok(api) = api() else {
let ctx = unsafe { &mut *user_data.cast::<FindContext<'_>>() };
ctx.api_failed = true;
return;
};
let ctx = unsafe { &mut *user_data.cast::<FindContext<'_>>() };
if ctx.result.is_some() {
return;
}
let image = api.assembly_get_image(assembly);
if image.is_null() {
return;
}
let name_ptr = api.image_get_name(image);
if name_ptr.is_null() {
return;
}
let name = unsafe { CStr::from_ptr(name_ptr) }.to_str().unwrap_or("");
if name == ctx.target_name {
ctx.result = Some(unsafe { MonoImage::from_ptr_unchecked(image) });
}
}