summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Notes/SMiri-GenericFfi.md46
-rw-r--r--tools/standalone_miri/ffi.cpp141
-rw-r--r--tools/standalone_miri/linux.api50
-rw-r--r--tools/standalone_miri/win32.api24
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
+}
+