diff options
author | John Hodge <tpg@mutabah.net> | 2019-11-23 13:24:30 +0800 |
---|---|---|
committer | John Hodge <tpg@mutabah.net> | 2019-11-23 13:24:30 +0800 |
commit | bfc90119d8fdfbd2e1a4935d1d73d4b19198fe0d (patch) | |
tree | a8a0239aa283b74ff0d36e2e7c52b67aa0fe1f59 | |
parent | 87df981564afc3debfe5b11842f375bfae5900ff (diff) | |
download | mrust-bfc90119d8fdfbd2e1a4935d1d73d4b19198fe0d.tar.gz |
Standalone MIRI - Extended windows FFI support, works for "hello, world"
-rw-r--r-- | tools/standalone_miri/miri.cpp | 173 | ||||
-rw-r--r-- | tools/standalone_miri/u128.hpp | 1 | ||||
-rw-r--r-- | tools/standalone_miri/value.cpp | 31 | ||||
-rw-r--r-- | tools/standalone_miri/value.hpp | 9 | ||||
-rw-r--r-- | vsproject/build_hello_mmir.cmd | 2 |
5 files changed, 214 insertions, 2 deletions
diff --git a/tools/standalone_miri/miri.cpp b/tools/standalone_miri/miri.cpp index 951b3ccd..70d15109 100644 --- a/tools/standalone_miri/miri.cpp +++ b/tools/standalone_miri/miri.cpp @@ -41,6 +41,8 @@ public: virtual bool modulo(const PrimitiveValue& v) = 0; virtual void write_to_value(ValueCommonWrite& tgt, size_t ofs) const = 0; + virtual U128 as_u128() const = 0; + template<typename T> const T& check(const char* opname) const { @@ -97,6 +99,10 @@ struct PrimitiveUInt: this->v = newv; return true; } + + U128 as_u128() const override { + return U128(static_cast<uint64_t>(this->v)); + } }; struct PrimitiveU64: public PrimitiveUInt<uint64_t> { @@ -175,6 +181,10 @@ struct PrimitiveSInt: this->v = newv; return true; } + + U128 as_u128() const override { + return U128(static_cast<uint64_t>(this->v)); + } }; struct PrimitiveI64: public PrimitiveSInt<int64_t> { @@ -1746,6 +1756,7 @@ bool InterpreterThread::step_one(Value& out_thread_result) { RelocationPtr fcn_alloc_ptr; const ::HIR::Path* fcn_p; + ::HIR::Path ffi_fcn_ptr; if( te.fcn.is_Path() ) { fcn_p = &te.fcn.as_Path(); } @@ -1763,17 +1774,28 @@ bool InterpreterThread::step_one(Value& out_thread_result) case RelocationPtr::Ty::Function: fcn_p = &fcn_alloc_ptr.fcn(); break; +#if 0 case RelocationPtr::Ty::FfiPointer: if( !fcn_alloc_ptr.ffi().layout ) { + // TODO: FFI function pointers + // - Call the function pointer using known argument rules #ifdef _WIN32 if( fcn_alloc_ptr.ffi().ptr_value == AcquireSRWLockExclusive ) { - LOG_TODO(""); + ffi_fcn_ptr = ::HIR::Path(::HIR::SimplePath { "#FFI", { "system", "AcquireSRWLockExclusive" } }); + fcn_p = &ffi_fcn_ptr; + break; + } + else if( fcn_alloc_ptr.ffi().ptr_value == ReleaseSRWLockExclusive ) + { + ffi_fcn_ptr = ::HIR::Path(::HIR::SimplePath { "#FFI", { "system", "ReleaseSRWLockExclusive" } }); + fcn_p = &ffi_fcn_ptr; break; } #endif } +#endif default: LOG_ERROR("Calling value that isn't a function pointer - " << v); } @@ -1907,6 +1929,13 @@ bool InterpreterThread::call_path(Value& ret, const ::HIR::Path& path, ::std::ve ret = Value::new_i32(120); //ERROR_CALL_NOT_IMPLEMENTED return true; } + // Win32 Shared RW locks (no-op) + if( path == ::HIR::SimplePath { "std", { "sys", "windows", "c", "AcquireSRWLockExclusive" } } + || path == ::HIR::SimplePath { "std", { "sys", "windows", "c", "ReleaseSRWLockExclusive" } } + ) + { + return true; + } // - No guard page needed if( path == ::HIR::SimplePath { "std", {"sys", "imp", "thread", "guard", "init" } } @@ -1926,6 +1955,13 @@ bool InterpreterThread::call_path(Value& ret, const ::HIR::Path& path, ::std::ve } } + if( path.m_name == "" && path.m_trait.m_simplepath.crate_name == "#FFI" ) + { + const auto& link_abi = path.m_trait.m_simplepath.ents.at(0); + const auto& link_name = path.m_trait.m_simplepath.ents.at(1); + return this->call_extern(ret, link_name, link_abi, ::std::move(args)); + } + const auto& fcn = m_modtree.get_function(path); if( fcn.external.link_name != "" ) @@ -2141,6 +2177,8 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c if( ret ) { + // TODO: Get the functon name (and source library) and store in the result + // - Maybe return a FFI function pointer (::"#FFI"::DllName+ProcName) rv = Value::new_ffiptr(FFIPointer::new_void("GetProcAddress", ret)); } else @@ -2150,6 +2188,121 @@ bool InterpreterThread::call_extern(Value& rv, const ::std::string& link_name, c rv.write_usize(0,0); } } + // --- Thread-local storage + else if( link_name == "TlsAlloc" ) + { + auto key = ThreadState::s_next_tls_key ++; + + rv = Value::new_u32(key); + } + else if( link_name == "TlsGetValue" ) + { + // LPVOID TlsGetValue( DWORD dwTlsIndex ); + auto key = args.at(0).read_u32(0); + + // Get a pointer-sized value from storage + if( key < m_thread.tls_values.size() ) + { + const auto& e = m_thread.tls_values[key]; + rv = Value::new_usize(e.first); + if( e.second ) + { + rv.set_reloc(0, POINTER_SIZE, e.second); + } + } + else + { + // Return zero until populated + rv = Value::new_usize(0); + } + } + else if( link_name == "TlsSetValue" ) + { + // BOOL TlsSetValue( DWORD dwTlsIndex, LPVOID lpTlsValue ); + auto key = args.at(0).read_u32(0); + auto v = args.at(1).read_usize(0); + auto v_reloc = args.at(1).get_relocation(0); + + // Store a pointer-sized value in storage + if( key >= m_thread.tls_values.size() ) { + m_thread.tls_values.resize(key+1); + } + m_thread.tls_values[key] = ::std::make_pair(v, v_reloc); + + rv = Value::new_i32(1); + } + // --- + else if( link_name == "InitializeCriticalSection" ) + { + // HACK: Just ignore, no locks + } + else if( link_name == "EnterCriticalSection" ) + { + // HACK: Just ignore, no locks + } + else if( link_name == "TryEnterCriticalSection" ) + { + // HACK: Just ignore, no locks + rv = Value::new_i32(1); + } + else if( link_name == "LeaveCriticalSection" ) + { + // HACK: Just ignore, no locks + } + else if( link_name == "DeleteCriticalSection" ) + { + // HACK: Just ignore, no locks + } + // --- + else if( link_name == "GetStdHandle" ) + { + // HANDLE WINAPI GetStdHandle( _In_ DWORD nStdHandle ); + auto val = args.at(0).read_u32(0); + rv = Value::new_ffiptr(FFIPointer::new_void("HANDLE", GetStdHandle(val))); + } + else if( link_name == "GetConsoleMode" ) + { + // BOOL WINAPI GetConsoleMode( _In_ HANDLE hConsoleHandle, _Out_ LPDWORD lpMode ); + auto hConsoleHandle = args.at(0).read_pointer_tagged_nonnull(0, "HANDLE"); + auto lpMode_vr = args.at(1).read_pointer_valref_mut(0, sizeof(DWORD)); + LOG_DEBUG("GetConsoleMode(" << hConsoleHandle << ", " << lpMode_vr); + auto lpMode = reinterpret_cast<LPDWORD>(lpMode_vr.data_ptr_mut()); + auto rv_bool = GetConsoleMode(hConsoleHandle, lpMode); + if( rv_bool ) + { + LOG_DEBUG("= TRUE (" << *lpMode << ")"); + lpMode_vr.mark_bytes_valid(0, sizeof(DWORD)); + } + else + { + LOG_DEBUG("= FALSE"); + } + rv = Value::new_i32(rv_bool ? 1 : 0); + } + else if( link_name == "WriteConsoleW" ) + { + //BOOL WINAPI WriteConsole( _In_ HANDLE hConsoleOutput, _In_ const VOID *lpBuffer, _In_ DWORD nNumberOfCharsToWrite, _Out_ LPDWORD lpNumberOfCharsWritten, _Reserved_ LPVOID lpReserved ); + auto hConsoleOutput = args.at(0).read_pointer_tagged_nonnull(0, "HANDLE"); + auto nNumberOfCharsToWrite = args.at(2).read_u32(0); + auto lpBuffer = args.at(1).read_pointer_const(0, nNumberOfCharsToWrite * 2); + auto lpNumberOfCharsWritten_vr = args.at(3).read_pointer_valref_mut(0, sizeof(DWORD)); + auto lpReserved = args.at(4).read_usize(0); + LOG_DEBUG("WriteConsoleW(" << hConsoleOutput << ", " << lpBuffer << ", " << nNumberOfCharsToWrite << ", " << lpNumberOfCharsWritten_vr << ")"); + + auto lpNumberOfCharsWritten = reinterpret_cast<LPDWORD>(lpNumberOfCharsWritten_vr.data_ptr_mut()); + + LOG_ASSERT(lpReserved == 0, ""); + auto rv_bool = WriteConsoleW(hConsoleOutput, lpBuffer, nNumberOfCharsToWrite, lpNumberOfCharsWritten, nullptr); + if( rv_bool ) + { + LOG_DEBUG("= TRUE (" << *lpNumberOfCharsWritten << ")"); + } + else + { + LOG_DEBUG("= FALSE"); + } + rv = Value::new_i32(rv_bool ? 1 : 0); + } #else // POSIX else if( link_name == "write" ) @@ -3107,6 +3260,24 @@ bool InterpreterThread::call_intrinsic(Value& rv, const RcString& name, const :: dst_alloc.alloc().write_value(dst_vr.m_offset, ::std::move(src_val)); } } + // ---------------------------------------------------------------- + // Bit Twiddling + // --- + // cttz = CounT Trailing Zeroes + else if( name == "cttz_nonzero" ) + { + const auto& ty_T = ty_params.tys.at(0); + auto v_inner = PrimitiveValueVirt::from_value(ty_T, args.at(0)); + auto v = v_inner.get().as_u128(); + unsigned n = 0; + while( (v & 1) == 0 && n < ty_T.get_size()*8 ) + { + v = v >> static_cast<uint8_t>(1); + n ++; + } + rv = Value( HIR::TypeRef(RawType::USize) ); + rv.write_usize(0, n); + } else { LOG_TODO("Call intrinsic \"" << name << "\""); diff --git a/tools/standalone_miri/u128.hpp b/tools/standalone_miri/u128.hpp index 8403b94a..9a28c151 100644 --- a/tools/standalone_miri/u128.hpp +++ b/tools/standalone_miri/u128.hpp @@ -7,6 +7,7 @@ class U128 public: U128(): lo(0), hi(0) {} + explicit U128(uint64_t v): lo(v), hi(0) {} U128(uint8_t v): lo(v), hi(0) {} U128(int8_t v): lo(v), hi(v < 0 ? -1 : 0) {} diff --git a/tools/standalone_miri/value.cpp b/tools/standalone_miri/value.cpp index 58934200..c4cc9b59 100644 --- a/tools/standalone_miri/value.cpp +++ b/tools/standalone_miri/value.cpp @@ -273,6 +273,37 @@ void ValueCommonWrite::write_usize(size_t ofs, uint64_t v) { this->write_bytes(ofs, &v, POINTER_SIZE); } +void* ValueCommonRead::read_pointer_tagged_null(size_t rd_ofs, const char* tag) const +{ + auto ofs = read_usize(rd_ofs); + LOG_ASSERT(ofs >= Allocation::PTR_BASE, "Deref of invalid pointer"); + ofs -= Allocation::PTR_BASE; + if( ofs != 0 ) { + LOG_FATAL("Read a non-zero offset for tagged pointer"); + } + auto reloc = get_relocation(rd_ofs); + //LOG_TODO("read_pointer_tagged_null(" << rd_ofs << ", '" << tag << "')"); + if( !reloc ) + { + return nullptr; + } + else + { + switch(reloc.get_ty()) + { + case RelocationPtr::Ty::FfiPointer: { + const auto& f = reloc.ffi(); + assert(f.tag_name); + assert(tag); + if( ::std::strcmp(f.tag_name, tag) != 0 ) + LOG_FATAL("Expected a '" << tag << "' pointer, got a '" << f.tag_name << "' pointer"); + return f.ptr_value; + } + default: + LOG_FATAL("Reading a tagged pointer from non-FFI source"); + } + } +} void* ValueCommonRead::read_pointer_unsafe(size_t rd_ofs, size_t req_valid, size_t& out_size, bool& out_is_mut) const { auto ofs = read_usize(rd_ofs); diff --git a/tools/standalone_miri/value.hpp b/tools/standalone_miri/value.hpp index 03a21970..23c3ace0 100644 --- a/tools/standalone_miri/value.hpp +++ b/tools/standalone_miri/value.hpp @@ -210,6 +210,15 @@ struct ValueCommonRead /// De-reference a pointer (of target type `ty`) at the given offset, and return a reference to it ValueRef deref(size_t ofs, const ::HIR::TypeRef& ty); + /// Read a pointer that must be FFI with the specified tag (or NULL) + void* read_pointer_tagged_null(size_t rd_ofs, const char* tag) const; + /// Read a pointer that must be FFI with the specified tag (cannot be NULL) + void* read_pointer_tagged_nonnull(size_t rd_ofs, const char* tag) const { + auto rv = read_pointer_tagged_null(rd_ofs, tag); + if(!rv) + LOG_FATAL("Accessing NUL pointer"); + return rv; + } /// Read a pointer from the value, requiring at least `req_valid` valid bytes, saves avaliable space in `size` void* read_pointer_unsafe(size_t rd_ofs, size_t req_valid, size_t& size, bool& is_mut) const; /// Read a pointer, requiring `req_len` valid bytes diff --git a/vsproject/build_hello_mmir.cmd b/vsproject/build_hello_mmir.cmd index 30ac5ccc..d1a76afc 100644 --- a/vsproject/build_hello_mmir.cmd +++ b/vsproject/build_hello_mmir.cmd @@ -12,5 +12,5 @@ if %errorlevel% neq 0 exit /b %errorlevel% x64\Release\mrustc.exe ..\rustc-%RUSTC_VERSION%-src\src\test\run-pass\hello.rs -L %OUTDIR% -o %OUTDIR%\hello.exe -C codegen-type=monomir if %errorlevel% neq 0 exit /b %errorlevel% -x64\Release\standalone_miri.exe %OUTDIR%\hello.exe.mir +x64\Release\standalone_miri.exe --logfile smiri.log %OUTDIR%\hello.exe.mir if %errorlevel% neq 0 exit /b %errorlevel%
\ No newline at end of file |