summaryrefslogtreecommitdiff
path: root/src/vm/disassembler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/disassembler.cpp')
-rwxr-xr-xsrc/vm/disassembler.cpp329
1 files changed, 329 insertions, 0 deletions
diff --git a/src/vm/disassembler.cpp b/src/vm/disassembler.cpp
new file mode 100755
index 0000000000..86a277ad62
--- /dev/null
+++ b/src/vm/disassembler.cpp
@@ -0,0 +1,329 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+
+#include "disassembler.h"
+#include "dllimport.h"
+
+#if USE_DISASSEMBLER
+
+// TODO: Which contracts should be used where? Currently, everything is using LIMITED_METHOD_CONTRACT.
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+HMODULE Disassembler::s_libraryHandle = nullptr;
+InitDisasm_t *Disassembler::External_InitDisasm = nullptr;
+FinishDisasm_t *Disassembler::External_FinishDisasm = nullptr;
+DisasmInstruction_t *Disassembler::External_DisasmInstruction = nullptr;
+#endif // USE_COREDISTOOLS_DISASSEMBLER
+
+Disassembler::ExternalDisassembler *Disassembler::s_availableExternalDisassembler = nullptr;
+
+#if defined(_TARGET_AMD64_) || defined(_TARGET_X86_)
+// static
+bool Disassembler::IsRexPrefix(UINT8 potentialRexByte)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#ifdef _TARGET_AMD64_
+ return (potentialRexByte & 0xf0) == REX_PREFIX_BASE;
+#else // !_TARGET_AMD64_
+ return false;
+#endif // _TARGET_AMD64_
+}
+
+// static
+UINT8 Disassembler::DecodeModFromModRm(UINT8 modRm)
+{
+ LIMITED_METHOD_CONTRACT;
+ return modRm >> 6;
+}
+
+// static
+UINT8 Disassembler::DecodeRegOrOpCodeFromModRm(UINT8 modRm)
+{
+ LIMITED_METHOD_CONTRACT;
+ return (modRm >> 3) & 0x7;
+}
+
+// static
+UINT8 Disassembler::DecodeRmFromModRm(UINT8 modRm)
+{
+ LIMITED_METHOD_CONTRACT;
+ return modRm & 0x7;
+}
+#endif // defined(_TARGET_AMD64_) || defined(_TARGET_X86_)
+
+// static
+bool Disassembler::IsAvailable()
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+ return s_libraryHandle != nullptr;
+#else // !USE_COREDISTOOLS_DISASSEMBLER
+ return true;
+#endif // USE_COREDISTOOLS_DISASSEMBLER
+}
+
+void Disassembler::StaticInitialize()
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+ _ASSERTE(!IsAvailable());
+
+ HMODULE libraryHandle = nullptr;
+ PathString libPath;
+ DWORD result = WszGetModuleFileName(nullptr, libPath);
+ if (result == 0) {
+#ifdef _DEBUG
+ wprintf(
+ W("GetModuleFileName failed, function 'DisasmInstruction': error %u\n"),
+ GetLastError());
+#endif // _DEBUG
+ return;
+ }
+
+#if defined(FEATURE_PAL)
+ WCHAR delim = W('/');
+#else
+ WCHAR delim = W('\\');
+#endif
+ LPCWSTR libFileName = MAKEDLLNAME(W("coredistools"));
+ PathString::Iterator iter = libPath.End();
+ if (libPath.FindBack(iter, delim)) {
+ libPath.Truncate(++iter);
+ libPath.Append(libFileName);
+ }
+ else {
+ _ASSERTE(!"unreachable");
+ }
+
+ LPCWSTR libraryName = libPath.GetUnicode();
+ libraryHandle = CLRLoadLibrary(libraryName);
+ do
+ {
+ if (libraryHandle == nullptr)
+ {
+ #ifdef _DEBUG
+ wprintf(W("LoadLibrary failed for '%s': error %u\n"), libraryName, GetLastError());
+ #endif // _DEBUG
+ break;
+ }
+
+ External_InitDisasm =
+ reinterpret_cast<decltype(External_InitDisasm)>(GetProcAddress(libraryHandle, "InitDisasm"));
+ if (External_InitDisasm == nullptr)
+ {
+ #ifdef _DEBUG
+ wprintf(
+ W("GetProcAddress failed for library '%s', function 'InitDisasm': error %u\n"),
+ libraryName,
+ GetLastError());
+ #endif // _DEBUG
+ break;
+ }
+
+ External_DisasmInstruction =
+ reinterpret_cast<decltype(External_DisasmInstruction)>(GetProcAddress(libraryHandle, "DisasmInstruction"));
+ if (External_DisasmInstruction == nullptr)
+ {
+ #ifdef _DEBUG
+ wprintf(
+ W("GetProcAddress failed for library '%s', function 'DisasmInstruction': error %u\n"),
+ libraryName,
+ GetLastError());
+ #endif // _DEBUG
+ break;
+ }
+
+ External_FinishDisasm =
+ reinterpret_cast<decltype(External_FinishDisasm)>(GetProcAddress(libraryHandle, "FinishDisasm"));
+ if (External_FinishDisasm == nullptr)
+ {
+ #ifdef _DEBUG
+ wprintf(
+ W("GetProcAddress failed for library '%s', function 'FinishDisasm': error %u\n"),
+ libraryName,
+ GetLastError());
+ #endif // _DEBUG
+ break;
+ }
+
+ // Set this last to indicate successful load of the library and all exports
+ s_libraryHandle = libraryHandle;
+ _ASSERTE(IsAvailable());
+ return;
+ } while (false);
+
+ _ASSERTE(!IsAvailable());
+
+#endif // USE_COREDISTOOLS_DISASSEMBLER
+}
+
+// static
+void Disassembler::StaticClose()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (!IsAvailable())
+ {
+ return;
+ }
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+ CLRFreeLibrary(s_libraryHandle);
+ s_libraryHandle = nullptr;
+#endif
+}
+
+Disassembler::Disassembler()
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(IsAvailable());
+
+ // TODO: Is it ok to save and reuse an instance of the LLVM-based disassembler? It may later be used from a different
+ // thread, and it may be deleted from a different thread than the one from which it was created.
+
+ // Try to get an external disassembler that is already available for use before creating one
+ ExternalDisassembler *externalDisassembler =
+ FastInterlockExchangePointer(&s_availableExternalDisassembler, static_cast<ExternalDisassembler *>(nullptr));
+ if (externalDisassembler == nullptr)
+ {
+ #if USE_COREDISTOOLS_DISASSEMBLER
+ // First parameter:
+ // - Empty string for the current architecture
+ // - A string of the form "x86_64-pc-win32"
+ externalDisassembler = External_InitDisasm(Target_Host);
+ #elif USE_MSVC_DISASSEMBLER
+ #ifdef _TARGET_X86_
+ externalDisassembler = ExternalDisassembler::PdisNew(ExternalDisassembler::distX86);
+ #elif defined(_TARGET_AMD64_)
+ externalDisassembler = ExternalDisassembler::PdisNew(ExternalDisassembler::distX8664);
+ #endif // defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+ #endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+ }
+
+ _ASSERTE(externalDisassembler != nullptr);
+ m_externalDisassembler = externalDisassembler;
+}
+
+Disassembler::~Disassembler()
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(IsAvailable());
+
+ // Save the external disassembler for future use. We only save one instance, so delete a previously saved one.
+ ExternalDisassembler *externalDisassemblerToDelete =
+ FastInterlockExchangePointer(&s_availableExternalDisassembler, m_externalDisassembler);
+ if (externalDisassemblerToDelete == nullptr)
+ {
+ return;
+ }
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+ External_FinishDisasm(externalDisassemblerToDelete);
+#elif USE_MSVC_DISASSEMBLER
+ delete externalDisassemblerToDelete;
+#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+}
+
+SIZE_T Disassembler::DisassembleInstruction(const UINT8 *code, SIZE_T codeLength, InstructionType *instructionTypeRef) const
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(IsAvailable());
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+ SIZE_T instructionLength = External_DisasmInstruction(m_externalDisassembler, code, code, codeLength);
+#elif USE_MSVC_DISASSEMBLER
+ SIZE_T instructionLength =
+ m_externalDisassembler->CbDisassemble(reinterpret_cast<ExternalDisassembler::ADDR>(code), code, codeLength);
+#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+ _ASSERTE(instructionLength <= codeLength);
+
+ if (instructionTypeRef != nullptr)
+ {
+ if (instructionLength == 0)
+ {
+ *instructionTypeRef = InstructionType::Unknown;
+ }
+ else
+ {
+ #if USE_COREDISTOOLS_DISASSEMBLER
+ *instructionTypeRef = DetermineInstructionType(code, instructionLength);
+ #elif USE_MSVC_DISASSEMBLER
+ *instructionTypeRef = DetermineInstructionType(m_externalDisassembler->Trmt());
+ #endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+ }
+ }
+
+ return instructionLength;
+}
+
+// static
+InstructionType Disassembler::DetermineInstructionType(
+#if USE_COREDISTOOLS_DISASSEMBLER
+ const UINT8 *instructionCode, SIZE_T instructionCodeLength
+#elif USE_MSVC_DISASSEMBLER
+ ExternalDisassembler::TRMT terminationType
+#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+ )
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if USE_COREDISTOOLS_DISASSEMBLER
+ _ASSERTE(instructionCodeLength != 0);
+
+ SIZE_T i = 0;
+ if (Disassembler::IsRexPrefix(instructionCode[i]))
+ {
+ ++i;
+ }
+
+ switch (instructionCode[i])
+ {
+ case 0xe8: // call near rel
+ #ifdef _TARGET_X86_
+ case 0x9a: // call far ptr
+ #endif // _TARGET_X86_
+ return InstructionType::Call_DirectUnconditional;
+
+ case 0xff:
+ ++i;
+ if (i >= instructionCodeLength)
+ {
+ break;
+ }
+
+ switch (Disassembler::DecodeRegOrOpCodeFromModRm(instructionCode[i]))
+ {
+ case 2: // call near r/m
+ case 3: // call far m
+ return InstructionType::Call_IndirectUnconditional;
+
+ case 4: // jmp near r/m
+ case 5: // jmp far m
+ return InstructionType::Branch_IndirectUnconditional;
+ }
+ break;
+ }
+#elif USE_MSVC_DISASSEMBLER
+ switch (terminationType)
+ {
+ case ExternalDisassembler::trmtCall:
+ return InstructionType::Call_DirectUnconditional;
+
+ case ExternalDisassembler::trmtCallInd:
+ return InstructionType::Call_IndirectUnconditional;
+
+ case ExternalDisassembler::trmtBraInd:
+ return InstructionType::Branch_IndirectUnconditional;
+ }
+#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
+
+ return InstructionType::Unknown;
+}
+
+#endif // USE_DISASSEMBLER