Skip to content

[WIP] Support for automatically shimming dynamic libraries #813

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ rustc-workspace-hack = "1.0.0"
# Depend on num-traits with default features to avoid having to rebuild
# between "cargo build" and "cargo intall".
num-traits = "*"
cfg-if = "0.1.9"
libc = "0.2.58"
libffi = "0.7.0"

[build-dependencies]
vergen = "3"
Expand Down
5 changes: 2 additions & 3 deletions src/fn_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use syntax::symbol::sym;
use rand::RngCore;

use crate::*;
use crate::sys::PlatformExt;

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
Expand Down Expand Up @@ -937,9 +938,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx

// We can't execute anything else.
_ => {
return err!(Unimplemented(
format!("can't call foreign function: {}", link_name),
));
this.eval_ffi(def_id, args, dest, ret, link_name)?;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod mono_hash_map;
mod stacked_borrows;
mod intptrcast;
mod memory;
mod sys;

use std::collections::HashMap;
use std::borrow::Cow;
Expand Down
209 changes: 209 additions & 0 deletions src/sys/linux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
use rustc::ty::{self, TyKind, Ty};
use rustc::ty::layout::{Align, LayoutOf, Size};
use rustc::hir::def_id::DefId;
use rustc::hir::Mutability;
use rustc::mir;
use syntax::attr;
use syntax::symbol::sym;
use syntax::ast::{IntTy, UintTy};

use std::ffi::CString;

use crate::*;
use crate::sys::PlatformExt;

use libc::{dlsym, RTLD_DEFAULT};
use libffi::middle::*;
use libffi::low;
use libffi::high::CType;


pub struct BoxWrapper {
pub ptr: *mut libc::c_void,
dtor: Option<Box<FnOnce(*mut libc::c_void)>>
}

impl BoxWrapper {
fn new<T>(data: T) -> BoxWrapper {
let dtor = Box::new(|raw_ptr| {
unsafe { Box::from_raw(raw_ptr as *mut T) };
});
BoxWrapper {
ptr: Box::into_raw(Box::new(data)) as *mut libc::c_void,
dtor: Some(dtor)
}
}
}

impl Drop for BoxWrapper {
fn drop(&mut self) {
(self.dtor.take().unwrap())(self.ptr)
}
}

fn ty_to_type<'tcx>(ty: Ty<'tcx>) -> InterpResult<'tcx, Type> {
Ok(match ty.sty {
TyKind::Bool => Type::u8(),
TyKind::Int(IntTy::I8) => Type::i8(),
TyKind::Int(IntTy::Isize) => Type::isize(),
TyKind::Uint(UintTy::Usize) => Type::usize(),
TyKind::RawPtr(_) => Type::pointer(),
_ => return err!(Unimplemented(format!("Don't know how represent type {:?} in FFI", ty)))
})
}

enum WrappedArg {
Plain(BoxWrapper),
Pointer(PointerData)
}

struct PointerData {
pointer: Pointer<Tag>,
data: Vec<u8>
}

fn convert_ty<'mir, 'tcx: 'mir>(this: &mut MiriEvalContext<'mir, 'tcx>,
arg: OpTy<'tcx, Tag>,
builder: &mut Builder,
args: &mut Vec<WrappedArg>) -> InterpResult<'tcx> {

Ok(match arg.layout.ty.sty {
TyKind::Bool => {
args.push(WrappedArg::Plain(BoxWrapper::new(this.read_scalar(arg)?.to_bool()?)));
},
TyKind::Int(IntTy::I8) => {
args.push(WrappedArg::Plain(BoxWrapper::new(this.read_scalar(arg)?.to_i8()?)));
},
TyKind::Int(IntTy::Isize) => {
args.push(WrappedArg::Plain(BoxWrapper::new(this.read_scalar(arg)?.to_isize(this)?)));
}
TyKind::Uint(UintTy::Usize) => {
args.push(WrappedArg::Plain(BoxWrapper::new(this.read_scalar(arg)?.to_usize(this)?)));
},
TyKind::RawPtr(_) => {
let ptr = this.read_scalar(arg)?.to_ptr()?;
let mut bytes = this.memory_mut().get_mut(ptr.alloc_id)?.bytes.clone();
args.push(WrappedArg::Pointer(PointerData {
pointer: ptr,
data: bytes
}));
}
_ => return err!(Unimplemented(format!("Don't know how represent type {:?} in FFI", arg.layout.ty)))
})
}

