diff options
-rw-r--r-- | Notes/SMiri-GenericFfi.md | 46 | ||||
-rw-r--r-- | tools/standalone_miri/ffi.cpp | 141 | ||||
-rw-r--r-- | tools/standalone_miri/linux.api | 50 | ||||
-rw-r--r-- | tools/standalone_miri/win32.api | 24 |
4 files changed, 261 insertions, 0 deletions
diff --git a/Notes/SMiri-GenericFfi.md b/Notes/SMiri-GenericFfi.md new file mode 100644 index 00000000..d43b44ad --- /dev/null +++ b/Notes/SMiri-GenericFfi.md @@ -0,0 +1,46 @@ +Problem description: +==================== + +There's a collection of hard-coded FFI hooks in `miri.cpp` to handle both API +rules (e.g. that the `write` syscall takes a count and a buffer of at least +that valid size) and wrapping non-trivial or dangerous calls (e.g. the pthread +ones). + + +It would be more useful to have a runtime description/encoding of these APIs +that runs safety checks and handles the magic. + + +Requirements +============ + +- Validity checks (pointer validity, tags) + - These checks should provide useful error messages +- Returned allocation tagging (valid size, opaque tags) + - Some returned allocations can be complex +- Completely re-definining operations + - E.g. replacing pthread APIs with checked versions + + + +Design Ideas +============ + +Raw MIR? +------- +- Downside: How would it differ from actual MIR? + + +Basic-alike instruction sequence +-------------------------------- +- All lines start with a keyword + - `ASSIGN` + - `LET` + - `CALL` + + +Simplified rust-ish language +---------------------------- +- Basic type inference (uni-directional) +- Full AST + diff --git a/tools/standalone_miri/ffi.cpp b/tools/standalone_miri/ffi.cpp new file mode 100644 index 00000000..827d862f --- /dev/null +++ b/tools/standalone_miri/ffi.cpp @@ -0,0 +1,141 @@ +/* + * mrustc Standalone MIRI + * - by John Hodge (Mutabah) + * + * ffi.cpp + * - FFI wrappers + */ + +/// Argument reference (for checking validity) +struct ArgRef +{ + uint8_t idx; // if 255, it's not referencing anything + + static ArgRef null() { return ArgRef { 255 }; } +}; + +/// Representation of a FFI type (defined by the .api file) +/// - These specify various flags used to tag pointers in the MIR +struct FfiType +{ + // Pointer: + // - Mutability + // - Nullability + // - Array size (number of allocated elements) + // > Can be either a number, or an argument name + // - Valid size (number of initialised elements) + // - Allocation source + struct Pointer { + bool is_mut; + bool is_nullable; + ArgOrCount alloc_size; + ArgOrCount valid_size; + ArgRef alloc_source; // Can be "null" + }; + ::std::vector<Pointer> pointers; // Reverse list (last entry is the outermost pointer) + + // Datatypes: + // - `void` + // - size/alignment + // - u8,u16,... + // - function + // - Name + enum class Datatype { + Void, + Signed, + Unsigned, + Float, + Function, + } datatype; + union Meta { + struct { + size_t size; + size_t align; + ::std::string tag; + } void_data; + unsigned bits; + struct { + ArgRef name_source; + } function; + } meta; +}; + + +struct FfiShim +{ + class ValueRef + { + public: + static ValueRef new_global(std::string name); + static ValueRef new_local(std::string name); + static ValueRef new_deref(ValueRef target); + }; + class Expr + { + enum { + LITERAL, + VALUE, + CALL, + } ty; + union { + } data; + public: + static Expr new_lit(uint64_t v); + static Expr new_value(ValueRef vr); + static Expr new_call_int(::std::vector<::std::string> path, ::std::vector<Expr> args); + }; + struct Stmt; + struct Block + { + ::std::vector<Stmt> statements; + Expr val; + }; + class Stmt + { + enum { + DEFINE, // `let foo = bar;` + ASSIGN, // `foo ?= bar;` + IF, + } ty; + union { + struct { + ::std::string slot; + Expr value; + } define; + struct { + ValueRef slot; + Expr value; + } assign; + struct { + Expr cond; + Block true_arm; + Block false_arm; + } if_block; + }; + }; +}; + +struct FfiFunction +{ + ::std::vector<FfiType> arg_types; + FfiType ret_type; + ::std::vector<std::string> arg_names; + + // Either directly defers to a function + ::std::string library; + ::std::string function; + + // Or, it has code for more advanced checking + //FfiShimExpr code; + + bool call(Value& rv, ::std::vector<Value> args) const; +}; + +bool FfiFunction::call(Value& rv, ::std::vector<Value> args) const +{ + +} + +bool call_ffi(Value& rv, const ::std::string& link_name, const ::std::string& abi, ::std::vector<Value> args) +{ +} diff --git a/tools/standalone_miri/linux.api b/tools/standalone_miri/linux.api new file mode 100644 index 00000000..a4b8dd36 --- /dev/null +++ b/tools/standalone_miri/linux.api @@ -0,0 +1,50 @@ +# +# Expression grammar: +# `let <name> = <expr>;` +# `<slot> <op>= <expr>;` +# `<expr>` + +# `signal` - Just ignore it +fn signal(..) -> *const [null] void [size(0)] { + 0 +} +fn memchr(ptr: *const [count(n)] u8, c: u8, n: usize) -> *const [null,alloc(ptr)] u8 = "":"memchr"; +fn memrchr(ptr: *const [count(n)] u8, c: u8, n: usize) -> *const [null,alloc(ptr)] u8 = "":"memrchr"; +fn strlen(ptr: *const [cstr] u8) -> usize = "":"strlen"; + + +#fn write(fd: i32, count: isize, buf: *const void [size(count)]) -> i32 = "":"write"; +fn write(fd: i32, count: isize, buf: *const void) -> i32 { + miri::assert("invalid fd passed", fd > 0); + miri::ensure_valid_read("source buffer invalid", buf, 0, count); + miri::call_i32("", "write", fd, count, buf) +} +fn sysconf(name: i32) -> usize = "":"sysconf"; + + +# 64-bit linux pthread_attr_t +type pthread_attr_t = void [size(56),align(8)]; +fn pthread_attr_init(*mut pthread_attr_t) -> i32 = "":"pthread_attr_init"; +fn pthread_attr_destroy(*mut pthread_attr_t) -> i32 = "":"pthread_attr_destroy"; + +type pthread_key_t = u32; +static PTHREAD_NEXT_KEY: u32 = 1; +static PTHREAD_TLS: Map<u32,u64>; +fn pthread_key_create(ptr: *mut pthread_key_t) -> i32 { + let key = PTHREAD_NEXT_KEY; + PTHREAD_NEXT_KEY += 1; + *ptr = key; + 0 +} +fn pthread_key_delete(key: pthread_key_t) -> i32 { + let _ = Map::remove(key); + 0 +} +fn pthread_setspecific(key: pthread_key_t, val: u64) -> i32 { + Map::set(PTHREAD_TLS, key, val); + 0 +} +fn pthread_getspecific(key: pthread_key_t) -> u64 { + let rv_opt = Map::get(PTHREAD_TLS, key); + Option::unwrap_or(rv_opt, 0) +} diff --git a/tools/standalone_miri/win32.api b/tools/standalone_miri/win32.api new file mode 100644 index 00000000..1f374c1e --- /dev/null +++ b/tools/standalone_miri/win32.api @@ -0,0 +1,24 @@ +# +# Windows API calls +# + +type HMODULE = void [size(0), name("HMODULE")]; + +#fn GetModuleHandleW(lpcwsName: *const [cstr,null] u16) -> *const [null] HMODULE = "Kernel32.dll":"GetModuleHandleW"; +fn GetModuleHandleW(lpcwsName: *const [cstr,null] u16) -> *const [null] HMODULE { + miri::ensure_valid_nulseq("lpcwsName", lpcwsName); + return miri::call_ptr("Kernel32.dll", "GetModuleHandleW", lpcwsName); +} + +# - The returned function pointer is annotated with the passed name +fn GetProcAddress(hModule: *const HMODULE, name: *const [cstr] u8) -> fn(?) [null] { + miri::ensure_valid_nulseq("name", name); + let rv = miri::call_ptr("Kernel32.dll", "GetProcAddress", hModule, name); + # Create a named function pointer from the raw pointer return, that will go though the .api file + return miri::make_named_fn_ptr(rv, miri::cstr8_to_string(name)); +} + +fn AddVectoredExceptionHandler(..) -> usize { + 1 +} + |