Skip to content

Commit a5c0d97

Browse files
committed
native-lib: pass structs to native code
1 parent ebdb536 commit a5c0d97

File tree

8 files changed

+403
-130
lines changed

8 files changed

+403
-130
lines changed

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ features = ['unprefixed_malloc_on_supported_platforms']
3838

3939
[target.'cfg(unix)'.dependencies]
4040
libc = "0.2"
41-
libffi = "4.0.0"
41+
libffi = "4.1.1"
4242
libloading = "0.8"
4343

4444
[target.'cfg(target_os = "linux")'.dependencies]

src/shims/native_lib/ffi.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use libffi::low::CodePtr;
2+
use libffi::middle::{Arg as ArgPtr, Cif, Type as FfiType};
3+
4+
/// Perform the actual FFI call.
5+
///
6+
/// SAFETY: The `FfiArg`s passed must have been correctly instantiated (i.e. their
7+
/// type layout must match the data they point to), and the safety invariants of
8+
/// the foreign function being called must be upheld (if any).
9+
pub unsafe fn call<'a, R: libffi::high::CType>(fun: CodePtr, args: Vec<FfiArg<'a>>) -> R {
10+
let mut arg_tys = vec![];
11+
let mut arg_ptrs = vec![];
12+
for arg in args {
13+
arg_tys.push(arg.ty);
14+
arg_ptrs.push(arg.ptr)
15+
}
16+
let cif = Cif::new(arg_tys, R::reify().into_middle());
17+
unsafe { cif.call(fun, &arg_ptrs) }
18+
}
19+
20+
/// A wrapper type for `libffi::middle::Type` which also holds a pointer to the data.
21+
pub struct FfiArg<'a> {
22+
/// The type layout information for the pointed-to data.
23+
ty: FfiType,
24+
/// A pointer to the data described in `ty`.
25+
ptr: ArgPtr,
26+
/// Lifetime of the actual pointed-to data.
27+
_p: std::marker::PhantomData<&'a [u8]>,
28+
}
29+
30+
impl<'a> FfiArg<'a> {
31+
fn new(ty: FfiType, ptr: ArgPtr) -> Self {
32+
Self { ty, ptr, _p: std::marker::PhantomData }
33+
}
34+
}
35+
36+
/// An owning form of `FfiArg`.
37+
/// We introduce this enum instead of just calling `Arg::new` and storing a list of
38+
/// `libffi::middle::Arg` directly, because the `libffi::middle::Arg` just wraps a reference to
39+
/// the value it represents and we need to store a copy of the value, and pass a reference to
40+
/// this copy to C instead.
41+
#[derive(Debug, Clone)]
42+
pub enum CArg {
43+
/// Primitive type.
44+
Primitive(CPrimitive),
45+
/// Struct with its computed type layout and bytes.
46+
Struct(FfiType, Box<[u8]>),
47+
}
48+
49+
impl CArg {
50+
/// Convert a `CArg` to the required FFI argument type.
51+
pub fn arg_downcast<'a>(&'a self) -> FfiArg<'a> {
52+
match self {
53+
CArg::Primitive(cprim) => cprim.arg_downcast(),
54+
// FIXME: Using `&items[0]` to reference the whole array is definitely
55+
// unsound under SB, but we're waiting on
56+
// https://github.com/libffi-rs/libffi-rs/commit/112a37b3b6ffb35bd75241fbcc580de40ba74a73
57+
// to land in a release so that we don't need to do this.
58+
CArg::Struct(cstruct, items) => FfiArg::new(cstruct.clone(), ArgPtr::new(&items[0])),
59+
}
60+
}
61+
}
62+
63+
impl From<CPrimitive> for CArg {
64+
fn from(prim: CPrimitive) -> Self {
65+
Self::Primitive(prim)
66+
}
67+
}
68+
69+
#[derive(Debug, Clone)]
70+
/// Enum of supported primitive arguments to external C functions.
71+
pub enum CPrimitive {
72+
/// 8-bit signed integer.
73+
Int8(i8),
74+
/// 16-bit signed integer.
75+
Int16(i16),
76+
/// 32-bit signed integer.
77+
Int32(i32),
78+
/// 64-bit signed integer.
79+
Int64(i64),
80+
/// isize.
81+
ISize(isize),
82+
/// 8-bit unsigned integer.
83+
UInt8(u8),
84+
/// 16-bit unsigned integer.
85+
UInt16(u16),
86+
/// 32-bit unsigned integer.
87+
UInt32(u32),
88+
/// 64-bit unsigned integer.
89+
UInt64(u64),
90+
/// usize.
91+
USize(usize),
92+
/// Raw pointer, stored as C's `void*`.
93+
RawPtr(*mut std::ffi::c_void),
94+
}
95+
96+
impl CPrimitive {
97+
/// Convert a primitive to the required FFI argument type.
98+
fn arg_downcast<'a>(&'a self) -> FfiArg<'a> {
99+
match self {
100+
CPrimitive::Int8(i) => FfiArg::new(FfiType::i8(), ArgPtr::new(i)),
101+
CPrimitive::Int16(i) => FfiArg::new(FfiType::i16(), ArgPtr::new(i)),
102+
CPrimitive::Int32(i) => FfiArg::new(FfiType::i32(), ArgPtr::new(i)),
103+
CPrimitive::Int64(i) => FfiArg::new(FfiType::i64(), ArgPtr::new(i)),
104+
CPrimitive::ISize(i) => FfiArg::new(FfiType::isize(), ArgPtr::new(i)),
105+
CPrimitive::UInt8(i) => FfiArg::new(FfiType::u8(), ArgPtr::new(i)),
106+
CPrimitive::UInt16(i) => FfiArg::new(FfiType::u16(), ArgPtr::new(i)),
107+
CPrimitive::UInt32(i) => FfiArg::new(FfiType::u32(), ArgPtr::new(i)),
108+
CPrimitive::UInt64(i) => FfiArg::new(FfiType::u64(), ArgPtr::new(i)),
109+
CPrimitive::USize(i) => FfiArg::new(FfiType::usize(), ArgPtr::new(i)),
110+
CPrimitive::RawPtr(i) => FfiArg::new(FfiType::pointer(), ArgPtr::new(i)),
111+
}
112+
}
113+
}

0 commit comments

Comments
 (0)