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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
//! **A simple isolated dynamic FFI framework for Rust.**
//!
//! `chillffi` allows dynamically loading **C ABI-compatible libraries** (`.so`, `.dylib`, `.dll`)
//! and calling their functions at runtime, **isolating each FFI call in a separate process**.
//! If third-party native code crashes or corrupts memory,
//! the failure is contained within the isolated process,
//! keeping your main Rust application running.
//!
//! # Platform support
//!
//! Unix-like OSes and Windows (MSVC toolchain — `libffi-sys` has no `gnu` build).
//! On Unix a clone is a `fork` of the main zygote; on Windows it's a fresh
//! process re-running the same executable, since there is no `fork`.
//!
//! # Quick start
//!
//! ```no_run
//! use chillffi::ffi;
//!
//! fn main() -> ()
//! {
//! // Perform an FFI call inside an isolated context using a macro
//! let result: f64 = ffi!(|scope| {
//! // Dynamically load the system library, bound to this scope
//! let libm: Library = scope.load("libm.so.6")?;
//!
//! // Call the "sqrt" function, specifying the expected return type
//! libm.call("sqrt").arg::<f64>(4.0).result()
//!
//! // Here libm will be automatically cleared due to drop() when exiting the closure.
//! // You can also do this manually via drop(libm) or libm.unload()?
//! }).expect("FFI call failed");
//!
//! // Process the typed result
//! println!("sqrt(4.0) = {}", result);
//! assert!((result - 2.0).abs() < f64::EPSILON, "sqrt(4.0) != 2.0");
//! }
//! ```
//!
//! Example of a memory-sensitive call to the `clock_gettime` function
//! from the system library `libc.so.6` using [`AllocatedMemory`](ffi::allocatedMemory::AllocatedMemory):
//!
//! ```no_run
//! use chillffi::ffi::allocatedMemory::{AllocatedMemory};
//! use chillffi::ffi::errors::FFIError;
//! use chillffi::ffi;
//!
//! fn main() -> ()
//! {
//! // clock_gettime(CLOCK_REALTIME, ×pec) — struct out-param via Alloc/ReadMemory,
//! // the case a plain Value::Pointer can't cover on its own.
//! let (secs, nanos): (i64, i64) = ffi!(|scope| {
//! let libc: Library = scope.load("libc.so.6")?;
//!
//! // struct timespec { time_t tv_sec; long tv_nsec; } — 16 bytes on x86_64 Linux
//! let mem: AllocatedMemory = scope.alloc(16)?;
//!
//! libc.call("clock_gettime")
//! .arg::<i32>(0 /* CLOCK_REALTIME */)
//! .arg(mem.asPointer())
//! .void()?;
//!
//! let bytes: Vec<u8> = mem.read()?;
//! drop(mem);
//!
//! let secs: i64 = i64::from_ne_bytes(bytes[0..8].try_into().unwrap());
//! let nanos: i64 = i64::from_ne_bytes(bytes[8..16].try_into().unwrap());
//! Ok((secs, nanos))
//! }).expect("clock_gettime failed");
//!
//! println!("clock_gettime(CLOCK_REALTIME) = {}.{:09}", secs, nanos);
//! }
//! ```
//!
//! For more detailed examples, see the `examples` folder.
//!
//! The tests there are divided by features, and inside there are different usage variations.
//!
//! You can also run them via `cargo run --example <name>`.
//!
//! # Why is this convenient
//!
//! In general practice, we are used to doing it like in Python and other
//! programming languages — precisely specifying all the wrappers for FFI.
//! After which we observe how FFI still crashes anyway and the libraries are
//! not built, and the code does not work.
//!
//! This is all because FFI requires a manual bridge and it is not always
//! possible to make one.
//!
//! **chillffi** works on a different principle — you can write any FFI code
//! inside isolated blocks. Because FFI should not be scattered throughout your
//! code — this is an unsafe approach. Therefore, we write it in isolation and
//! preferably briefly, only when necessary.
//!
//! Since everything is located in isolated processes — we do not damage the
//! main runtime in any way and do not touch your code. All FFI requests work
//! in a sterile manner and in case of errors will clearly let you know about
//! it. You can also simply ignore them if you want.
//!
//! As a result, we can freely and simply write:
//! - Test code
//! - Educational code
//! - FFI bridges
//! - Dynamic programming languages
//! - Game engines
//! - Reactive systems and dynamic systems
//! - And many other things
//!
//! This is also different from the WASM approach — because we preserve a true
//! native execution here.
//!
//! # How it works
//!
//! 1. Before your code starts running, a Zygote is created — it is an empty
//! process for cloning itself and isolating FFI.
//! 2. When work with FFI is required — a copy is created from the zygote.
//! 3. Data and descriptors are transferred through a secure socket channel in
//! memory.
//! 4. In case of errors, the supervisor intercepts the worker crash and returns
//! the error to Rust, keeping your application stable.
//!
//! <div class="warning">
//!
//! This does not protect you from the FFI code running inside the isolated process.
//!
//! For example, if it does something with your OS or file system -
//! it is already your responsibility to separately protect against this.
//!
//! For example: You can use a virtual space for the file system and so on.
//!
//! </div>
//!
//! <div class="warning">
//!
//! FFI blocks should be as small as possible in size. I.e., not 100 lines in 1 FFI space.
//!
//! An exception can be considered when you need a single address space for several operations.
//!
//! In other cases, you should separate FFI requests as much as possible.
//!
//! Because no one can guarantee that any FFI request will not break your code.
//!
//! Even if you are an experienced programmer, there are things that do not depend on your experience.
//!
//! </div>
//!
//! # License
//!
//! The source code is distributed under the FCL license.
//! See the repository for the full text.
// =================================================================================================
compile_error!;
// =================================================================================================
/// Used for running tests.
// =================================================================================================
// =================================================================================================
use ;
use crate;
use crateCloneFlag;
use crate;
// =================================================================================================
/// Single entry point for zygote initialization in any binary (including tests).
/// Checks whether the process is running as a zygote; if so — switches to daemon mode,
/// otherwise — initializes the parent side.
// =================================================================================================
/// Internal items re-exported for the [`ffi!`] macro.
/// Not part of the public API; do not use directly.
/// Main macro for working with FFI.
///
/// It creates a copy of the zygote from the main zygote and opens a [`Scope`](crate::ffi::scope::Scope)
/// bound to it — `scope` is how you load libraries ([`Scope::load`](crate::ffi::scope::Scope::load))
/// and allocate memory ([`Scope::alloc`](crate::ffi::scope::Scope::alloc)) for the duration of the block.
///
/// `Library<'g>` can only be constructed via `scope.load(...)`, and only lives as long as the
/// scope that produced it — the compiler enforces this, not us. There is no variant of this
/// macro without a scope: an FFI block always needs one to load anything into.
///
/// Isolation allows adding FFI insertions without breaking or corrupting the main runtime.
// =================================================================================================