diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index a3764a7d14266..d360d14aed5b4 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -994,6 +994,11 @@ impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRef<'a, 'tcx, Pr pub(crate) fn has_provenance(&self) -> bool { !self.alloc.provenance().range_empty(self.range, &self.tcx) } + + /// Get the base address of the backing allocation + pub fn base_addr(&self) -> *const u8 { + self.alloc.base_addr() + } } impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 6705bf7b0070c..f3952bcb52b21 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -24,6 +24,9 @@ log = "0.4" rand = "0.8" smallvec = "1.7" +libffi = "3.2.0" +libloading = "0.7" + # A noop dependency that changes in the Rust repository, it's a bit of a hack. # See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` # for more information. @@ -33,11 +36,8 @@ measureme = "10.0.0" [target.'cfg(unix)'.dependencies] libc = "0.2" -[target.'cfg(target_os = "linux")'.dependencies] -libffi = "3.2.0" -libloading = "0.7" - [dev-dependencies] +cc = "1.0" colored = "2" ui_test = "0.5" rustc_version = "0.4" diff --git a/src/tools/miri/src/aligned_bytes.rs b/src/tools/miri/src/aligned_bytes.rs new file mode 100644 index 0000000000000..7fb7f770fdc06 --- /dev/null +++ b/src/tools/miri/src/aligned_bytes.rs @@ -0,0 +1,73 @@ +use std::borrow::Cow; +use std::alloc::{Allocator, Global, Layout}; +use std::ops::{Deref, DerefMut}; +use std::ptr::NonNull; +use rustc_abi::{Size, Align}; +use rustc_middle::mir::interpret::AllocBytes; + +enum FillBytes<'a> { + Bytes(&'a [u8]), + Zero(Size), +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct AlignedBytes(NonNull<[u8]>, Align); + +impl AlignedBytes { + fn alloc(fill: FillBytes<'_>, align: Align) -> Option { + let len = match fill { + FillBytes::Bytes(b) => b.len(), + FillBytes::Zero(s) => s.bytes() as usize, + }; + + let layout = Layout::from_size_align(len, align.bytes() as usize) + .unwrap(); + let mut bytes = Global.allocate_zeroed(layout) + .ok()?; + + let slice = unsafe { bytes.as_mut() }; + match fill { + FillBytes::Bytes(data) => slice.copy_from_slice(data), + FillBytes::Zero(_) => (), + } + + debug_assert_eq!(bytes.as_ptr().cast::<()>() as usize % align.bytes() as usize, 0); + + Some(Self(bytes, align)) + } +} + +impl Deref for AlignedBytes { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + unsafe { self.0.as_ref() } + } +} + +impl DerefMut for AlignedBytes { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { self.0.as_mut() } + } +} + +impl AllocBytes for AlignedBytes { + fn adjust_to_align(self, align: Align) -> Self { + if self.1 >= align { + self + } else { + let out = Self::alloc(FillBytes::Bytes(&*self), align) + .unwrap(); + out + } + } + + fn from_bytes<'a>(slice: impl Into>, align: Align) -> Self { + Self::alloc(FillBytes::Bytes(&*slice.into()), align) + .unwrap() + } + + fn zeroed(size: Size, align: Align) -> Option { + Self::alloc(FillBytes::Zero(size), align) + } +} \ No newline at end of file diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index fb4e59acd001d..0e19c0850d751 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -9,6 +9,7 @@ #![feature(local_key_cell_methods)] #![feature(is_terminal)] #![feature(round_ties_even)] +#![feature(allocator_api)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, @@ -43,6 +44,7 @@ #![recursion_limit = "256"] extern crate rustc_apfloat; +extern crate rustc_abi; extern crate rustc_ast; #[macro_use] extern crate rustc_middle; @@ -73,6 +75,7 @@ mod operator; mod range_map; mod shims; mod tag_gc; +mod aligned_bytes; // Establish a "crate-wide prelude": we often import `crate::*`. diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 477d8d33ebba7..14ef3ea599255 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -458,9 +458,9 @@ pub struct MiriMachine<'mir, 'tcx> { pub(crate) basic_block_count: u64, /// Handle of the optional shared object file for external functions. - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "windows"))] pub external_so_lib: Option<(libloading::Library, std::path::PathBuf)>, - #[cfg(not(target_os = "linux"))] + #[cfg(not(any(target_os = "linux", target_os = "windows")))] pub external_so_lib: Option, /// Run a garbage collector for BorTags every N basic blocks. @@ -549,7 +549,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { report_progress: config.report_progress, basic_block_count: 0, clock: Clock::new(config.isolated_op == IsolatedOp::Allow), - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "windows"))] external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| { let target_triple = layout_cx.tcx.sess.opts.target_triple.triple(); // Check if host target == the session target. @@ -571,7 +571,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { lib_file_path.clone(), ) }), - #[cfg(not(target_os = "linux"))] + #[cfg(not(any(target_os = "linux", target_os = "windows")))] external_so_lib: config.external_so_file.as_ref().map(|_| { panic!("loading external .so files is only supported on Linux") }), diff --git a/src/tools/miri/src/shims/ffi_support.rs b/src/tools/miri/src/shims/ffi_support.rs index e628c44a86788..0a1b2d358a0a9 100644 --- a/src/tools/miri/src/shims/ffi_support.rs +++ b/src/tools/miri/src/shims/ffi_support.rs @@ -1,11 +1,14 @@ +use std::ffi::c_void; use libffi::{high::call as ffi, low::CodePtr}; use std::ops::Deref; +use std::path::Path; -use rustc_middle::ty::{self as ty, IntTy, Ty, UintTy}; +use rustc_middle::ty::{self as ty, IntTy, UintTy}; use rustc_span::Symbol; -use rustc_target::abi::HasDataLayout; +use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout}; use crate::*; +use crate::intptrcast::GlobalStateInner; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} @@ -13,11 +16,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Extract the scalar value from the result of reading a scalar from the machine, /// and convert it to a `CArg`. fn scalar_to_carg( + &mut self, k: Scalar, - arg_type: Ty<'tcx>, - cx: &impl HasDataLayout, + arg_type: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, CArg> { - match arg_type.kind() { + let this = self.eval_context_mut(); + + match arg_type.ty.kind() { // If the primitive provided can be converted to a type matching the type pattern // then create a `CArg` of this primitive value with the corresponding `CArg` constructor. // the ints @@ -36,7 +41,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ty::Int(IntTy::Isize) => { // This will fail if host != target, but then the entire FFI thing probably won't work well // in that situation. - return Ok(CArg::ISize(k.to_target_isize(cx)?.try_into().unwrap())); + return Ok(CArg::ISize(k.to_target_isize(this)?.try_into().unwrap())); } // the uints ty::Uint(UintTy::U8) => { @@ -54,7 +59,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ty::Uint(UintTy::Usize) => { // This will fail if host != target, but then the entire FFI thing probably won't work well // in that situation. - return Ok(CArg::USize(k.to_target_usize(cx)?.try_into().unwrap())); + return Ok(CArg::USize(k.to_target_usize(this)?.try_into().unwrap())); + } + // pointers + ty::RawPtr(ty::TypeAndMut { mutbl, ty }) if mutbl.is_not() => { + // FIXME: Add warning for types that are probably not C-portable + // FIXME: This should + // - expose the pointer + if let Scalar::Ptr(ptr, _) = k { + if let Provenance::Concrete { alloc_id, tag } = ptr.provenance { + GlobalStateInner::expose_ptr( + this, + alloc_id, + tag, + )?; + } + + let pointee = this.tcx.layout_of(this.param_env().and(*ty)) + .unwrap(); + let alloc = this.get_ptr_alloc( + ptr.into(), + pointee.layout.size(), + pointee.layout.align().abi, + )?; + + // If none, the pointee is a ZST, so we can just magic up an address for it + let addr = alloc.map(|a| a.base_addr()) + .unwrap_or_else(|| arg_type.layout.align().abi.bytes() as usize as *const _); + return Ok(CArg::Ptr(addr as *const c_void)); + } } _ => {} } @@ -154,14 +187,50 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } + /// Check whether a loaded symbol is correct to use - on linux this ensures it comes from + /// the expected so, on windows this always returns true currently because lookup doesn't check + /// dependencies. + fn verify_sym(_func: &libloading::Symbol<'_, unsafe extern "C" fn()>, _lib_path: &Path) -> bool { + #[cfg(unix)] + { + // FIXME: this is a hack! + // The `libloading` crate will automatically load system libraries like `libc`. + // On linux `libloading` is based on `dlsym`: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#202 + // and `dlsym`(https://linux.die.net/man/3/dlsym) looks through the dependency tree of the + // library if it can't find the symbol in the library itself. + // So, in order to check if the function was actually found in the specified + // `machine.external_so_lib` we need to check its `dli_fname` and compare it to + // the specified SO file path. + // This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`, + // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411 + // using the `libc` crate where this interface is public. + // No `libc::dladdr` on windows. + let mut info = std::mem::MaybeUninit::::uninit(); + unsafe { + if libc::dladdr(*_func.deref() as *const _, info.as_mut_ptr()) != 0 { + if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap() + != _lib_path.to_str().unwrap() + { + return false; + } + } + } + return true; + } + + #[cfg(windows)] + { + return true; + } + } + /// Get the pointer to the function of the specified name in the shared object file, /// if it exists. The function must be in the shared object file specified: we do *not* /// return pointers to functions in dependencies of the library. fn get_func_ptr_explicitly_from_lib(&mut self, link_name: Symbol) -> Option { let this = self.eval_context_mut(); // Try getting the function from the shared library. - // On windows `_lib_path` will be unused, hence the name starting with `_`. - let (lib, _lib_path) = this.machine.external_so_lib.as_ref().unwrap(); + let (lib, lib_path) = this.machine.external_so_lib.as_ref().unwrap(); let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe { match lib.get(link_name.as_str().as_bytes()) { Ok(x) => x, @@ -171,30 +240,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } }; - // FIXME: this is a hack! - // The `libloading` crate will automatically load system libraries like `libc`. - // On linux `libloading` is based on `dlsym`: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#202 - // and `dlsym`(https://linux.die.net/man/3/dlsym) looks through the dependency tree of the - // library if it can't find the symbol in the library itself. - // So, in order to check if the function was actually found in the specified - // `machine.external_so_lib` we need to check its `dli_fname` and compare it to - // the specified SO file path. - // This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`, - // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411 - // using the `libc` crate where this interface is public. - // No `libc::dladdr` on windows. - let mut info = std::mem::MaybeUninit::::uninit(); - unsafe { - if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 { - if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap() - != _lib_path.to_str().unwrap() - { - return None; - } - } + if Self::verify_sym(&func, lib_path) { + // Return a pointer to the function. + Some(CodePtr(*func.deref() as *mut _)) + } else { + None } - // Return a pointer to the function. - Some(CodePtr(*func.deref() as *mut _)) } /// Call specified external C function, with supplied arguments. @@ -217,15 +268,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } }; - let this = self.eval_context_mut(); - // Get the function arguments, and convert them to `libffi`-compatible form. let mut libffi_args = Vec::::with_capacity(args.len()); for cur_arg in args.iter() { - libffi_args.push(Self::scalar_to_carg( - this.read_scalar(cur_arg)?, - cur_arg.layout.ty, - this, + let scalar = self.eval_context_mut().read_scalar(cur_arg)?; + libffi_args.push(self.scalar_to_carg( + scalar, + cur_arg.layout, )?); } @@ -268,6 +317,8 @@ pub enum CArg { UInt64(u64), /// usize. USize(usize), + /// A pointer to a value + Ptr(*const c_void), } impl<'a> CArg { @@ -284,6 +335,7 @@ impl<'a> CArg { CArg::UInt32(i) => ffi::arg(i), CArg::UInt64(i) => ffi::arg(i), CArg::USize(i) => ffi::arg(i), + CArg::Ptr(i) => ffi::arg(i), } } } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index fcee381ff7132..2bb81decdab76 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -383,7 +383,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); // First deal with any external C functions in linked .so file. - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "windows"))] if this.machine.external_so_lib.as_ref().is_some() { use crate::shims::ffi_support::EvalContextExt as _; // An Ok(false) here means that the function being called was not exported diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index 918efda3777fa..3af70de5950be 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -1,7 +1,7 @@ #![warn(clippy::integer_arithmetic)] mod backtrace; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "windows"))] pub mod ffi_support; pub mod foreign_items; pub mod intrinsics; diff --git a/src/tools/miri/tests/compiletest.rs b/src/tools/miri/tests/compiletest.rs index 21eaeabe6ad8f..4b7c1b4ff5afa 100644 --- a/src/tools/miri/tests/compiletest.rs +++ b/src/tools/miri/tests/compiletest.rs @@ -1,7 +1,8 @@ +use std::borrow::Cow; use colored::*; use regex::bytes::Regex; use std::path::{Path, PathBuf}; -use std::{env, process::Command}; +use std::env; use ui_test::{color_eyre::Result, Config, Mode, OutputConflictHandling}; fn miri_path() -> PathBuf { @@ -14,33 +15,68 @@ fn get_host() -> String { .host } +fn get_args(output: &Path) -> Vec> { + #[cfg(target_os = "linux")] + return vec![ + "-shared", + "-o", + output.to_str().unwrap(), + "tests/extern-so/test.c", + // Only add the functions specified in libcode.version to the shared object file. + // This is to avoid automatically adding `malloc`, etc. + // Source: https://anadoxin.org/blog/control-over-symbol-exports-in-gcc.html/ + "-fPIC", + "-Wl,--version-script=tests/extern-so/libcode.version", + ].into_iter().map(Cow::Borrowed).collect(); + #[cfg(target_os = "windows")] + return vec![ + Cow::Borrowed("/LD"), + Cow::Borrowed("tests/extern-so/test.c"), + Cow::Borrowed("/link"), + Cow::Owned(format!("/OUT:{}", output.to_str().unwrap())), + ]; +} + // Build the shared object file for testing external C function calls. fn build_so_for_c_ffi_tests() -> PathBuf { - let cc = option_env!("CC").unwrap_or("cc"); + // TODO: Support multiple so files with individual tests // Target directory that we can write to. let so_target_dir = Path::new(&env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri-extern-so"); // Create the directory if it does not already exist. std::fs::create_dir_all(&so_target_dir) .expect("Failed to create directory for shared object file"); - let so_file_path = so_target_dir.join("libtestlib.so"); - let cc_output = Command::new(cc) - .args([ - "-shared", - "-o", - so_file_path.to_str().unwrap(), - "tests/extern-so/test.c", - // Only add the functions specified in libcode.version to the shared object file. - // This is to avoid automatically adding `malloc`, etc. - // Source: https://anadoxin.org/blog/control-over-symbol-exports-in-gcc.html/ - "-fPIC", - "-Wl,--version-script=tests/extern-so/libcode.version", - ]) - .output() + + #[cfg(not(windows))] + let ext = "libtestlib.so"; + #[cfg(windows)] + let ext = "testlib.dll"; + let so_file_path = so_target_dir.join(ext); + + #[cfg(not(windows))] + let mut cc = Command::new(option_env!("CC").unwrap_or("cc")); + #[cfg(windows)] + let mut cc = cc::windows_registry::find( + &get_host(), + "cl.exe", + ).unwrap(); + + let args = get_args(&so_file_path); + cc.args(args.iter().map(|c| &**c)); + + let output = cc.output() .expect("failed to generate shared object file for testing external C function calls"); - if !cc_output.status.success() { - panic!("error in generating shared object file for testing external C function calls"); + + if !output.status.success() { + panic!( + "error in generating shared object file for testing external C function calls:\n--------STDOUT--------\n{}\n--------STDERR--------\n{}", + std::str::from_utf8(&output.stdout) + .unwrap_or(""), + std::str::from_utf8(&output.stderr) + .unwrap_or(""), + ); } - so_file_path + + so_target_dir.join(ext) } fn run_tests(mode: Mode, path: &str, target: &str, with_dependencies: bool) -> Result<()> { @@ -80,10 +116,10 @@ fn run_tests(mode: Mode, path: &str, target: &str, with_dependencies: bool) -> R config.args.push(target.into()); } - // If we're on linux, and we're testing the extern-so functionality, + // If we're testing the extern-so functionality, // then build the shared object file for testing external C function calls // and push the relevant compiler flag. - if cfg!(target_os = "linux") && path.starts_with("tests/extern-so/") { + if path.starts_with("tests/extern-so/") { let so_file_path = build_so_for_c_ffi_tests(); let mut flag = std::ffi::OsString::from("-Zmiri-extern-so-file="); flag.push(so_file_path.into_os_string()); @@ -100,15 +136,12 @@ fn run_tests(mode: Mode, path: &str, target: &str, with_dependencies: bool) -> R }; // Handle command-line arguments. - config.path_filter.extend(std::env::args().skip(1).filter(|arg| { - match &**arg { - "--quiet" => { - config.quiet = true; - false - } - _ => true, - } - })); + config.path_filter.extend(std::env::args() + .skip_while(|arg| arg != "--")); + + config.path_filter.extend(["extern-so".to_string()]); + + eprintln!("Path Filter: {:?}", config.path_filter); let use_std = env::var_os("MIRI_NO_STD").is_none(); @@ -212,15 +245,13 @@ fn main() -> Result<()> { ui(Mode::Pass, "tests/pass-dep", &target, WithDependencies)?; ui(Mode::Panic, "tests/panic", &target, WithDependencies)?; ui(Mode::Fail { require_patterns: true }, "tests/fail", &target, WithDependencies)?; - if cfg!(target_os = "linux") { - ui(Mode::Pass, "tests/extern-so/pass", &target, WithoutDependencies)?; - ui( - Mode::Fail { require_patterns: true }, - "tests/extern-so/fail", - &target, - WithoutDependencies, - )?; - } + ui(Mode::Pass, "tests/extern-so/pass", &target, WithoutDependencies)?; + ui( + Mode::Fail { require_patterns: true }, + "tests/extern-so/fail", + &target, + WithoutDependencies, + )?; Ok(()) } diff --git a/src/tools/miri/tests/extern-so/fail/function_not_in_so.rs b/src/tools/miri/tests/extern-so/fail/function_not_in_so.rs index 3540c75b73a12..63a1a48f954c3 100644 --- a/src/tools/miri/tests/extern-so/fail/function_not_in_so.rs +++ b/src/tools/miri/tests/extern-so/fail/function_not_in_so.rs @@ -1,4 +1,3 @@ -//@only-target-linux //@only-on-host //@normalize-stderr-test: "OS `.*`" -> "$$OS" diff --git a/src/tools/miri/tests/extern-so/pass/call_extern_c_fn.rs b/src/tools/miri/tests/extern-so/pass/call_extern_c_fn.rs index 1e1d0b11e99ff..c29b2c68ace6d 100644 --- a/src/tools/miri/tests/extern-so/pass/call_extern_c_fn.rs +++ b/src/tools/miri/tests/extern-so/pass/call_extern_c_fn.rs @@ -1,4 +1,3 @@ -//@only-target-linux //@only-on-host extern "C" { diff --git a/src/tools/miri/tests/extern-so/pass/call_extern_c_ptr.rs b/src/tools/miri/tests/extern-so/pass/call_extern_c_ptr.rs new file mode 100644 index 0000000000000..06601bf8756c5 --- /dev/null +++ b/src/tools/miri/tests/extern-so/pass/call_extern_c_ptr.rs @@ -0,0 +1,38 @@ +//@only-on-host + +#[repr(C)] +struct Foo { + a: i32, + b: f32, +} + +extern "C" { + fn single_deref(p: *const i32) -> i32; + fn double_deref(p: *const *const i32) -> i32; + fn struct_a(p: *const Foo) -> i32; + fn struct_b(p: *const Foo) -> f32; +} + +fn main() { + let foo = Foo { a: -10, b: -1.0 }; + + let a = &20 as *const _; + let b = &a as *const _; + + let alloc = Box::new(20); + let alloc_ptr = &(&*alloc as *const _) as *const _; + + unsafe { + assert_eq!(single_deref(&10), 10); + + println!("Double Alloc"); + assert_eq!(double_deref(alloc_ptr), 20); + + println!("Double Global"); + assert_eq!(double_deref(b), 20); + + assert_eq!(struct_a(&foo), -10); + + assert_eq!(struct_b(&foo), -1.0); + } +} diff --git a/src/tools/miri/tests/extern-so/test.c b/src/tools/miri/tests/extern-so/test.c index 68714f1743b6e..931773bee97a0 100644 --- a/src/tools/miri/tests/extern-so/test.c +++ b/src/tools/miri/tests/extern-so/test.c @@ -1,27 +1,61 @@ #include +#include -int add_one_int(int x) { +#ifdef _WIN32 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; +typedef uint32_t u32; + +EXPORT i32 add_one_int(i32 x) { return 2 + x; } -void printer() { +EXPORT void printer() { printf("printing from C\n"); } // function with many arguments, to test functionality when some args are stored // on the stack -int test_stack_spill(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) { +EXPORT i32 test_stack_spill(i32 a, i32 b, i32 c, i32 d, i32 e, i32 f, i32 g, i32 h, i32 i, i32 j, i32 k, i32 l) { return a+b+c+d+e+f+g+h+i+j+k+l; } -unsigned int get_unsigned_int() { +EXPORT u32 get_unsigned_int() { return -10; } -short add_int16(short x) { +EXPORT i16 add_int16(i16 x) { return x + 3; } -long add_short_to_long(short x, long y) { +EXPORT i64 add_short_to_long(i16 x, i64 y) { return x + y; } + +EXPORT i32 single_deref(const i32 *p) { + return *p; +} + +EXPORT i32 double_deref(const i32 **p) { + fprintf(stderr, "%p\n", *p); + return **p; +} + +struct Foo { + i32 a; + float b; +}; + +EXPORT i32 struct_int(const struct Foo* p) { + return p->a; +} + +EXPORT float struct_float(const struct Foo* p) { + return p->b; +}