#[derive(Debug)]
enum RetData {
Small(u128),
Large(Vec<u8>)
}

fn call_fn<'mir, 'tcx: 'mir>(this: &mut MiriEvalContext<'mir, 'tcx>, ptr: *const libc::c_void, args: &[OpTy<'tcx, Tag>], ret: PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
let mut builder = Builder::new();
let mut actual_args = vec![];
for arg in args {
error!("miri: adding arg {:?}", arg);
builder = builder.arg(ty_to_type(arg.layout.ty)?);
convert_ty(this, *arg, &mut builder, &mut actual_args)?;
}
builder = builder.res(ty_to_type(ret.layout.ty)?);;
let mut cif = builder.into_cif();

let mut cif_args: Vec<*mut libc::c_void> = vec![];

for arg in &mut actual_args {
match arg {
WrappedArg::Plain(box_wrapper) => {
cif_args.push(box_wrapper.ptr)
},
WrappedArg::Pointer(pointer_data) => {
cif_args.push(pointer_data.data.as_mut_ptr() as *mut libc::c_void)
}
}
}

let fn_ptr = unsafe { std::mem::transmute::<*const libc::c_void, Option<unsafe extern "C" fn()>>(ptr) };

let mut ret_data: RetData;
let mut ret_ptr: *mut libc::c_void;
// It fits in a u128 - we can use an Immediate
if ret.layout.size.bytes() <= 16 {
ret_data = RetData::Small(0);
match &mut ret_data {
RetData::Small(ref mut s) => ret_ptr = s as *mut u128 as *mut libc::c_void,
_ => unreachable!()
}
} else {
ret_data = RetData::Large(vec![0; ret.layout.size.bytes() as usize]);
match &mut ret_data {
RetData::Large(ref mut l) => ret_ptr = l.as_mut_ptr() as *mut libc::c_void,
_ => unreachable!()
}
}

error!("miri: calling cif {:?}", cif);

let res = unsafe {
libffi::raw::ffi_call(cif.as_raw_ptr(),
fn_ptr,
ret_ptr,
cif_args.as_mut_ptr())

};

error!("miri: Result: {:?}", ret_data);

let tcx = &{this.tcx.tcx};

for arg in &actual_args {
match arg {
WrappedArg::Pointer(pointer_data) => {
error!("miri: Writing back data to pointer {:?}: {:?}", pointer_data.pointer,
pointer_data.data);
this.memory_mut().get_mut(pointer_data.pointer.alloc_id)?.write_bytes(
tcx,
pointer_data.pointer,
&pointer_data.data
)?;
},
_ => {}
}
}

match ret_data {
RetData::Small(s) => {
this.write_scalar(Scalar::from_uint(s, ret.layout.size), ret)?;
},
RetData::Large(data) => {
let ptr = ret.to_ptr()?;
this.memory_mut().get_mut(ptr.alloc_id)?.write_bytes(tcx, ptr, &data)?;
}
}

Ok(())
}

impl<'mir, 'tcx: 'mir> PlatformExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {
fn eval_ffi(
&mut self,
def_id: DefId,
args: &[OpTy<'tcx, Tag>],
dest: PlaceTy<'tcx, Tag>,
ret: mir::BasicBlock,
link_name: &str
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
error!("calling dlsym({})", link_name);

let c_link_name = CString::new(link_name).unwrap();
let ret = unsafe { dlsym(RTLD_DEFAULT, c_link_name.as_ptr()) };
error!("dlsym({}): got symbol {:?}", link_name, ret);

if ret.is_null() {
return err!(Unimplemented(format!("Failed to find dynamic symbol {}", link_name)));
}

call_fn(self, ret, args, dest)?;

Ok(None)
}
}
27 changes: 27 additions & 0 deletions src/sys/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use cfg_if::cfg_if;

use rustc::ty;
use rustc::ty::layout::{Align, LayoutOf, Size};
use rustc::hir::def_id::DefId;
use rustc::mir;
use syntax::attr;
use syntax::symbol::sym;
use crate::*;

pub trait PlatformExt<'mir, 'tcx>: crate::MiriEvalContextExt<'mir, 'tcx> {
fn eval_ffi(
&mut self,
def_id: DefId,
args: &[OpTy<'tcx, Tag>],
dest: PlaceTy<'tcx, Tag>,
ret: mir::BasicBlock,
link_name: &str
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>;
}

cfg_if! {
if #[cfg(target_os = "linux")] {
mod linux;
pub use linux::*;
}
}