diff options
Diffstat (limited to 'ceee/testing')
52 files changed, 6797 insertions, 0 deletions
diff --git a/ceee/testing/manual/ie_broker/call_broker/call_broker.cc b/ceee/testing/manual/ie_broker/call_broker/call_broker.cc new file mode 100644 index 0000000..ed51183 --- /dev/null +++ b/ceee/testing/manual/ie_broker/call_broker/call_broker.cc @@ -0,0 +1,247 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// A console app driving the IE broker to and from many threads. + +#include <atlbase.h> +#include <atlstr.h> + +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/values.h" +#include "chrome/browser/automation/extension_automation_constants.h" +#include "chrome/browser/extensions/extension_tabs_module_constants.h" +#include "ceee/common/com_utils.h" + +#include "broker_lib.h" // NOLINT + +namespace keys = extension_automation_constants; +namespace ext = extension_tabs_module_constants; + +namespace { + +// Unused WindowProc to create windows. +LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + return 1; +} + +// The number of destination thread that we will create. +static const size_t kTestSize = 222; +HWND g_windows[kTestSize]; +CHandle thread_ready[kTestSize]; + +CHandle kill_all_indexed_threads; +HMODULE g_module = NULL; + +// Faking IEFrame for the Broker. +static const wchar_t* kWindowClassName = L"IEFrame"; + +// A ThreadProc to run all the threads that create windows and set their +// HWND in g_windows[] so that they can be used as destination threads. +DWORD WINAPI IndexedWindowCreationProc(LPVOID param) { + size_t index = reinterpret_cast<size_t>(param); + printf("Starting destination thread: 0x%08x, index: %d\n", + ::GetCurrentThreadId(), index); + g_windows[index] = ::CreateWindow(kWindowClassName, NULL, 0, 0, 0, 0, 0, + NULL, NULL, NULL, 0); + DCHECK_NE(static_cast<HWND>(NULL), g_windows[index]) << + com::LogWe(::GetLastError()); + // The destination thread MUST be CoInitialized. + CoInitialize(NULL); + + // We're ready, our creator can continue. + ::SetEvent(thread_ready[index]); + MSG msg; + while (true) { + // Regular message loop for destination threads. + DWORD result = ::MsgWaitForMultipleObjects(1, &kill_all_indexed_threads.m_h, + FALSE, INFINITE, QS_POSTMESSAGE); + if (result == WAIT_OBJECT_0) { + // Our death signal! + break; + } + DCHECK_EQ(WAIT_OBJECT_0 + 1, result); + // We got woken up by a message, empty the queue. + while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { + BOOL result = ::GetMessage(&msg, NULL, 0, 0); + if (result != 0 && result != -1) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + } + } + return 0; +} + +void CreateMessage(HWND window, int request_id, std::string* message_str) { + // Create a JSON encoded message to call GetWindow for our window of interest. + DictionaryValue message; + message.SetString(keys::kAutomationNameKey, "windows.get"); + message.SetInteger(keys::kAutomationRequestIdKey, request_id); + scoped_ptr<Value> function_args(Value::CreateIntegerValue( + reinterpret_cast<int>(window))); + std::string function_args_str; + base::JSONWriter::Write(function_args.get(), false, &function_args_str); + message.SetString(keys::kAutomationArgsKey, function_args_str); + base::JSONWriter::Write(&message, false, message_str); +} + +void ValidateResponse(BSTR response, HWND window, int request_id, + bool may_have_failed) { + scoped_ptr<Value> response_value(base::JSONReader::Read( + CW2A(response).m_psz, true)); + DCHECK(response_value.get() != NULL); + DCHECK(response_value->IsType(Value::TYPE_DICTIONARY)); + DictionaryValue* response_dict = + static_cast<DictionaryValue*>(response_value.get()); + DCHECK(response_dict->HasKey(keys::kAutomationRequestIdKey)); + int response_id = -1; + response_dict->GetInteger(keys::kAutomationRequestIdKey, &response_id); + DCHECK(response_id == request_id); + if (response_dict->HasKey(keys::kAutomationErrorKey)) { + // The only error we should get is if the window isn't valid anymore + DCHECK(::IsWindow(window) == FALSE || may_have_failed); + } else { + DCHECK(response_dict->HasKey(keys::kAutomationResponseKey)); + std::string response_str; + response_dict->GetString(keys::kAutomationResponseKey, &response_str); + scoped_ptr<Value> response_value( + base::JSONReader::Read(response_str, true)); + DCHECK(response_value.get() != NULL); + DCHECK(response_value->IsType(Value::TYPE_DICTIONARY)); + DictionaryValue* dict = static_cast<DictionaryValue*>(response_value.get()); + DCHECK(dict->HasKey(ext::kIdKey)); + int window_id = 0; + dict->GetInteger(ext::kIdKey, &window_id); + DCHECK(window_id == reinterpret_cast<int>(window)); + } +} + +void ExecuteAtIndex(ICeeeBroker* broker, size_t index) { + DCHECK(index < kTestSize); + printf("Calling Execute for Window: 0x%08x, index: %d\n", + g_windows[index], index); + std::string message_str; + CreateMessage(g_windows[index], 42 + index, &message_str); + CComBSTR response; + HRESULT hr = broker->Execute(CComBSTR(CA2W(message_str.c_str()).m_psz), + &response); + DCHECK(SUCCEEDED(hr)) << com::LogHr(hr); + ValidateResponse(response, g_windows[index], 42 + index, false); +} + + +// Source threads are created to make concurrent calls to the broker. +DWORD WINAPI IndexedSourceThreadProc(LPVOID param) { + size_t index = reinterpret_cast<size_t>(param); + printf("Starting source thread: 0x%08x, for index: %d\n", + ::GetCurrentThreadId(), index); + // Of course... + ::CoInitialize(NULL); + + CComPtr<ICeeeBroker> broker; + HRESULT hr = broker.CoCreateInstance(__uuidof(CeeeBroker)); + DCHECK(SUCCEEDED(hr)) << com::LogHr(hr); + + // We aim at different destination threads to create concurrency. + // When threads 0, 1, 2, 3 are started they all call into destination thread + // index 0, same for 0-7 afterward, and 0-15. + ExecuteAtIndex(broker, index / 4); + ExecuteAtIndex(broker, index / 8); + ExecuteAtIndex(broker, index / 16); + + printf("Done with calls from thread: 0x%08x, calling execute for index: %d\n", + ::GetCurrentThreadId(), index); + + return 0; +} + +} // namespace + + +int _tmain(int argc, _TCHAR* argv[]) { + // We need the module handle to create windows. + BOOL success = ::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + reinterpret_cast<LPCTSTR>(WindowProc), &g_module); + DCHECK(success) << com::LogWe(::GetLastError()); + + WNDCLASS window_class; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hbrBackground = NULL; + window_class.hCursor = NULL; + window_class.hIcon = NULL; + window_class.hInstance = static_cast<HINSTANCE>(g_module); + window_class.lpfnWndProc = WindowProc; + window_class.lpszClassName = kWindowClassName; + window_class.lpszMenuName = NULL; + window_class.style = 0; + ATOM window_class_atom = ::RegisterClass(&window_class); + DCHECK_NE(static_cast<ATOM>(NULL), window_class_atom) << + com::LogWe(::GetLastError()); + + ::CoInitialize(NULL); + + // Needed by more than one thread so manual reset. + kill_all_indexed_threads.Attach(::CreateEvent(NULL, TRUE, FALSE, NULL)); + DCHECK_NE(static_cast<HANDLE>(NULL), kill_all_indexed_threads.m_h) << + com::LogWe(::GetLastError()); + + // We'll also make some calls from this thread... Why not? + CComPtr<ICeeeBroker> broker; + HRESULT hr = broker.CoCreateInstance(__uuidof(CeeeBroker)); + DCHECK(SUCCEEDED(hr)) << com::LogHr(hr); + + // Create all our destination threads... + for (size_t index = 0; index < kTestSize; ++index) { + thread_ready[index].Attach(::CreateEvent(NULL, FALSE, FALSE, NULL)); + CHandle thread_handle(::CreateThread(NULL, 0, IndexedWindowCreationProc, + reinterpret_cast<void*>(index), 0, NULL)); + DCHECK_NE(CHandle(), thread_handle) << com::LogWe(::GetLastError()); + DWORD result = ::WaitForSingleObject(thread_ready[index], 3000); + DCHECK_EQ(WAIT_OBJECT_0, result); + } + + // Now create 4 times as many source threads, each making a few calls. + CHandle last_thread_handle; + for (size_t index = 0; index < kTestSize * 4; ++index) { + if (index % 4 == 0) { + CComBSTR response; + // Once in a while, make a call from here... + printf("Initiating call from main thread, for index: %d\n", index); + std::string message; + CreateMessage(g_windows[index % kTestSize], index, &message); + hr = broker->Execute(CComBSTR(CA2W(message.c_str()).m_psz), + &response); + DCHECK(SUCCEEDED(hr)) << com::LogHr(hr); + ValidateResponse(response, g_windows[index % kTestSize], index, false); + printf("Done with call from main thread, for index: %d\n", index); + } else { + // And for the rest of the time, create a temporary caller thread. + CHandle src_thread_handle(::CreateThread(NULL, 0, IndexedSourceThreadProc, + reinterpret_cast<void*>(index), 0, NULL)); + } + } + + puts("Waiting to make sure ceee_broker.exe won't go away too soon"); + ::Sleep(10000); + + printf("Initiating one last call from main thread, for index: %d\n", + 42 % kTestSize); + std::string message; + CreateMessage(g_windows[42 % kTestSize], 666, &message); + CComBSTR response; + hr = broker->Execute(CComBSTR(CA2W(message.c_str()).m_psz), + &response); + DCHECK(SUCCEEDED(hr)) << com::LogHr(hr); + ValidateResponse(response, g_windows[42 % kTestSize], 666, false); + + printf("Done with call from main thread, for index: %d\n", 42 % kTestSize); + + puts("Kill all threads."); + ::SetEvent(kill_all_indexed_threads); + return 0; +} diff --git a/ceee/testing/manual/ie_broker/call_broker/call_broker.gyp b/ceee/testing/manual/ie_broker/call_broker/call_broker.gyp new file mode 100644 index 0000000..6021b60 --- /dev/null +++ b/ceee/testing/manual/ie_broker/call_broker/call_broker.gyp @@ -0,0 +1,35 @@ +# Copyright (c) 2010 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + '../../../../../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'call_broker', + 'type': 'executable', + 'sources': [ + 'call_broker.cc', + 'precompile.cc', + 'precompile.h', + ], + 'configurations': { + 'Debug': { + 'msvs_precompiled_header': 'precompile.h', + 'msvs_precompiled_source': 'precompile.cc', + }, + }, + 'defines': [ + '_CONSOLE', + ], + 'dependencies': [ + '<(DEPTH)/ceee/ie/plugin/toolband/toolband.gyp:toolband_idl', + '<(DEPTH)/ceee/ie/common/common.gyp:ie_common', + '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/ceee/common/common.gyp:ceee_common', + ], + }, + ] +} diff --git a/ceee/testing/manual/ie_broker/call_broker/precompile.cc b/ceee/testing/manual/ie_broker/call_broker/precompile.cc new file mode 100644 index 0000000..76d5c35 --- /dev/null +++ b/ceee/testing/manual/ie_broker/call_broker/precompile.cc @@ -0,0 +1,6 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Precompile generator file. +#include "ceee/testing/manual/ie_broker/call_broker/precompile.h" diff --git a/ceee/testing/manual/ie_broker/call_broker/precompile.h b/ceee/testing/manual/ie_broker/call_broker/precompile.h new file mode 100644 index 0000000..ca9238e --- /dev/null +++ b/ceee/testing/manual/ie_broker/call_broker/precompile.h @@ -0,0 +1,13 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Precompile header for IE broker manual test to call the broker. + +#ifndef CEEE_TESTING_MANUAL_IE_BROKER_CALL_BROKER_PRECOMPILE_H_ +#define CEEE_TESTING_MANUAL_IE_BROKER_CALL_BROKER_PRECOMPILE_H_ + +#include <stdio.h> +#include <tchar.h> + +#endif // CEEE_TESTING_MANUAL_IE_BROKER_CALL_BROKER_PRECOMPILE_H_ diff --git a/ceee/testing/sidestep/auto_testing_hook.h b/ceee/testing/sidestep/auto_testing_hook.h new file mode 100644 index 0000000..33f3ba2 --- /dev/null +++ b/ceee/testing/sidestep/auto_testing_hook.h @@ -0,0 +1,128 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Utility for using SideStep with unit tests. + +#ifndef CEEE_TESTING_SIDESTEP_AUTO_TESTING_HOOK_H_ +#define CEEE_TESTING_SIDESTEP_AUTO_TESTING_HOOK_H_ + +#include "base/basictypes.h" +#include "ceee/testing/sidestep/integration.h" +#include "ceee/testing/sidestep/preamble_patcher.h" + +namespace sidestep { + +// Same trick as common/scope_cleanup.h ScopeGuardImplBase +class AutoTestingHookBase { + public: + virtual ~AutoTestingHookBase() {} +}; + +// This is the typedef you normally use for the class, e.g. +// +// AutoTestingHook hook = MakeTestingHook(TargetFunc, HookTargetFunc); +// +// The 'hook' variable will then be destroyed when it goes out of scope. +// +// NOTE: You must not hold this type as a member of another class. Its +// destructor will not get called. +typedef const AutoTestingHookBase& AutoTestingHook; + +// This is the class you must use when holding a hook as a member of another +// class, e.g. +// +// public: +// AutoTestingHookHolder holder_; +// MyClass() : my_hook_holder(MakeTestingHookHolder(Target, Hook)) {} +class AutoTestingHookHolder { + public: + explicit AutoTestingHookHolder(AutoTestingHookBase* hook) : hook_(hook) {} + ~AutoTestingHookHolder() { delete hook_; } + private: + AutoTestingHookHolder() {} // disallow + AutoTestingHookBase* hook_; +}; + +// This class helps patch a function, then unpatch it when the object exits +// scope, and also maintains the pointer to the original function stub. +// +// To enable use of the class without having to explicitly provide the +// type of the function pointers (and instead only providing it +// implicitly) we use the same trick as ScopeGuard (see +// common/scope_cleanup.h) uses, so to create a hook you use the MakeHook +// function rather than a constructor. +// +// NOTE: This function is only safe for e.g. unit tests and _not_ for +// production code. See PreamblePatcher class for details. +template <typename T> +class AutoTestingHookImpl : public AutoTestingHookBase { + public: + static AutoTestingHookImpl<T> MakeTestingHook(T target_function, + T replacement_function, + bool do_it) { + return AutoTestingHookImpl<T>(target_function, replacement_function, do_it); + } + + static AutoTestingHookImpl<T>* MakeTestingHookHolder(T target_function, + T replacement_function, + bool do_it) { + return new AutoTestingHookImpl<T>(target_function, + replacement_function, do_it); + } + + ~AutoTestingHookImpl() { + if (did_it_) { + SIDESTEP_CHK(SIDESTEP_SUCCESS == PreamblePatcher::Unpatch( + target_function_, replacement_function_, + original_function_)); + } + } + + // Returns a pointer to the original function. To use this method you will + // have to explicitly create an AutoTestingHookImpl of the specific + // function pointer type (i.e. not use the AutoTestingHook typedef). + T original_function() { + return original_function_; + } + + private: + AutoTestingHookImpl(T target_function, T replacement_function, bool do_it) + : target_function_(target_function), + replacement_function_(replacement_function), + original_function_(NULL), + did_it_(do_it) { + if (do_it) { + SIDESTEP_CHK(SIDESTEP_SUCCESS == PreamblePatcher::Patch(target_function, + replacement_function, + &original_function_)); + } + } + + T target_function_; // always valid + T original_function_; // always valid + T replacement_function_; // always valid + bool did_it_; // Remember if we did it or not... +}; + +template <typename T> +inline AutoTestingHookImpl<T> MakeTestingHook(T target, + T replacement, + bool do_it) { + return AutoTestingHookImpl<T>::MakeTestingHook(target, replacement, do_it); +} + +template <typename T> +inline AutoTestingHookImpl<T> MakeTestingHook(T target, T replacement) { + return AutoTestingHookImpl<T>::MakeTestingHook(target, replacement, true); +} + +template <typename T> +inline AutoTestingHookImpl<T>* MakeTestingHookHolder(T target, T replacement) { + return AutoTestingHookImpl<T>::MakeTestingHookHolder(target, replacement, + true); +} + +}; // namespace sidestep + +#endif // CEEE_TESTING_SIDESTEP_AUTO_TESTING_HOOK_H_ diff --git a/ceee/testing/sidestep/documentation.h b/ceee/testing/sidestep/documentation.h new file mode 100644 index 0000000..00ca47f --- /dev/null +++ b/ceee/testing/sidestep/documentation.h @@ -0,0 +1,20 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// SideStep SideStep Library Overview +// +// The SideStep library includes two main components: +// -# A preamble patching utility, enabling patching of most +// functions. See PreamblePatcher. +// -# A mini disassembler, used by the preamble patching utility. See +// MiniDisassembler. +// +// To use the library, see integration.h; at a high level, you need to: +// -# Link in a function sidestep::AssertImpl or explicitly define +// the SIDESTEP_ASSERT macro. +// -# Link in a function sidestep::LogImpl or explicitly define the +// SIDESTEP_LOG macro. +// -# Optional: Call sidestep::UnitTests() from from a unit test in your +// code and check that it returns true (otherwise the unit test +// found an error). diff --git a/ceee/testing/sidestep/ia32_modrm_map.cc b/ceee/testing/sidestep/ia32_modrm_map.cc new file mode 100644 index 0000000..bfb19a8e --- /dev/null +++ b/ceee/testing/sidestep/ia32_modrm_map.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Table of relevant information about how to decode the ModR/M byte. +// Based on information in the IA-32 Intel® Architecture +// Software Developer’s Manual Volume 2: Instruction Set Reference. + +#include "ceee/testing/sidestep/mini_disassembler.h" +#include "ceee/testing/sidestep/mini_disassembler_types.h" + +namespace sidestep { + +const ModrmEntry MiniDisassembler::s_ia16_modrm_map_[] = { +// mod == 00 + /* r/m == 000 */ { false, false, OS_ZERO }, + /* r/m == 001 */ { false, false, OS_ZERO }, + /* r/m == 010 */ { false, false, OS_ZERO }, + /* r/m == 011 */ { false, false, OS_ZERO }, + /* r/m == 100 */ { false, false, OS_ZERO }, + /* r/m == 101 */ { false, false, OS_ZERO }, + /* r/m == 110 */ { true, false, OS_WORD }, + /* r/m == 111 */ { false, false, OS_ZERO }, +// mod == 01 + /* r/m == 000 */ { true, false, OS_BYTE }, + /* r/m == 001 */ { true, false, OS_BYTE }, + /* r/m == 010 */ { true, false, OS_BYTE }, + /* r/m == 011 */ { true, false, OS_BYTE }, + /* r/m == 100 */ { true, false, OS_BYTE }, + /* r/m == 101 */ { true, false, OS_BYTE }, + /* r/m == 110 */ { true, false, OS_BYTE }, + /* r/m == 111 */ { true, false, OS_BYTE }, +// mod == 10 + /* r/m == 000 */ { true, false, OS_WORD }, + /* r/m == 001 */ { true, false, OS_WORD }, + /* r/m == 010 */ { true, false, OS_WORD }, + /* r/m == 011 */ { true, false, OS_WORD }, + /* r/m == 100 */ { true, false, OS_WORD }, + /* r/m == 101 */ { true, false, OS_WORD }, + /* r/m == 110 */ { true, false, OS_WORD }, + /* r/m == 111 */ { true, false, OS_WORD }, +// mod == 11 + /* r/m == 000 */ { false, false, OS_ZERO }, + /* r/m == 001 */ { false, false, OS_ZERO }, + /* r/m == 010 */ { false, false, OS_ZERO }, + /* r/m == 011 */ { false, false, OS_ZERO }, + /* r/m == 100 */ { false, false, OS_ZERO }, + /* r/m == 101 */ { false, false, OS_ZERO }, + /* r/m == 110 */ { false, false, OS_ZERO }, + /* r/m == 111 */ { false, false, OS_ZERO } +}; + +const ModrmEntry MiniDisassembler::s_ia32_modrm_map_[] = { +// mod == 00 + /* r/m == 000 */ { false, false, OS_ZERO }, + /* r/m == 001 */ { false, false, OS_ZERO }, + /* r/m == 010 */ { false, false, OS_ZERO }, + /* r/m == 011 */ { false, false, OS_ZERO }, + /* r/m == 100 */ { false, true, OS_ZERO }, + /* r/m == 101 */ { true, false, OS_DOUBLE_WORD }, + /* r/m == 110 */ { false, false, OS_ZERO }, + /* r/m == 111 */ { false, false, OS_ZERO }, +// mod == 01 + /* r/m == 000 */ { true, false, OS_BYTE }, + /* r/m == 001 */ { true, false, OS_BYTE }, + /* r/m == 010 */ { true, false, OS_BYTE }, + /* r/m == 011 */ { true, false, OS_BYTE }, + /* r/m == 100 */ { true, true, OS_BYTE }, + /* r/m == 101 */ { true, false, OS_BYTE }, + /* r/m == 110 */ { true, false, OS_BYTE }, + /* r/m == 111 */ { true, false, OS_BYTE }, +// mod == 10 + /* r/m == 000 */ { true, false, OS_DOUBLE_WORD }, + /* r/m == 001 */ { true, false, OS_DOUBLE_WORD }, + /* r/m == 010 */ { true, false, OS_DOUBLE_WORD }, + /* r/m == 011 */ { true, false, OS_DOUBLE_WORD }, + /* r/m == 100 */ { true, true, OS_DOUBLE_WORD }, + /* r/m == 101 */ { true, false, OS_DOUBLE_WORD }, + /* r/m == 110 */ { true, false, OS_DOUBLE_WORD }, + /* r/m == 111 */ { true, false, OS_DOUBLE_WORD }, +// mod == 11 + /* r/m == 000 */ { false, false, OS_ZERO }, + /* r/m == 001 */ { false, false, OS_ZERO }, + /* r/m == 010 */ { false, false, OS_ZERO }, + /* r/m == 011 */ { false, false, OS_ZERO }, + /* r/m == 100 */ { false, false, OS_ZERO }, + /* r/m == 101 */ { false, false, OS_ZERO }, + /* r/m == 110 */ { false, false, OS_ZERO }, + /* r/m == 111 */ { false, false, OS_ZERO }, +}; + +}; // namespace sidestep diff --git a/ceee/testing/sidestep/ia32_opcode_map.cc b/ceee/testing/sidestep/ia32_opcode_map.cc new file mode 100644 index 0000000..bfb094c --- /dev/null +++ b/ceee/testing/sidestep/ia32_opcode_map.cc @@ -0,0 +1,1159 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Opcode decoding maps. Based on the IA-32 Intel® Architecture +// Software Developer's Manual Volume 2: Instruction Set Reference. Idea +// for how to lay out the tables in memory taken from the implementation +// in the Bastard disassembly environment. + +#include "ceee/testing/sidestep/mini_disassembler.h" + +namespace sidestep { + +/* +* This is the first table to be searched; the first field of each +* Opcode in the table is either 0 to indicate you're in the +* right table, or an index to the correct table, in the global +* map g_pentiumOpcodeMap +*/ +const Opcode s_first_opcode_byte[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF */ { 1, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x10 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x11 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x12 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x13 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x14 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x15 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x16 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x17 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x18 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x19 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1E */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1F */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x20 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x21 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x22 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x23 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x24 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x25 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x26 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x27 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "daa", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x28 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x29 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2E */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "das", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x30 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x31 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x32 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x33 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x34 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x35 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x36 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x37 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "aaa", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x38 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x39 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3E */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "aas", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x40 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x41 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x42 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x43 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x44 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x45 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x46 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x47 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x48 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x49 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4A */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4B */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4C */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4E */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x50 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x51 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x52 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x53 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x54 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x55 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x56 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x57 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x58 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x59 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5A */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5B */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5C */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5E */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x60 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "pushad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x61 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "popad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x62 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_A, AM_NOT_USED, "bound", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x63 */ { 0, IT_GENERIC, AM_E | OT_W, AM_G | OT_W, AM_NOT_USED, "arpl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x64 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x65 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x66 */ { 0, IT_PREFIX_OPERAND, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x67 */ { 0, IT_PREFIX_ADDRESS, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x68 */ { 0, IT_GENERIC, AM_I | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x69 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_I | OT_V, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6A */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_I | OT_B, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6C */ { 0, IT_GENERIC, AM_Y | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "insb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6D */ { 0, IT_GENERIC, AM_Y | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "insd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6E */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_X | OT_B, AM_NOT_USED, "outsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_X | OT_V, AM_NOT_USED, "outsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x70 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x71 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x72 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x73 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x74 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x75 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x76 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x77 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "ja", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x78 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "js", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x79 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7A */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7B */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7C */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7D */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7E */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7F */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x80 */ { 2, IT_REFERENCE, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x81 */ { 3, IT_REFERENCE, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x82 */ { 4, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x83 */ { 5, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x84 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x85 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x86 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x87 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x88 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x89 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8C */ { 0, IT_GENERIC, AM_E | OT_W, AM_S | OT_W, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8D */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_ADDRESS_MODE_M, AM_NOT_USED, "lea", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8E */ { 0, IT_GENERIC, AM_S | OT_W, AM_E | OT_W, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8F */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x90 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "nop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x91 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x92 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x93 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x94 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x95 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x96 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x97 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x98 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cwde", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x99 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cdq", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9A */ { 0, IT_JUMP, AM_A | OT_P, AM_NOT_USED, AM_NOT_USED, "callf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9B */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wait", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9C */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "pushfd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9D */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "popfd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9E */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sahf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lahf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA0 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_O | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA1 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_O | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA2 */ { 0, IT_GENERIC, AM_O | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA3 */ { 0, IT_GENERIC, AM_O | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA4 */ { 0, IT_GENERIC, AM_X | OT_B, AM_Y | OT_B, AM_NOT_USED, "movsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA5 */ { 0, IT_GENERIC, AM_X | OT_V, AM_Y | OT_V, AM_NOT_USED, "movsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA6 */ { 0, IT_GENERIC, AM_X | OT_B, AM_Y | OT_B, AM_NOT_USED, "cmpsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA7 */ { 0, IT_GENERIC, AM_X | OT_V, AM_Y | OT_V, AM_NOT_USED, "cmpsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA8 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA9 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAA */ { 0, IT_GENERIC, AM_Y | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "stosb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAB */ { 0, IT_GENERIC, AM_Y | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "stosd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_X| OT_B, AM_NOT_USED, "lodsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_X| OT_V, AM_NOT_USED, "lodsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAE */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_Y | OT_B, AM_NOT_USED, "scasb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAF */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_Y | OT_V, AM_NOT_USED, "scasd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB0 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB1 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB2 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB3 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB5 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB6 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB7 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB8 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB9 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBA */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBB */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBC */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBE */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBF */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC0 */ { 6, IT_REFERENCE, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC1 */ { 7, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC2 */ { 0, IT_RETURN, AM_I | OT_W, AM_NOT_USED, AM_NOT_USED, "ret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC3 */ { 0, IT_RETURN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC4 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_P, AM_NOT_USED, "les", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC5 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_P, AM_NOT_USED, "lds", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC8 */ { 0, IT_GENERIC, AM_I | OT_W, AM_I | OT_B, AM_NOT_USED, "enter", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "leave", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCA */ { 0, IT_RETURN, AM_I | OT_W, AM_NOT_USED, AM_NOT_USED, "retf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCB */ { 0, IT_RETURN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "retf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCC */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "int3", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCD */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "int", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCE */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "into", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCF */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "iret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD0 */ { 8, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD1 */ { 9, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD2 */ { 10, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD3 */ { 11, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD4 */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "aam", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD5 */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "aad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD7 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "xlat", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + + // The following 8 lines would be references to the FPU tables, but we currently + // do not support the FPU instructions in this disassembler. + + /* 0xD8 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD9 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xDA */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xDB */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xDC */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xDD */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xDE */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xDF */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + + + /* 0xE0 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loopnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE1 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loopz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE2 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE3 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jcxz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE5 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE6 */ { 0, IT_GENERIC, AM_I | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE7 */ { 0, IT_GENERIC, AM_I | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE8 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE9 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xEA */ { 0, IT_JUMP, AM_A | OT_P, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xEB */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xEC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_REGISTER | OT_W, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xED */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_W, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xEE */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xEF */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_REGISTER | OT_V, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF0 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lock:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF2 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "repne:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF3 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rep:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF4 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "hlt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF5 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cmc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF6 */ { 12, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF7 */ { 13, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF8 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "stc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xFA */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cli", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xFB */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xFC */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xFD */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "std", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xFE */ { 14, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xFF */ { 15, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0f[] = { + /* 0x0 */ { 16, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 17, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "lar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "lsl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "invd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wbinvd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ud2", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xE */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x10 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "movups", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "movsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "movss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "movupd" } }, + /* 0x11 */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movups", true, + /* F2h */ { 0, IT_GENERIC, AM_W | OT_SD, AM_V | OT_SD, AM_NOT_USED, "movsd" }, + /* F3h */ { 0, IT_GENERIC, AM_W | OT_SS, AM_V | OT_SS, AM_NOT_USED, "movss" }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movupd" } }, + /* 0x12 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhlps" }, // only one of ... + /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhlps" }, // ...these two is correct, Intel doesn't specify which + /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_S, AM_NOT_USED, "movlpd" } }, + /* 0x13 */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movlps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movlpd" } }, + /* 0x14 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_Q, AM_NOT_USED, "unpcklps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_Q, AM_NOT_USED, "unpcklpd" } }, + /* 0x15 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_Q, AM_NOT_USED, "unpckhps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_Q, AM_NOT_USED, "unpckhpd" } }, + /* 0x16 */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movhps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlhps" }, // only one of... + /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlhps" }, // ...these two is correct, Intel doesn't specify which + /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movhpd" } }, + /* 0x17 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhpd" } }, + /* 0x18 */ { 18, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x19 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1A */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1B */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1C */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1D */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1E */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1F */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x20 */ { 0, IT_GENERIC, AM_R | OT_D, AM_C | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x21 */ { 0, IT_GENERIC, AM_R | OT_D, AM_D | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x22 */ { 0, IT_GENERIC, AM_C | OT_D, AM_R | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x23 */ { 0, IT_GENERIC, AM_D | OT_D, AM_R | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x24 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x25 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x26 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x27 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x28 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "movaps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "movapd" } }, + /* 0x29 */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movaps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movapd" } }, + /* 0x2A */ { 0, IT_GENERIC, AM_V | OT_PS, AM_Q | OT_Q, AM_NOT_USED, "cvtpi2ps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_E | OT_D, AM_NOT_USED, "cvtsi2sd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_E | OT_D, AM_NOT_USED, "cvtsi2ss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_Q | OT_DQ, AM_NOT_USED, "cvtpi2pd" } }, + /* 0x2B */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movntps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movntpd" } }, + /* 0x2C */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_W | OT_PS, AM_NOT_USED, "cvttps2pi", true, + /* F2h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SD, AM_NOT_USED, "cvttsd2si" }, + /* F3h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SS, AM_NOT_USED, "cvttss2si" }, + /* 66h */ { 0, IT_GENERIC, AM_Q | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvttpd2pi" } }, + /* 0x2D */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_W | OT_PS, AM_NOT_USED, "cvtps2pi", true, + /* F2h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SD, AM_NOT_USED, "cvtsd2si" }, + /* F3h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SS, AM_NOT_USED, "cvtss2si" }, + /* 66h */ { 0, IT_GENERIC, AM_Q | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvtpd2pi" } }, + /* 0x2E */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "ucomiss", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "ucomisd" } }, + /* 0x2F */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_SS, AM_NOT_USED, "comiss", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "comisd" } }, + /* 0x30 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wrmsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x31 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdtsc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x32 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdmsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x33 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdpmc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x34 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sysenter", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x35 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sysexit", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x36 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x37 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x38 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x39 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3A */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3B */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3C */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "movnti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3D */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3E */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3F */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x40 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x41 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x42 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x43 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x44 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x45 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x46 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x47 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmova", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x48 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x49 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4A */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4C */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4D */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4E */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4F */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x50 */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_PS, AM_NOT_USED, "movmskps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_PD, AM_NOT_USED, "movmskpd" } }, + /* 0x51 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "sqrtps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "sqrtsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "sqrtss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "sqrtpd" } }, + /* 0x52 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "rsqrtps", true, + /* F2h */ { 0 }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "rsqrtss" }, + /* 66h */ { 0 } }, + /* 0x53 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "rcpps", true, + /* F2h */ { 0 }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "rcpss" }, + /* 66h */ { 0 } }, + /* 0x54 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "andps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "andpd" } }, + /* 0x55 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "andnps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "andnpd" } }, + /* 0x56 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "orps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "orpd" } }, + /* 0x57 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "xorps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "xorpd" } }, + /* 0x58 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "addps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "addsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "addss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "addpd" } }, + /* 0x59 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "mulps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "mulsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "mulss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "mulpd" } }, + /* 0x5A */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PS, AM_NOT_USED, "cvtps2pd", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "cvtsd2ss" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "cvtss2sd" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PD, AM_NOT_USED, "cvtpd2ps" } }, + /* 0x5B */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_DQ, AM_NOT_USED, "cvtdq2ps", true, + /* F2h */ { 0 }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PS, AM_NOT_USED, "cvttps2dq" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PS, AM_NOT_USED, "cvtps2dq" } }, + /* 0x5C */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "subps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "subsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "subss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "subpd" } }, + /* 0x5D */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "minps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "minsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "minss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "minpd" } }, + /* 0x5E */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "divps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "divsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "divss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "divpd" } }, + /* 0x5F */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "maxps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "maxsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "maxss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "maxpd" } }, + /* 0x60 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpcklbw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklbw" } }, + /* 0x61 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpcklwd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklwd" } }, + /* 0x62 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckldq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpckldq" } }, + /* 0x63 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packsswb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "packsswb" } }, + /* 0x64 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtb" } }, + /* 0x65 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtw" } }, + /* 0x66 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtd" } }, + /* 0x67 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packuswb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "packuswb" } }, + /* 0x68 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhbw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhbw" } }, + /* 0x69 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhwd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhwd" } }, + /* 0x6A */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhdq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhdq" } }, + /* 0x6B */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packssdw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "packssdw" } }, + /* 0x6C */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklqdq" } }, + /* 0x6D */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklqdq" } }, + /* 0x6E */ { 0, IT_GENERIC, AM_P | OT_D, AM_E | OT_D, AM_NOT_USED, "movd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_E | OT_D, AM_NOT_USED, "movd" } }, + /* 0x6F */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "movq", true, + /* F2h */ { 0 }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "movdqu" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "movdqa" } }, + /* 0x70 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_I | OT_B, "pshuf", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshuflw" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshufhw" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshufd" } }, + /* 0x71 */ { 19, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x72 */ { 20, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x73 */ { 21, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x74 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqb" } }, + /* 0x75 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqw" } }, + /* 0x76 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqd" } }, + /* 0x77 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "emms", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + + // The following six opcodes are escapes into the MMX stuff, which this disassembler does not support. + /* 0x78 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x79 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7A */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7B */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7C */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7D */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + + /* 0x7E */ { 0, IT_GENERIC, AM_E | OT_D, AM_P | OT_D, AM_NOT_USED, "movd", true, + /* F2h */ { 0 }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movq" }, + /* 66h */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_DQ, AM_NOT_USED, "movd" } }, + /* 0x7F */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_P | OT_Q, AM_NOT_USED, "movq", true, + /* F2h */ { 0 }, + /* F3h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movdqu" }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movdqa" } }, + /* 0x80 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x81 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x82 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x83 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x84 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x85 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x86 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x87 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "ja", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x88 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "js", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x89 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8A */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8B */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8C */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8D */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8E */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x8F */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x90 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "seto", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x91 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x92 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x93 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x94 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x95 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x96 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x97 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "seta", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x98 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "sets", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x99 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9A */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9B */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9C */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9D */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9E */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x9F */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA0 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA1 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA2 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cpuid", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "bt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B, "shld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B | AM_REGISTER, "shld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA6 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA7 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA8 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xA9 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAA */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rsm", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAB */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "bts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAC */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B, "shrd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAD */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B | AM_REGISTER, "shrd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAE */ { 22, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xAF */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "cmpxchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "cmpxchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB2 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lss", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "btr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB4 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lfs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB5 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lgs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB6 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_B, AM_NOT_USED, "movzx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB7 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "movzx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB8 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xB9 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ud1", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBA */ { 23, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBB */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "btc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBC */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "bsf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBD */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "bsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBE */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_B, AM_NOT_USED, "movsx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xBF */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "movsx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xadd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "xadd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC2 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_I | OT_B, "cmpps", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_I | OT_B, "cmpsd" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_I | OT_B, "cmpss" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_I | OT_B, "cmppd" } }, + /* 0xC3 */ { 0, IT_GENERIC, AM_E | OT_D, AM_G | OT_D, AM_NOT_USED, "movnti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_E | OT_D, AM_I | OT_B, "pinsrw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_E | OT_D, AM_I | OT_B, "pinsrw" } }, + /* 0xC5 */ { 0, IT_GENERIC, AM_G | OT_D, AM_P | OT_Q, AM_I | OT_B, "pextrw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_G | OT_D, AM_V | OT_DQ, AM_I | OT_B, "pextrw" } }, + /* 0xC6 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_I | OT_B, "shufps", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_I | OT_B, "shufpd" } }, + /* 0xC7 */ { 24, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC8 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xC9 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCA */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCB */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCC */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCD */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCE */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xCF */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xD1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrlw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrlw" } }, + /* 0xD2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrld", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrld" } }, + /* 0xD3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrlq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrlq" } }, + /* 0xD4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddq" } }, + /* 0xD5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmullw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmullw" } }, + /* 0xD6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "unused without prefix", true, + /* F2h */ { 0, IT_GENERIC, AM_P | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movdq2q" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_Q | OT_Q, AM_NOT_USED, "movq2dq" }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movq" } }, + /* 0xD7 */ { 0, IT_GENERIC, AM_G | OT_D, AM_P | OT_Q, AM_NOT_USED, "pmovmskb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_G | OT_D, AM_V | OT_DQ, AM_NOT_USED, "pmovmskb" } }, + /* 0xD8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubusb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubusb" } }, + /* 0xD9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubusw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubusw" } }, + /* 0xDA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pminub", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pminub" } }, + /* 0xDB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pand", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pand" } }, + /* 0xDC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddusb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddusb" } }, + /* 0xDD */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddusw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddusw" } }, + /* 0xDE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaxub", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaxub" } }, + /* 0xDF */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pandn", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pandn" } }, + /* 0xE0 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pavgb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pavgb" } }, + /* 0xE1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psraw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrqw" } }, + /* 0xE2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrad", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrad" } }, + /* 0xE3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pavgw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pavgw" } }, + /* 0xE4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmulhuw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmulhuw" } }, + /* 0xE5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmulhuw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmulhw" } }, + /* 0xE6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true, + /* F2h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvtpd2dq" }, + /* F3h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_DQ, AM_NOT_USED, "cvtdq2pd" }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvttpd2dq" } }, + /* 0xE7 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movntq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movntdq" } }, + /* 0xE8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubsb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubsb" } }, + /* 0xE9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubsw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubsw" } }, + /* 0xEA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pminsw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pminsw" } }, + /* 0xEB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "por", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "por" } }, + /* 0xEC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddsb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddsb" } }, + /* 0xED */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddsw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddsw" } }, + /* 0xEE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaxsw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaxsw" } }, + /* 0xEF */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pxor", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pxor" } }, + /* 0xF0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0xF1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psllw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psllw" } }, + /* 0xF2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pslld", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pslld" } }, + /* 0xF3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psllq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psllq" } }, + /* 0xF4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmuludq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmuludq" } }, + /* 0xF5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaddwd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaddwd" } }, + /* 0xF6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psadbw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psadbw" } }, + /* 0xF7 */ { 0, IT_GENERIC, AM_P | OT_PI, AM_Q | OT_PI, AM_NOT_USED, "maskmovq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "maskmovdqu" } }, + /* 0xF8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubb" } }, + /* 0xF9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubw" } }, + /* 0xFA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubd" } }, + /* 0xFB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubq" } }, + /* 0xFC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddb", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddb" } }, + /* 0xFD */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddw" } }, + /* 0xFE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddd", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddd" } }, + /* 0xFF */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0f00[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "sldt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "str", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "lldt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "ltr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "verr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "verw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0f01[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "sgdt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "sidt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "lgdt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "lidt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "smsw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "lmsw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_M | OT_B, AM_NOT_USED, AM_NOT_USED, "invlpg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0f18[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_M | OT_ADDRESS_MODE_M, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0f71[] = { + /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrlw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrlw" } }, + /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psraw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psraw" } }, + /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psllw", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psllw" } }, + /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0f72[] = { + /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrld", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrld" } }, + /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrad", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrad" } }, + /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "pslld", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslld" } }, + /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0f73[] = { + /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrlq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrlq" } }, + /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psllq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psllq" } }, + /* 0x7 */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslldq", true, + /* F2h */ { 0 }, + /* F3h */ { 0 }, + /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslldq" } }, +}; + +const Opcode s_opcode_byte_after_0fae[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "fxsave", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "fxrstor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ldmxcsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "stmxcsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "mfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clflush/sfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, +}; + +const Opcode s_opcode_byte_after_0fba[] = { + /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "bt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "bts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "btr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "btc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_0fc7[] = { + /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_M | OT_Q, AM_NOT_USED, AM_NOT_USED, "cmpxch8b", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_80[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_81[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_82[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_83[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_c0[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_c1[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_d0[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_d1[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_d2[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_d3[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_f6[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "not", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "neg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, OT_B | AM_REGISTER, AM_E | OT_B, AM_NOT_USED, "mul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, OT_B | AM_REGISTER, AM_E | OT_B, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_E | OT_B, AM_NOT_USED, "div", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_E | OT_B, AM_NOT_USED, "idiv", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_f7[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "not", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "neg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "mul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "div", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "idiv", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_fe[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +const Opcode s_opcode_byte_after_ff[] = { + /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x2 */ { 0, IT_JUMP, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x3 */ { 0, IT_JUMP, AM_E | OT_P, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x4 */ { 0, IT_JUMP, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x5 */ { 0, IT_JUMP, AM_E | OT_P, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, + /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } +}; + +/* +* A table of all the other tables, containing some extra information, e.g. +* how to mask out the byte we're looking at. +*/ +const OpcodeTable MiniDisassembler::s_ia32_opcode_map_[] = { + // One-byte opcodes and jumps to larger + /* 0 */ {s_first_opcode_byte, 0, 0xff, 0, 0xff}, + // Two-byte opcodes (second byte) + /* 1 */ {s_opcode_byte_after_0f, 0, 0xff, 0, 0xff}, + // Start of tables for opcodes using ModR/M bits as extension + /* 2 */ {s_opcode_byte_after_80, 3, 0x07, 0, 0x07}, + /* 3 */ {s_opcode_byte_after_81, 3, 0x07, 0, 0x07}, + /* 4 */ {s_opcode_byte_after_82, 3, 0x07, 0, 0x07}, + /* 5 */ {s_opcode_byte_after_83, 3, 0x07, 0, 0x07}, + /* 6 */ {s_opcode_byte_after_c0, 3, 0x07, 0, 0x07}, + /* 7 */ {s_opcode_byte_after_c1, 3, 0x07, 0, 0x07}, + /* 8 */ {s_opcode_byte_after_d0, 3, 0x07, 0, 0x07}, + /* 9 */ {s_opcode_byte_after_d1, 3, 0x07, 0, 0x07}, + /* 10 */ {s_opcode_byte_after_d2, 3, 0x07, 0, 0x07}, + /* 11 */ {s_opcode_byte_after_d3, 3, 0x07, 0, 0x07}, + /* 12 */ {s_opcode_byte_after_f6, 3, 0x07, 0, 0x07}, + /* 13 */ {s_opcode_byte_after_f7, 3, 0x07, 0, 0x07}, + /* 14 */ {s_opcode_byte_after_fe, 3, 0x07, 0, 0x01}, + /* 15 */ {s_opcode_byte_after_ff, 3, 0x07, 0, 0x07}, + /* 16 */ {s_opcode_byte_after_0f00, 3, 0x07, 0, 0x07}, + /* 17 */ {s_opcode_byte_after_0f01, 3, 0x07, 0, 0x07}, + /* 18 */ {s_opcode_byte_after_0f18, 3, 0x07, 0, 0x07}, + /* 19 */ {s_opcode_byte_after_0f71, 3, 0x07, 0, 0x07}, + /* 20 */ {s_opcode_byte_after_0f72, 3, 0x07, 0, 0x07}, + /* 21 */ {s_opcode_byte_after_0f73, 3, 0x07, 0, 0x07}, + /* 22 */ {s_opcode_byte_after_0fae, 3, 0x07, 0, 0x07}, + /* 23 */ {s_opcode_byte_after_0fba, 3, 0x07, 0, 0x07}, + /* 24 */ {s_opcode_byte_after_0fc7, 3, 0x07, 0, 0x01} +}; + +}; // namespace sidestep diff --git a/ceee/testing/sidestep/integration.h b/ceee/testing/sidestep/integration.h new file mode 100644 index 0000000..f1239cf --- /dev/null +++ b/ceee/testing/sidestep/integration.h @@ -0,0 +1,63 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Defines functions/macros that can and/or need to be defined by the +// user of this library, and a unit test function that can optionally +// be called from whichever unit test harness the user of the library +// is using. + +#ifndef CEEE_TESTING_SIDESTEP_INTEGRATION_H_ +#define CEEE_TESTING_SIDESTEP_INTEGRATION_H_ + +#ifndef SIDESTEP_ASSERT // you may define it explicitly yourself +#if defined(ENABLE_SIDESTEP_ASSERTIONS) || defined(_DEBUG) || defined(DEBUG) +#define SIDESTEP_ASSERT(x) sidestep::AssertImpl(x, #x) +#else +#define SIDESTEP_ASSERT(x) +#endif +#endif + +#ifndef SIDESTEP_LOG // you may define it explicitly yourself +#if defined(ENABLE_SIDESTEP_LOGGING) || defined(_DEBUG) || defined(DEBUG) +#define SIDESTEP_LOG(x) sidestep::LogImpl(x) +#else +#define SIDESTEP_LOG(x) +#endif +#endif + +// You may define SIDESTEP_CHK yourself to make it fail your unit test +// harness when the condition is not met (see auto_testing_hook.h). +#ifndef SIDESTEP_CHK +#define SIDESTEP_CHK(x) if (x) {} +#endif + +// You may define SIDESTEP_EXPECT_TRUE to override the behavior of +// the SideStep unit test in preamble_patcher_test.cc +#ifndef SIDESTEP_EXPECT_TRUE +#define SIDESTEP_EXPECT_TRUE(x) \ + do { if (!(x)) { \ + SIDESTEP_ASSERT(false && #x); \ + return false; } } while (false) +#endif + +namespace sidestep { + +// This is the function the macro SIDESTEP_ASSERT calls in debug +// builds by default. Users of the library must link in its +// implementation. +// @param assertion_is_true True if the condition of the assertion was met. +// @param message A stringified version of the condition of the assertion. +void AssertImpl(bool assertion_is_true, const char* message); + +// This is the function the macro SIDESTEP_LOG calls in debug builds, +// by default. Users of the library must link in its implementation. +// @param message The message to be logged. +void LogImpl(const char* message); + +// Runs the SideStep unit tests and returns true on success. +bool UnitTests(); + +}; // namespace sidestep + +#endif // CEEE_TESTING_SIDESTEP_INTEGRATION_H_ diff --git a/ceee/testing/sidestep/mini_disassembler.cc b/ceee/testing/sidestep/mini_disassembler.cc new file mode 100644 index 0000000..3858a1b --- /dev/null +++ b/ceee/testing/sidestep/mini_disassembler.cc @@ -0,0 +1,390 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Implementation of MiniDisassembler. + +#include "ceee/testing/sidestep/mini_disassembler.h" + +namespace sidestep { + +MiniDisassembler::MiniDisassembler(bool operand_default_is_32_bits, + bool address_default_is_32_bits) + : operand_default_is_32_bits_(operand_default_is_32_bits), + address_default_is_32_bits_(address_default_is_32_bits) { + Initialize(); +} + +MiniDisassembler::MiniDisassembler() + : operand_default_is_32_bits_(true), + address_default_is_32_bits_(true) { + Initialize(); +} + +InstructionType MiniDisassembler::Disassemble( + unsigned char* start_byte, + unsigned int& instruction_bytes) { + // Clean up any state from previous invocations. + Initialize(); + + // Start by processing any prefixes. + unsigned char* current_byte = start_byte; + unsigned int size = 0; + InstructionType instruction_type = ProcessPrefixes(current_byte, size); + + if (IT_UNKNOWN == instruction_type) + return instruction_type; + + current_byte += size; + size = 0; + + // Invariant: We have stripped all prefixes, and the operand_is_32_bits_ + // and address_is_32_bits_ flags are correctly set. + + instruction_type = ProcessOpcode(current_byte, 0, size); + + // Check for error processing instruction + if ((IT_UNKNOWN == instruction_type_) || (IT_UNUSED == instruction_type_)) { + return IT_UNKNOWN; + } + + current_byte += size; + + // Invariant: operand_bytes_ indicates the total size of operands + // specified by the opcode and/or ModR/M byte and/or SIB byte. + // pCurrentByte points to the first byte after the ModR/M byte, or after + // the SIB byte if it is present (i.e. the first byte of any operands + // encoded in the instruction). + + // We get the total length of any prefixes, the opcode, and the ModR/M and + // SIB bytes if present, by taking the difference of the original starting + // address and the current byte (which points to the first byte of the + // operands if present, or to the first byte of the next instruction if + // they are not). Adding the count of bytes in the operands encoded in + // the instruction gives us the full length of the instruction in bytes. + instruction_bytes += operand_bytes_ + (current_byte - start_byte); + + // Return the instruction type, which was set by ProcessOpcode(). + return instruction_type_; +} + +void MiniDisassembler::Initialize() { + operand_is_32_bits_ = operand_default_is_32_bits_; + address_is_32_bits_ = address_default_is_32_bits_; + operand_bytes_ = 0; + have_modrm_ = false; + should_decode_modrm_ = false; + instruction_type_ = IT_UNKNOWN; + got_f2_prefix_ = false; + got_f3_prefix_ = false; + got_66_prefix_ = false; +} + +InstructionType MiniDisassembler::ProcessPrefixes(unsigned char* start_byte, + unsigned int& size) { + InstructionType instruction_type = IT_GENERIC; + const Opcode& opcode = s_ia32_opcode_map_[0].table_[*start_byte]; + + switch (opcode.type_) { + case IT_PREFIX_ADDRESS: + address_is_32_bits_ = !address_default_is_32_bits_; + goto nochangeoperand; + case IT_PREFIX_OPERAND: + operand_is_32_bits_ = !operand_default_is_32_bits_; + nochangeoperand: + case IT_PREFIX: + + if (0xF2 == (*start_byte)) + got_f2_prefix_ = true; + else if (0xF3 == (*start_byte)) + got_f3_prefix_ = true; + else if (0x66 == (*start_byte)) + got_66_prefix_ = true; + + instruction_type = opcode.type_; + size++; + // we got a prefix, so add one and check next byte + ProcessPrefixes(start_byte + 1, size); + default: + break; // not a prefix byte + } + + return instruction_type; +} + +InstructionType MiniDisassembler::ProcessOpcode(unsigned char* start_byte, + unsigned int table_index, + unsigned int& size) { + const OpcodeTable& table = s_ia32_opcode_map_[table_index]; // Get our table + unsigned char current_byte = (*start_byte) >> table.shift_; + current_byte = current_byte & table.mask_; // Mask out the bits we will use + + // Check whether the byte we have is inside the table we have. + if (current_byte < table.min_lim_ || current_byte > table.max_lim_) { + instruction_type_ = IT_UNKNOWN; + return instruction_type_; + } + + const Opcode& opcode = table.table_[current_byte]; + if (IT_UNUSED == opcode.type_) { + // This instruction is not used by the IA-32 ISA, so we indicate + // this to the user. Probably means that we were pointed to + // a byte in memory that was not the start of an instruction. + instruction_type_ = IT_UNUSED; + return instruction_type_; + } else if (IT_REFERENCE == opcode.type_) { + // We are looking at an opcode that has more bytes (or is continued + // in the ModR/M byte). Recursively find the opcode definition in + // the table for the opcode's next byte. + size++; + ProcessOpcode(start_byte + 1, opcode.table_index_, size); + return instruction_type_; + } + + const SpecificOpcode* specific_opcode = + reinterpret_cast<const SpecificOpcode*>(&opcode); + if (opcode.is_prefix_dependent_) { + if (got_f2_prefix_ && opcode.opcode_if_f2_prefix_.mnemonic_ != 0) { + specific_opcode = &opcode.opcode_if_f2_prefix_; + } else if (got_f3_prefix_ && opcode.opcode_if_f3_prefix_.mnemonic_ != 0) { + specific_opcode = &opcode.opcode_if_f3_prefix_; + } else if (got_66_prefix_ && opcode.opcode_if_66_prefix_.mnemonic_ != 0) { + specific_opcode = &opcode.opcode_if_66_prefix_; + } + } + + // Inv: The opcode type is known. + instruction_type_ = specific_opcode->type_; + + // Let's process the operand types to see if we have any immediate + // operands, and/or a ModR/M byte. + + ProcessOperand(specific_opcode->flag_dest_); + ProcessOperand(specific_opcode->flag_source_); + ProcessOperand(specific_opcode->flag_aux_); + + // Inv: We have processed the opcode and incremented operand_bytes_ + // by the number of bytes of any operands specified by the opcode + // that are stored in the instruction (not registers etc.). Now + // we need to return the total number of bytes for the opcode and + // for the ModR/M or SIB bytes if they are present. + + if (table.mask_ != 0xff) { + if (have_modrm_) { + // we're looking at a ModR/M byte so we're not going to + // count that into the opcode size + ProcessModrm(start_byte, size); + return IT_GENERIC; + } else { + // need to count the ModR/M byte even if it's just being + // used for opcode extension + size++; + return IT_GENERIC; + } + } else { + if (have_modrm_) { + // The ModR/M byte is the next byte. + size++; + ProcessModrm(start_byte + 1, size); + return IT_GENERIC; + } else { + size++; + return IT_GENERIC; + } + } +} + +bool MiniDisassembler::ProcessOperand(int flag_operand) { + bool succeeded = true; + if (AM_NOT_USED == flag_operand) + return succeeded; + + // Decide what to do based on the addressing mode. + switch (flag_operand & AM_MASK) { + // No ModR/M byte indicated by these addressing modes, and no + // additional (e.g. immediate) parameters. + case AM_A: // Direct address + case AM_F: // EFLAGS register + case AM_X: // Memory addressed by the DS:SI register pair + case AM_Y: // Memory addressed by the ES:DI register pair + case AM_IMPLICIT: // Parameter is implicit, occupies no space in + // instruction + break; + + // There is a ModR/M byte but it does not necessarily need + // to be decoded. + case AM_C: // reg field of ModR/M selects a control register + case AM_D: // reg field of ModR/M selects a debug register + case AM_G: // reg field of ModR/M selects a general register + case AM_P: // reg field of ModR/M selects an MMX register + case AM_R: // mod field of ModR/M may refer only to a general register + case AM_S: // reg field of ModR/M selects a segment register + case AM_T: // reg field of ModR/M selects a test register + case AM_V: // reg field of ModR/M selects a 128-bit XMM register + have_modrm_ = true; + break; + + // In these addressing modes, there is a ModR/M byte and it needs to be + // decoded. No other (e.g. immediate) params than indicated in ModR/M. + case AM_E: // Operand is either a general-purpose register or memory, + // specified by ModR/M byte + case AM_M: // ModR/M byte will refer only to memory + case AM_Q: // Operand is either an MMX register or memory (complex + // evaluation), specified by ModR/M byte + case AM_W: // Operand is either a 128-bit XMM register or memory (complex + // eval), specified by ModR/M byte + have_modrm_ = true; + should_decode_modrm_ = true; + break; + + // These addressing modes specify an immediate or an offset value + // directly, so we need to look at the operand type to see how many + // bytes. + case AM_I: // Immediate data. + case AM_J: // Jump to offset. + case AM_O: // Operand is at offset. + switch (flag_operand & OT_MASK) { + case OT_B: // Byte regardless of operand-size attribute. + operand_bytes_ += OS_BYTE; + break; + case OT_C: // Byte or word, depending on operand-size attribute. + if (operand_is_32_bits_) + operand_bytes_ += OS_WORD; + else + operand_bytes_ += OS_BYTE; + break; + case OT_D: // Doubleword, regardless of operand-size attribute. + operand_bytes_ += OS_DOUBLE_WORD; + break; + case OT_DQ: // Double-quadword, regardless of operand-size attribute. + operand_bytes_ += OS_DOUBLE_QUAD_WORD; + break; + case OT_P: // 32-bit or 48-bit pointer, depending on operand-size + // attribute. + if (operand_is_32_bits_) + operand_bytes_ += OS_48_BIT_POINTER; + else + operand_bytes_ += OS_32_BIT_POINTER; + break; + case OT_PS: // 128-bit packed single-precision floating-point data. + operand_bytes_ += OS_128_BIT_PACKED_SINGLE_PRECISION_FLOATING; + break; + case OT_Q: // Quadword, regardless of operand-size attribute. + operand_bytes_ += OS_QUAD_WORD; + break; + case OT_S: // 6-byte pseudo-descriptor. + operand_bytes_ += OS_PSEUDO_DESCRIPTOR; + break; + case OT_SD: // Scalar Double-Precision Floating-Point Value + case OT_PD: // Unaligned packed double-precision floating point value + operand_bytes_ += OS_DOUBLE_PRECISION_FLOATING; + break; + case OT_SS: + // Scalar element of a 128-bit packed single-precision + // floating data. + // We simply return enItUnknown since we don't have to support + // floating point + succeeded = false; + break; + case OT_V: // Word or doubleword, depending on operand-size attribute. + if (operand_is_32_bits_) + operand_bytes_ += OS_DOUBLE_WORD; + else + operand_bytes_ += OS_WORD; + break; + case OT_W: // Word, regardless of operand-size attribute. + operand_bytes_ += OS_WORD; + break; + + // Can safely ignore these. + case OT_A: // Two one-word operands in memory or two double-word + // operands in memory + case OT_PI: // Quadword MMX technology register (e.g. mm0) + case OT_SI: // Doubleword integer register (e.g., eax) + break; + + default: + break; + } + break; + + default: + break; + } + + return succeeded; +} + +bool MiniDisassembler::ProcessModrm(unsigned char* start_byte, + unsigned int& size) { + // If we don't need to decode, we just return the size of the ModR/M + // byte (there is never a SIB byte in this case). + if (!should_decode_modrm_) { + size++; + return true; + } + + // We never care about the reg field, only the combination of the mod + // and r/m fields, so let's start by packing those fields together into + // 5 bits. + unsigned char modrm = (*start_byte); + unsigned char mod = modrm & 0xC0; // mask out top two bits to get mod field + modrm = modrm & 0x07; // mask out bottom 3 bits to get r/m field + mod = mod >> 3; // shift the mod field to the right place + modrm = mod | modrm; // combine the r/m and mod fields as discussed + mod = mod >> 3; // shift the mod field to bits 2..0 + + // Invariant: modrm contains the mod field in bits 4..3 and the r/m field + // in bits 2..0, and mod contains the mod field in bits 2..0 + + const ModrmEntry* modrm_entry = 0; + if (address_is_32_bits_) + modrm_entry = &s_ia32_modrm_map_[modrm]; + else + modrm_entry = &s_ia16_modrm_map_[modrm]; + + // Invariant: modrm_entry points to information that we need to decode + // the ModR/M byte. + + // Add to the count of operand bytes, if the ModR/M byte indicates + // that some operands are encoded in the instruction. + if (modrm_entry->is_encoded_in_instruction_) + operand_bytes_ += modrm_entry->operand_size_; + + // Process the SIB byte if necessary, and return the count + // of ModR/M and SIB bytes. + if (modrm_entry->use_sib_byte_) { + size++; + return ProcessSib(start_byte + 1, mod, size); + } else { + size++; + return true; + } +} + +bool MiniDisassembler::ProcessSib(unsigned char* start_byte, + unsigned char mod, + unsigned int& size) { + // get the mod field from the 2..0 bits of the SIB byte + unsigned char sib_base = (*start_byte) & 0x07; + if (0x05 == sib_base) { + switch (mod) { + case 0x00: // mod == 00 + case 0x02: // mod == 10 + operand_bytes_ += OS_DOUBLE_WORD; + break; + case 0x01: // mod == 01 + operand_bytes_ += OS_BYTE; + break; + case 0x03: // mod == 11 + // According to the IA-32 docs, there does not seem to be a disp + // value for this value of mod + default: + break; + } + } + + size++; + return true; +} + +}; // namespace sidestep diff --git a/ceee/testing/sidestep/mini_disassembler.h b/ceee/testing/sidestep/mini_disassembler.h new file mode 100644 index 0000000..aeee3b1 --- /dev/null +++ b/ceee/testing/sidestep/mini_disassembler.h @@ -0,0 +1,155 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Definition of MiniDisassembler. + +#ifndef CEEE_TESTING_SIDESTEP_MINI_DISASSEMBLER_H_ +#define CEEE_TESTING_SIDESTEP_MINI_DISASSEMBLER_H_ + +#include "ceee/testing/sidestep/mini_disassembler_types.h" + +namespace sidestep { + +// This small disassembler is very limited +// in its functionality, and in fact does only the bare minimum required by the +// preamble patching utility. It may be useful for other purposes, however. +// +// The limitations include at least the following: +// -# No support for coprocessor opcodes, MMX, etc. +// -# No machine-readable identification of opcodes or decoding of +// assembly parameters. The name of the opcode (as a string) is given, +// however, to aid debugging. +// +// You may ask what this little disassembler actually does, then? The answer is +// that it does the following, which is exactly what the patching utility needs: +// -# Indicates if opcode is a jump (any kind) or a return (any kind) +// because this is important for the patching utility to determine if +// a function is too short or there are jumps too early in it for it +// to be preamble patched. +// -# The opcode length is always calculated, so that the patching utility +// can figure out where the next instruction starts, and whether it +// already has enough instructions to replace with the absolute jump +// to the patching code. +// +// The usage is quite simple; just create a MiniDisassembler and use its +// Disassemble() method. +// +// If you would like to extend this disassembler, please refer to the +// IA-32 Intel® Architecture Software Developer’s Manual Volume 2: +// Instruction Set Reference for information about operand decoding +// etc. +class MiniDisassembler { + public: + + // Creates a new instance and sets defaults. + // + // @param operand_default_32_bits If true, the default operand size is + // set to 32 bits, which is the default under Win32. Otherwise it is 16 bits. + // @param address_default_32_bits If true, the default address size is + // set to 32 bits, which is the default under Win32. Otherwise it is 16 bits. + MiniDisassembler(bool operand_default_32_bits, + bool address_default_32_bits); + + // Equivalent to MiniDisassembler(true, true); + MiniDisassembler(); + + // Attempts to disassemble a single instruction starting from the + // address in memory it is pointed to. + // + // @param start Address where disassembly should start. + // @param instruction_bytes Variable that will be <b>incremented</b> by + // the length in bytes of the instruction. + // @return enItJump, enItReturn or enItGeneric on success. enItUnknown + // if unable to disassemble, enItUnused if this seems to be an unused + // opcode. In the last two (error) cases, cbInstruction will be set + // to 0xffffffff. + // + // @post This instance of the disassembler is ready to be used again, + // with unchanged defaults from creation time. + InstructionType Disassemble(unsigned char* start, + unsigned int& instruction_bytes); + + private: + + // Makes the disassembler ready for reuse. + void Initialize(); + + // Sets the flags for address and operand sizes. + // @return Number of prefix bytes. + InstructionType ProcessPrefixes(unsigned char* start, unsigned int& size); + + // Sets the flag for whether we have ModR/M, and increments + // operand_bytes_ if any are specifies by the opcode directly. + // @return Number of opcode bytes. + InstructionType ProcessOpcode(unsigned char* start, + unsigned int table, + unsigned int& size); + + // Checks the type of the supplied operand. Increments + // operand_bytes_ if it directly indicates an immediate etc. + // operand. Asserts have_modrm_ if the operand specifies + // a ModR/M byte. + bool ProcessOperand(int flag_operand); + + // Increments operand_bytes_ by size specified by ModR/M and + // by SIB if present. + // @return 0 in case of error, 1 if there is just a ModR/M byte, + // 2 if there is a ModR/M byte and a SIB byte. + bool ProcessModrm(unsigned char* start, unsigned int& size); + + // Processes the SIB byte that it is pointed to. + // @param start Pointer to the SIB byte. + // @param mod The mod field from the ModR/M byte. + // @return 1 to indicate success (indicates 1 SIB byte) + bool ProcessSib(unsigned char* start, unsigned char mod, unsigned int& size); + + // The instruction type we have decoded from the opcode. + InstructionType instruction_type_; + + // Counts the number of bytes that is occupied by operands in + // the current instruction (note: we don't care about how large + // operands stored in registers etc. are). + unsigned int operand_bytes_; + + // True iff there is a ModR/M byte in this instruction. + bool have_modrm_; + + // True iff we need to decode the ModR/M byte (sometimes it just + // points to a register, we can tell by the addressing mode). + bool should_decode_modrm_; + + // Current operand size is 32 bits if true, 16 bits if false. + bool operand_is_32_bits_; + + // Default operand size is 32 bits if true, 16 bits if false. + bool operand_default_is_32_bits_; + + // Current address size is 32 bits if true, 16 bits if false. + bool address_is_32_bits_; + + // Default address size is 32 bits if true, 16 bits if false. + bool address_default_is_32_bits_; + + // Huge big opcode table based on the IA-32 manual, defined + // in Ia32OpcodeMap.cc + static const OpcodeTable s_ia32_opcode_map_[]; + + // Somewhat smaller table to help with decoding ModR/M bytes + // when 16-bit addressing mode is being used. Defined in + // Ia32ModrmMap.cc + static const ModrmEntry s_ia16_modrm_map_[]; + + // Somewhat smaller table to help with decoding ModR/M bytes + // when 32-bit addressing mode is being used. Defined in + // Ia32ModrmMap.cc + static const ModrmEntry s_ia32_modrm_map_[]; + + // Indicators of whether we got certain prefixes that certain + // silly Intel instructions depend on in nonstandard ways for + // their behaviors. + bool got_f2_prefix_, got_f3_prefix_, got_66_prefix_; +}; + +}; // namespace sidestep + +#endif // CEEE_TESTING_SIDESTEP_MINI_DISASSEMBLER_H_ diff --git a/ceee/testing/sidestep/mini_disassembler_types.h b/ceee/testing/sidestep/mini_disassembler_types.h new file mode 100644 index 0000000..479d75a --- /dev/null +++ b/ceee/testing/sidestep/mini_disassembler_types.h @@ -0,0 +1,197 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Several simple types used by the disassembler and some of the patching +// mechanisms. + +#ifndef CEEE_TESTING_SIDESTEP_MINI_DISASSEMBLER_TYPES_H_ +#define CEEE_TESTING_SIDESTEP_MINI_DISASSEMBLER_TYPES_H_ + +namespace sidestep { + +// Categories of instructions that we care about +enum InstructionType { + // This opcode is not used + IT_UNUSED, + // This disassembler does not recognize this opcode (error) + IT_UNKNOWN, + // This is not an instruction but a reference to another table + IT_REFERENCE, + // This byte is a prefix byte that we can ignore + IT_PREFIX, + // This is a prefix byte that switches to the nondefault address size + IT_PREFIX_ADDRESS, + // This is a prefix byte that switches to the nondefault operand size + IT_PREFIX_OPERAND, + // A jump or call instruction + IT_JUMP, + // A return instruction + IT_RETURN, + // Any other type of instruction (in this case we don't care what it is) + IT_GENERIC, +}; + +// Lists IA-32 operand sizes in multiples of 8 bits +enum OperandSize { + OS_ZERO = 0, + OS_BYTE = 1, + OS_WORD = 2, + OS_DOUBLE_WORD = 4, + OS_QUAD_WORD = 8, + OS_DOUBLE_QUAD_WORD = 16, + OS_32_BIT_POINTER = 32/8, + OS_48_BIT_POINTER = 48/8, + OS_SINGLE_PRECISION_FLOATING = 32/8, + OS_DOUBLE_PRECISION_FLOATING = 64/8, + OS_DOUBLE_EXTENDED_PRECISION_FLOATING = 80/8, + OS_128_BIT_PACKED_SINGLE_PRECISION_FLOATING = 128/8, + OS_PSEUDO_DESCRIPTOR = 6 +}; + +// Operand addressing methods from the IA-32 manual. The enAmMask value +// is a mask for the rest. The other enumeration values are named for the +// names given to the addressing methods in the manual, e.g. enAm_D is for +// the D addressing method. +// +// The reason we use a full 4 bytes and a mask, is that we need to combine +// these flags with the enOperandType to store the details +// on the operand in a single integer. +enum AddressingMethod { + AM_NOT_USED = 0, // This operand is not used for this instruction + AM_MASK = 0x00FF0000, // Mask for the rest of the values in this enumeration + AM_A = 0x00010000, // A addressing type + AM_C = 0x00020000, // C addressing type + AM_D = 0x00030000, // D addressing type + AM_E = 0x00040000, // E addressing type + AM_F = 0x00050000, // F addressing type + AM_G = 0x00060000, // G addressing type + AM_I = 0x00070000, // I addressing type + AM_J = 0x00080000, // J addressing type + AM_M = 0x00090000, // M addressing type + AM_O = 0x000A0000, // O addressing type + AM_P = 0x000B0000, // P addressing type + AM_Q = 0x000C0000, // Q addressing type + AM_R = 0x000D0000, // R addressing type + AM_S = 0x000E0000, // S addressing type + AM_T = 0x000F0000, // T addressing type + AM_V = 0x00100000, // V addressing type + AM_W = 0x00110000, // W addressing type + AM_X = 0x00120000, // X addressing type + AM_Y = 0x00130000, // Y addressing type + AM_REGISTER = 0x00140000, // Specific register is always used as this op + AM_IMPLICIT = 0x00150000, // An implicit, fixed value is used +}; + +// Operand types from the IA-32 manual. The enOtMask value is +// a mask for the rest. The rest of the values are named for the +// names given to these operand types in the manual, e.g. enOt_ps +// is for the ps operand type in the manual. +// +// The reason we use a full 4 bytes and a mask, is that we need +// to combine these flags with the enAddressingMethod to store the details +// on the operand in a single integer. +enum OperandType { + OT_MASK = 0xFF000000, + OT_A = 0x01000000, + OT_B = 0x02000000, + OT_C = 0x03000000, + OT_D = 0x04000000, + OT_DQ = 0x05000000, + OT_P = 0x06000000, + OT_PI = 0x07000000, + OT_PS = 0x08000000, // actually unsupported for (we don't know its size) + OT_Q = 0x09000000, + OT_S = 0x0A000000, + OT_SS = 0x0B000000, + OT_SI = 0x0C000000, + OT_V = 0x0D000000, + OT_W = 0x0E000000, + OT_SD = 0x0F000000, // scalar double-precision floating-point value + OT_PD = 0x10000000, // double-precision floating point + // dummy "operand type" for address mode M - which doesn't specify + // operand type + OT_ADDRESS_MODE_M = 0x80000000 +}; + +// Everything that's in an Opcode (see below) except the three +// alternative opcode structs for different prefixes. +struct SpecificOpcode { + // Index to continuation table, or 0 if this is the last + // byte in the opcode. + int table_index_; + + // The opcode type + InstructionType type_; + + // Description of the type of the dest, src and aux operands, + // put together from an enOperandType flag and an enAddressingMethod + // flag. + int flag_dest_; + int flag_source_; + int flag_aux_; + + // We indicate the mnemonic for debugging purposes + const char* mnemonic_; +}; + +// The information we keep in our tables about each of the different +// valid instructions recognized by the IA-32 architecture. +struct Opcode { + // Index to continuation table, or 0 if this is the last + // byte in the opcode. + int table_index_; + + // The opcode type + InstructionType type_; + + // Description of the type of the dest, src and aux operands, + // put together from an enOperandType flag and an enAddressingMethod + // flag. + int flag_dest_; + int flag_source_; + int flag_aux_; + + // We indicate the mnemonic for debugging purposes + const char* mnemonic_; + + // Alternative opcode info if certain prefixes are specified. + // In most cases, all of these are zeroed-out. Only used if + // bPrefixDependent is true. + bool is_prefix_dependent_; + SpecificOpcode opcode_if_f2_prefix_; + SpecificOpcode opcode_if_f3_prefix_; + SpecificOpcode opcode_if_66_prefix_; +}; + +// Information about each table entry. +struct OpcodeTable { + // Table of instruction entries + const Opcode* table_; + // How many bytes left to shift ModR/M byte <b>before</b> applying mask + unsigned char shift_; + // Mask to apply to byte being looked at before comparing to table + unsigned char mask_; + // Minimum/maximum indexes in table. + unsigned char min_lim_; + unsigned char max_lim_; +}; + +// Information about each entry in table used to decode ModR/M byte. +struct ModrmEntry { + // Is the operand encoded as bytes in the instruction (rather than + // if it's e.g. a register in which case it's just encoded in the + // ModR/M byte) + bool is_encoded_in_instruction_; + + // Is there a SIB byte? In this case we always need to decode it. + bool use_sib_byte_; + + // What is the size of the operand (only important if it's encoded + // in the instruction)? + OperandSize operand_size_; +}; + +}; // namespace sidestep + +#endif // CEEE_TESTING_SIDESTEP_MINI_DISASSEMBLER_TYPES_H_ diff --git a/ceee/testing/sidestep/preamble_patcher.cc b/ceee/testing/sidestep/preamble_patcher.cc new file mode 100644 index 0000000..cdbfcfa --- /dev/null +++ b/ceee/testing/sidestep/preamble_patcher.cc @@ -0,0 +1,310 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Implementation of PreamblePatcher + +#include "ceee/testing/sidestep/integration.h" +#include "ceee/testing/sidestep/preamble_patcher.h" +#include "ceee/testing/sidestep/mini_disassembler.h" + +// Definitions of assembly statements we need +#define ASM_JMP32REL 0xE9 +#define ASM_INT3 0xCC +#define ASM_JMP32ABS_0 0xFF +#define ASM_JMP32ABS_1 0x25 +#define ASM_JMP8REL 0xEB + +namespace sidestep { + +// Handle a special case that we see with functions that point into an +// IAT table (including functions linked statically into the +// application): these function already starts with ASM_JMP32*. For +// instance, malloc() might be implemented as a JMP to __malloc(). +// This function follows the initial JMPs for us, until we get to the +// place where the actual code is defined. If we get to STOP_BEFORE, +// we return the address before stop_before. +void* PreamblePatcher::ResolveTargetImpl(unsigned char* target, + unsigned char* stop_before) { + if (target == NULL) + return NULL; + while (1) { + unsigned char* new_target; + if (target[0] == ASM_JMP32REL) { + // target[1-4] holds the place the jmp goes to, but it's + // relative to the next instruction. + int relative_offset; // Windows guarantees int is 4 bytes + SIDESTEP_ASSERT(sizeof(relative_offset) == 4); + memcpy(reinterpret_cast<void*>(&relative_offset), + reinterpret_cast<void*>(target + 1), 4); + new_target = target + 5 + relative_offset; + } else if (target[0] == ASM_JMP8REL) { + // Visual Studio 7.1 implements new[] as an 8 bit jump to new + signed char relative_offset; + memcpy(reinterpret_cast<void*>(&relative_offset), + reinterpret_cast<void*>(target + 1), 1); + new_target = target + 2 + relative_offset; + } else if (target[0] == ASM_JMP32ABS_0 && + target[1] == ASM_JMP32ABS_1) { + // Visual studio seems to sometimes do it this way instead of the + // previous way. Not sure what the rules are, but it was happening + // with operator new in some binaries. + void **new_target_v; + SIDESTEP_ASSERT(sizeof(new_target) == 4); + memcpy(&new_target_v, reinterpret_cast<void*>(target + 2), 4); + new_target = reinterpret_cast<unsigned char*>(*new_target_v); + } else { + break; + } + if (new_target == stop_before) + break; + target = new_target; + } + return target; +} + +// Special case scoped_ptr to avoid dependency on scoped_ptr below. +class DeleteUnsignedCharArray { + public: + explicit DeleteUnsignedCharArray(unsigned char* array) : array_(array) { + } + + ~DeleteUnsignedCharArray() { + if (array_) { + delete [] array_; + } + } + + unsigned char* Release() { + unsigned char* temp = array_; + array_ = NULL; + return temp; + } + + private: + unsigned char* array_; +}; + +SideStepError PreamblePatcher::RawPatchWithStubAndProtections( + void* target_function, void *replacement_function, + unsigned char* preamble_stub, unsigned long stub_size, + unsigned long* bytes_needed) { + // We need to be able to write to a process-local copy of the first + // MAX_PREAMBLE_STUB_SIZE bytes of target_function + DWORD old_target_function_protect = 0; + BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function), + MAX_PREAMBLE_STUB_SIZE, + PAGE_EXECUTE_READWRITE, + &old_target_function_protect); + if (!succeeded) { + SIDESTEP_ASSERT(false && "Failed to make page containing target function " + "copy-on-write."); + return SIDESTEP_ACCESS_DENIED; + } + + SideStepError error_code = RawPatchWithStub(target_function, + replacement_function, + preamble_stub, + stub_size, + bytes_needed); + + // Restore the protection of the first MAX_PREAMBLE_STUB_SIZE bytes of + // pTargetFunction to what they were before we started goofing around. + // We do this regardless of whether the patch succeeded or not. + succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function), + MAX_PREAMBLE_STUB_SIZE, + old_target_function_protect, + &old_target_function_protect); + if (!succeeded) { + SIDESTEP_ASSERT(false && + "Failed to restore protection to target function."); + // We must not return an error here because the function has + // likely actually been patched, and returning an error might + // cause our client code not to unpatch it. So we just keep + // going. + } + + if (SIDESTEP_SUCCESS != error_code) { // Testing RawPatchWithStub, above + SIDESTEP_ASSERT(false); + return error_code; + } + + // Flush the instruction cache to make sure the processor doesn't execute the + // old version of the instructions (before our patch). + // + // FlushInstructionCache is actually a no-op at least on + // single-processor XP machines. I'm not sure why this is so, but + // it is, yet I want to keep the call to the API here for + // correctness in case there is a difference in some variants of + // Windows/hardware. + succeeded = ::FlushInstructionCache(::GetCurrentProcess(), + target_function, + MAX_PREAMBLE_STUB_SIZE); + if (!succeeded) { + SIDESTEP_ASSERT(false && "Failed to flush instruction cache."); + // We must not return an error here because the function has actually + // been patched, and returning an error would likely cause our client + // code not to unpatch it. So we just keep going. + } + + return SIDESTEP_SUCCESS; +} + +SideStepError PreamblePatcher::RawPatch(void* target_function, + void* replacement_function, + void** original_function_stub) { + if (!target_function || !replacement_function || !original_function_stub || + (*original_function_stub) || target_function == replacement_function) { + SIDESTEP_ASSERT(false && "Preconditions not met"); + return SIDESTEP_INVALID_PARAMETER; + } + + // @see MAX_PREAMBLE_STUB_SIZE for an explanation of how we arrives at + // this size + unsigned char* preamble_stub = new unsigned char[MAX_PREAMBLE_STUB_SIZE]; + if (!preamble_stub) { + SIDESTEP_ASSERT(false && "Unable to allocate preamble-stub."); + return SIDESTEP_INSUFFICIENT_BUFFER; + } + + // Frees the array at end of scope. + DeleteUnsignedCharArray guard_preamble_stub(preamble_stub); + + // Change the protection of the newly allocated preamble stub to + // PAGE_EXECUTE_READWRITE. This is required to work with DEP (Data + // Execution Prevention) which will cause an exception if code is executed + // from a page on which you do not have read access. + DWORD old_stub_protect = 0; + BOOL succeeded = VirtualProtect(preamble_stub, MAX_PREAMBLE_STUB_SIZE, + PAGE_EXECUTE_READWRITE, &old_stub_protect); + if (!succeeded) { + SIDESTEP_ASSERT(false && + "Failed to make page preamble stub read-write-execute."); + return SIDESTEP_ACCESS_DENIED; + } + + SideStepError error_code = RawPatchWithStubAndProtections( + target_function, replacement_function, preamble_stub, + MAX_PREAMBLE_STUB_SIZE, NULL); + + if (SIDESTEP_SUCCESS != error_code) { + SIDESTEP_ASSERT(false); + return error_code; + } + + // Flush the instruction cache to make sure the processor doesn't execute the + // old version of the instructions (before our patch). + // + // FlushInstructionCache is actually a no-op at least on + // single-processor XP machines. I'm not sure why this is so, but + // it is, yet I want to keep the call to the API here for + // correctness in case there is a difference in some variants of + // Windows/hardware. + succeeded = ::FlushInstructionCache(::GetCurrentProcess(), + target_function, + MAX_PREAMBLE_STUB_SIZE); + if (!succeeded) { + SIDESTEP_ASSERT(false && "Failed to flush instruction cache."); + // We must not return an error here because the function has actually + // been patched, and returning an error would likely cause our client + // code not to unpatch it. So we just keep going. + } + + SIDESTEP_LOG("PreamblePatcher::RawPatch successfully patched."); + + // detach the scoped pointer so the memory is not freed + *original_function_stub = + reinterpret_cast<void*>(guard_preamble_stub.Release()); + return SIDESTEP_SUCCESS; +} + +SideStepError PreamblePatcher::Unpatch(void* target_function, + void* replacement_function, + void* original_function_stub) { + SIDESTEP_ASSERT(target_function && replacement_function && + original_function_stub); + if (!target_function || !replacement_function || + !original_function_stub) { + return SIDESTEP_INVALID_PARAMETER; + } + + // We disassemble the preamble of the _stub_ to see how many bytes we + // originally copied to the stub. + MiniDisassembler disassembler; + unsigned int preamble_bytes = 0; + while (preamble_bytes < 5) { + InstructionType instruction_type = + disassembler.Disassemble( + reinterpret_cast<unsigned char*>(original_function_stub) + + preamble_bytes, + preamble_bytes); + if (IT_GENERIC != instruction_type) { + SIDESTEP_ASSERT(false && + "Should only have generic instructions in stub!!"); + return SIDESTEP_UNSUPPORTED_INSTRUCTION; + } + } + + // Before unpatching, target_function should be a JMP to + // replacement_function. If it's not, then either it's an error, or + // we're falling into the case where the original instruction was a + // JMP, and we patched the jumped_to address rather than the JMP + // itself. (For instance, if malloc() is just a JMP to __malloc(), + // we patched __malloc() and not malloc().) + unsigned char* target = reinterpret_cast<unsigned char*>(target_function); + target = reinterpret_cast<unsigned char*>( + ResolveTargetImpl( + target, reinterpret_cast<unsigned char*>(replacement_function))); + // We should end at the function we patched. When we patch, we insert + // a ASM_JMP32REL instruction, so look for that as a sanity check. + if (target[0] != ASM_JMP32REL) { + SIDESTEP_ASSERT(false && + "target_function does not look like it was patched."); + return SIDESTEP_INVALID_PARAMETER; + } + + // We need to be able to write to a process-local copy of the first + // MAX_PREAMBLE_STUB_SIZE bytes of target_function + DWORD old_target_function_protect = 0; + BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target), + MAX_PREAMBLE_STUB_SIZE, + PAGE_EXECUTE_READWRITE, + &old_target_function_protect); + if (!succeeded) { + SIDESTEP_ASSERT(false && "Failed to make page containing target function " + "copy-on-write."); + return SIDESTEP_ACCESS_DENIED; + } + + // Replace the first few bytes of the original function with the bytes we + // previously moved to the preamble stub. + memcpy(reinterpret_cast<void*>(target), + original_function_stub, preamble_bytes); + + // Stub is now useless so delete it. + delete original_function_stub; + + // Restore the protection of the first MAX_PREAMBLE_STUB_SIZE bytes of + // target to what they were before we started goofing around. + succeeded = ::VirtualProtect(reinterpret_cast<void*>(target), + MAX_PREAMBLE_STUB_SIZE, + old_target_function_protect, + &old_target_function_protect); + + // Flush the instruction cache to make sure the processor doesn't execute the + // old version of the instructions (before our patch). + // + // See comment on FlushInstructionCache elsewhere in this file. + succeeded = ::FlushInstructionCache(::GetCurrentProcess(), + target, + MAX_PREAMBLE_STUB_SIZE); + if (!succeeded) { + SIDESTEP_ASSERT(false && "Failed to flush instruction cache."); + return SIDESTEP_UNEXPECTED; + } + + SIDESTEP_LOG("PreamblePatcher::Unpatch successfully unpatched."); + return SIDESTEP_SUCCESS; +} + +}; // namespace sidestep diff --git a/ceee/testing/sidestep/preamble_patcher.h b/ceee/testing/sidestep/preamble_patcher.h new file mode 100644 index 0000000..bfe6569 --- /dev/null +++ b/ceee/testing/sidestep/preamble_patcher.h @@ -0,0 +1,350 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Definition of PreamblePatcher + +#ifndef CEEE_TESTING_SIDESTEP_PREAMBLE_PATCHER_H_ +#define CEEE_TESTING_SIDESTEP_PREAMBLE_PATCHER_H_ + +#include <windows.h> + +// Maximum size of the preamble stub. We overwrite at least the first 5 +// bytes of the function. Considering the worst case scenario, we need 4 +// bytes + the max instruction size + 5 more bytes for our jump back to +// the original code. With that in mind, 32 is a good number :) +#define MAX_PREAMBLE_STUB_SIZE (32) + +namespace sidestep { + +// Possible results of patching/unpatching +enum SideStepError { + SIDESTEP_SUCCESS = 0, + SIDESTEP_INVALID_PARAMETER, + SIDESTEP_INSUFFICIENT_BUFFER, + SIDESTEP_JUMP_INSTRUCTION, + SIDESTEP_FUNCTION_TOO_SMALL, + SIDESTEP_UNSUPPORTED_INSTRUCTION, + SIDESTEP_NO_SUCH_MODULE, + SIDESTEP_NO_SUCH_FUNCTION, + SIDESTEP_ACCESS_DENIED, + SIDESTEP_UNEXPECTED, +}; + +#define SIDESTEP_TO_HRESULT(error) \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_NULL, error) + +// Implements a patching mechanism that overwrites the first few bytes of +// a function preamble with a jump to our hook function, which is then +// able to call the original function via a specially-made preamble-stub +// that imitates the action of the original preamble. +// +// NOTE: This patching mechanism should currently only be used for +// non-production code, e.g. unit tests, because it is not threadsafe. +// See the TODO in preamble_patcher_with_stub.cc for instructions on what +// we need to do before using it in production code; it's fairly simple +// but unnecessary for now since we only intend to use it in unit tests. +// +// To patch a function, use either of the typesafe Patch() methods. You +// can unpatch a function using Unpatch(). +// +// Typical usage goes something like this: +// @code +// typedef int (*MyTypesafeFuncPtr)(int x); +// MyTypesafeFuncPtr original_func_stub; +// int MyTypesafeFunc(int x) { +// return x + 1; +// } +// +// int HookMyTypesafeFunc(int x) { +// return 1 + original_func_stub(x); +// } +// +// void MyPatchInitializingFunction() { +// original_func_stub = PreamblePatcher::Patch( +// MyTypesafeFunc, HookMyTypesafeFunc); +// if (!original_func_stub) { +// // ... error handling ... +// } +// +// // ... continue - you have patched the function successfully ... +// } +// @endcode +// +// Note that there are a number of ways that this method of patching can +// fail. The most common are: +// - If there is a jump (jxx) instruction in the first 5 bytes of +// the function being patched, we cannot patch it because in the +// current implementation we do not know how to rewrite relative +// jumps after relocating them to the preamble-stub. Note that +// if you really really need to patch a function like this, it +// would be possible to add this functionality (but at some cost). +// - If there is a return (ret) instruction in the first 5 bytes +// we cannot patch the function because it may not be long enough +// for the jmp instruction we use to inject our patch. +// - If there is another thread currently executing within the bytes +// that are copied to the preamble stub, it will crash in an undefined +// way. +// +// If you get any other error than the above, you're either pointing the +// patcher at an invalid instruction (e.g. into the middle of a multi- +// byte instruction, or not at memory containing executable instructions) +// or, there may be a bug in the disassembler we use to find +// instruction boundaries. +// +// NOTE: In optimized builds, when you have very trivial functions that +// the compiler can reason do not have side effects, the compiler may +// reuse the result of calling the function with a given parameter, which +// may mean if you patch the function in between your patch will never get +// invoked. See preamble_patcher_test.cc for an example. +class PreamblePatcher { + public: + + // This is a typesafe version of RawPatch(), identical in all other + // ways than it takes a template parameter indicating the type of the + // function being patched. + // + // @param T The type of the function you are patching. Usually + // you will establish this type using a typedef, as in the following + // example: + // @code + // typedef BOOL (WINAPI *MessageBoxPtr)(HWND, LPCTSTR, LPCTSTR, UINT); + // MessageBoxPtr original = NULL; + // PreamblePatcher::Patch(MessageBox, Hook_MessageBox, &original); + // @endcode + template <class T> + static SideStepError Patch(T target_function, + T replacement_function, + T* original_function_stub) { + // NOTE: casting from a function to a pointer is contra the C++ + // spec. It's not safe on IA64, but is on i386. We use + // a C-style cast here to emphasize this is not legal C++. + return RawPatch((void*)(target_function), + (void*)(replacement_function), + (void**)(original_function_stub)); + } + + // Patches a named function imported from the named module using + // preamble patching. Uses RawPatch() to do the actual patching + // work. + // + // @param T The type of the function you are patching. Must + // exactly match the function you specify using module_name and + // function_name. + // + // @param module_name The name of the module from which the function + // is being imported. Note that the patch will fail if this module + // has not already been loaded into the current process. + // + // @param function_name The name of the function you wish to patch. + // + // @param replacement_function Your replacement function which + // will be called whenever code tries to call the original function. + // + // @param original_function_stub Pointer to memory that should receive a + // pointer that can be used (e.g. in the replacement function) to call the + // original function, or NULL to indicate failure. + // + // @return One of the EnSideStepError error codes; only SIDESTEP_SUCCESS + // indicates success. + template <class T> + static SideStepError Patch(LPCTSTR module_name, + LPCSTR function_name, + T replacement_function, + T* original_function_stub) { + SIDESTEP_ASSERT(module_name && function_name); + if (!module_name || !function_name) { + SIDESTEP_ASSERT(false && + "You must specify a module name and function name."); + return SIDESTEP_INVALID_PARAMETER; + } + HMODULE module = ::GetModuleHandle(module_name); + SIDESTEP_ASSERT(module != NULL); + if (!module) { + SIDESTEP_ASSERT(false && "Invalid module name."); + return SIDESTEP_NO_SUCH_MODULE; + } + FARPROC existing_function = ::GetProcAddress(module, function_name); + if (!existing_function) { + SIDESTEP_ASSERT( + false && "Did not find any function with that name in the module."); + return SIDESTEP_NO_SUCH_FUNCTION; + } + // NOTE: casting from a function to a pointer is contra the C++ + // spec. It's not safe on IA64, but is on i386. We use + // a C-style cast here to emphasize this is not legal C++. + return RawPatch((void*)existing_function, (void*)replacement_function, + (void**)(original_function_stub)); + } + + // Patches a function by overwriting its first few bytes with + // a jump to a different function. This is the "worker" function + // for each of the typesafe Patch() functions. In most cases, + // it is preferable to use the Patch() functions rather than + // this one as they do more checking at compile time. + // + // @param target_function A pointer to the function that should be + // patched. + // + // @param replacement_function A pointer to the function that should + // replace the target function. The replacement function must have + // exactly the same calling convention and parameters as the original + // function. + // + // @param original_function_stub Pointer to memory that should receive a + // pointer that can be used (e.g. in the replacement function) to call the + // original function, or NULL to indicate failure. + // + // @param original_function_stub Pointer to memory that should receive a + // pointer that can be used (e.g. in the replacement function) to call the + // original function, or NULL to indicate failure. + // + // @return One of the EnSideStepError error codes; only SIDESTEP_SUCCESS + // indicates success. + // + // @note The preamble-stub (the memory pointed to by + // *original_function_stub) is allocated on the heap, and (in + // production binaries) never destroyed, resulting in a memory leak. This + // will be the case until we implement safe unpatching of a method. + // However, it is quite difficult to unpatch a method (because other + // threads in the process may be using it) so we are leaving it for now. + // See however Unpatch, which can be used for binaries where you + // know only one thread is running, e.g. unit tests. + static SideStepError RawPatch(void* target_function, + void* replacement_function, + void** original_function_stub); + + // Unpatches target_function and deletes the stub that previously could be + // used to call the original version of the function. + // + // DELETES the stub that is passed to the function. + // + // @param target_function Pointer to the target function which was + // previously patched, i.e. a pointer which value should match the value + // of the symbol prior to patching it. + // + // @param replacement_function Pointer to the function target_function + // was patched to. + // + // @param original_function_stub Pointer to the stub returned when + // patching, that could be used to call the original version of the + // patched function. This function will also delete the stub, which after + // unpatching is useless. + // + // If your original call was + // Patch(VirtualAlloc, MyVirtualAlloc, &origptr) + // then to undo it you would call + // Unpatch(VirtualAlloc, MyVirtualAlloc, origptr); + // + // @return One of the EnSideStepError error codes; only SIDESTEP_SUCCESS + // indicates success. + static SideStepError Unpatch(void* target_function, + void* replacement_function, + void* original_function_stub); + + // A helper routine when patching, which follows jmp instructions at + // function addresses, to get to the "actual" function contents. + // This allows us to identify two functions that are at different + // addresses but actually resolve to the same code. + // + // @param target_function Pointer to a function. + // + // @return Either target_function (the input parameter), or if + // target_function's body consists entirely of a JMP instruction, + // the address it JMPs to (or more precisely, the address at the end + // of a chain of JMPs). + template <class T> + static T ResolveTarget(T target_function) { + return (T)ResolveTargetImpl( + reinterpret_cast<unsigned char*>(target_function), NULL); + } + + private: + // Patches a function by overwriting its first few bytes with + // a jump to a different function. This is similar to the RawPatch + // function except that it uses the stub allocated by the caller + // instead of allocating it. + // + // We call VirtualProtect to make the + // target function writable at least for the duration of the call. + // + // @param target_function A pointer to the function that should be + // patched. + // + // @param replacement_function A pointer to the function that should + // replace the target function. The replacement function must have + // exactly the same calling convention and parameters as the original + // function. + // + // @param preamble_stub A pointer to a buffer where the preamble stub + // should be copied. The size of the buffer should be sufficient to + // hold the preamble bytes. + // + // @param stub_size Size in bytes of the buffer allocated for the + // preamble_stub + // + // @param bytes_needed Pointer to a variable that receives the minimum + // number of bytes required for the stub. Can be set to NULL if you're + // not interested. + // + // @return An error code indicating the result of patching. + static SideStepError RawPatchWithStubAndProtections( + void* target_function, + void *replacement_function, + unsigned char* preamble_stub, + unsigned long stub_size, + unsigned long* bytes_needed); + + // A helper function used by RawPatchWithStubAndProtections -- it + // does everything but the VirtualProtect work. Defined in + // preamble_patcher_with_stub.cc. + // + // @param target_function A pointer to the function that should be + // patched. + // + // @param replacement_function A pointer to the function that should + // replace the target function. The replacement function must have + // exactly the same calling convention and parameters as the original + // function. + // + // @param preamble_stub A pointer to a buffer where the preamble stub + // should be copied. The size of the buffer should be sufficient to + // hold the preamble bytes. + // + // @param stub_size Size in bytes of the buffer allocated for the + // preamble_stub + // + // @param bytes_needed Pointer to a variable that receives the minimum + // number of bytes required for the stub. Can be set to NULL if you're + // not interested. + // + // @return An error code indicating the result of patching. + static SideStepError RawPatchWithStub(void* target_function, + void *replacement_function, + unsigned char* preamble_stub, + unsigned long stub_size, + unsigned long* bytes_needed); + + + // A helper routine when patching, which follows jmp instructions at + // function addresses, to get to the "actual" function contents. + // This allows us to identify two functions that are at different + // addresses but actually resolve to the same code. + // + // @param target_function Pointer to a function. + // + // @param stop_before If, when following JMP instructions from + // target_function, we get to the address stop, we return + // immediately, the address that jumps to stop_before. + // + // @return Either target_function (the input parameter), or if + // target_function's body consists entirely of a JMP instruction, + // the address it JMPs to (or more precisely, the address at the end + // of a chain of JMPs). + static void* ResolveTargetImpl(unsigned char* target_function, + unsigned char* stop_before); +}; + +}; // namespace sidestep + +#endif // CEEE_TESTING_SIDESTEP_PREAMBLE_PATCHER_H_ diff --git a/ceee/testing/sidestep/preamble_patcher_test.cc b/ceee/testing/sidestep/preamble_patcher_test.cc new file mode 100644 index 0000000..fafa5e8 --- /dev/null +++ b/ceee/testing/sidestep/preamble_patcher_test.cc @@ -0,0 +1,168 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Unit tests for PreamblePatcher + +#include "ceee/testing/sidestep/integration.h" +#include "ceee/testing/sidestep/preamble_patcher.h" +#include "ceee/testing/sidestep/mini_disassembler.h" +#pragma warning(push) +#pragma warning(disable:4553) +#include "ceee/testing/sidestep/auto_testing_hook.h" +#pragma warning(pop) + +// Turning off all optimizations for this file, since the official build's +// "Whole program optimization" seems to cause the TestPatchUsingDynamicStub +// test to crash with an access violation. We debugged this and found +// that the optimized access a register that is changed by a call to the hook +// function. +#pragma optimize("", off) + +namespace { + +// Function for testing - this is what we patch +// +// NOTE: Because of the way the compiler optimizes this function in +// release builds, we need to use a different input value every time we +// call it within a function, otherwise the compiler will just reuse the +// last calculated incremented value. +int __declspec(noinline) IncrementNumber(int i) { + return i + 1; +} + +// A function that is too short to patch +int __declspec(naked) TooShortFunction(int) { + __asm { + ret + } +} + +typedef int (*IncrementingFunc)(int); +IncrementingFunc original_function = NULL; + +int HookIncrementNumber(int i) { + SIDESTEP_ASSERT(original_function != NULL); + int incremented_once = original_function(i); + return incremented_once + 1; +} + +// For the AutoTestingHook test, we can't use original_function, because +// all that is encapsulated. +// This function "increments" by 10, just to set it apart from the other +// functions. +int __declspec(noinline) AutoHookIncrementNumber(int i) { + return i + 10; +} + +}; // namespace + +namespace sidestep { + +bool TestPatchUsingDynamicStub() { + original_function = NULL; + SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2); + SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == + sidestep::PreamblePatcher::Patch(IncrementNumber, + HookIncrementNumber, + &original_function)); + SIDESTEP_EXPECT_TRUE(original_function); + SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 4); + SIDESTEP_EXPECT_TRUE(original_function(3) == 4); + + // Clearbox test to see that the function has been patched. + sidestep::MiniDisassembler disassembler; + unsigned int instruction_size = 0; + SIDESTEP_EXPECT_TRUE(sidestep::IT_JUMP == disassembler.Disassemble( + reinterpret_cast<unsigned char*>(IncrementNumber), + instruction_size)); + + // Since we patched IncrementNumber, its first statement is a + // jmp to the hook function. So verify that we now can not patch + // IncrementNumber because it starts with a jump. +#if 0 + IncrementingFunc dummy = NULL; + // TODO(joi@chromium.org): restore this test once flag is added to + // disable JMP following + SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_JUMP_INSTRUCTION == + sidestep::PreamblePatcher::Patch(IncrementNumber, + HookIncrementNumber, + &dummy)); + + // This test disabled because code in preamble_patcher_with_stub.cc + // asserts before returning the error code -- so there is no way + // to get an error code here, in debug build. + dummy = NULL; + SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_FUNCTION_TOO_SMALL == + sidestep::PreamblePatcher::Patch(TooShortFunction, + HookIncrementNumber, + &dummy)); +#endif + + SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == + sidestep::PreamblePatcher::Unpatch(IncrementNumber, + HookIncrementNumber, + original_function)); + return true; +} + +bool PatchThenUnpatch() { + original_function = NULL; + SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == + sidestep::PreamblePatcher::Patch(IncrementNumber, + HookIncrementNumber, + &original_function)); + SIDESTEP_EXPECT_TRUE(original_function); + SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 3); + SIDESTEP_EXPECT_TRUE(original_function(2) == 3); + + SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == + sidestep::PreamblePatcher::Unpatch(IncrementNumber, + HookIncrementNumber, + original_function)); + original_function = NULL; + SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4); + + return true; +} + +bool AutoTestingHookTest() { + SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2); + + // Inner scope, so we can test what happens when the AutoTestingHook + // goes out of scope + { + AutoTestingHook hook = MakeTestingHook(IncrementNumber, + AutoHookIncrementNumber); + (void) hook; + SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 12); + } + SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4); + + return true; +} + +bool AutoTestingHookInContainerTest() { + SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2); + + // Inner scope, so we can test what happens when the AutoTestingHook + // goes out of scope + { + AutoTestingHookHolder hook(MakeTestingHookHolder(IncrementNumber, + AutoHookIncrementNumber)); + (void) hook; + SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 12); + } + SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4); + + return true; +} + +bool UnitTests() { + return TestPatchUsingDynamicStub() && PatchThenUnpatch() && + AutoTestingHookTest() && AutoTestingHookInContainerTest(); +} + +}; // namespace sidestep + +#pragma optimize("", on) diff --git a/ceee/testing/sidestep/preamble_patcher_with_stub.cc b/ceee/testing/sidestep/preamble_patcher_with_stub.cc new file mode 100644 index 0000000..8422475 --- /dev/null +++ b/ceee/testing/sidestep/preamble_patcher_with_stub.cc @@ -0,0 +1,172 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Implementation of PreamblePatcher + +#include "ceee/testing/sidestep/integration.h" +#include "ceee/testing/sidestep/preamble_patcher.h" +#include "ceee/testing/sidestep/mini_disassembler.h" + +// Definitions of assembly statements we need +#define ASM_JMP32REL 0xE9 +#define ASM_INT3 0xCC + +namespace sidestep { + +SideStepError PreamblePatcher::RawPatchWithStub( + void* target_function, + void *replacement_function, + unsigned char* preamble_stub, + unsigned long stub_size, + unsigned long* bytes_needed) { + if ((NULL == target_function) || + (NULL == replacement_function) || + (NULL == preamble_stub)) { + SIDESTEP_ASSERT(false && + "Invalid parameters - either pTargetFunction or " + "pReplacementFunction or pPreambleStub were NULL."); + return SIDESTEP_INVALID_PARAMETER; + } + + // TODO(joi@chromium.org) siggi@chromium.org and I had a discussion + // and decided that both patching and unpatching are actually + // unsafe. We also discussed a method of making it safe, which is + // to freeze all other threads in the process, check their thread + // context to see if their eip is currently inside the block of + // instructions we need to copy to the stub, and if so wait a bit + // and try again, then unfreeze all threads once we've patched. Not + // implementing this for now since we're only using SideStep for + // unit testing, but if we ever use it for production code this is + // what we should do. + // + // NOTE: stoyan@chromium.org suggests we can write 8 or even 10 + // bytes atomically using FPU instructions, and on newer processors + // we could use cmpxchg8b or cmpxchg16b. So it might be possible to + // do the patching/unpatching atomically and avoid having to freeze + // other threads. Note though, that doing it atomically does not + // help if one of the other threads happens to have its eip in the + // middle of the bytes you change while you change them. + + // First, deal with a special case that we see with functions that + // point into an IAT table (including functions linked statically + // into the application): these function already starts with + // ASM_JMP32REL. For instance, malloc() might be implemented as a + // JMP to __malloc(). In that case, we replace the destination of + // the JMP (__malloc), rather than the JMP itself (malloc). This + // way we get the correct behavior no matter how malloc gets called. + void *new_target = ResolveTarget(target_function); + if (new_target != target_function) { // we're in the IAT case + // I'd like to just say "target = new_target", but I can't, + // because the new target will need to have its protections set. + return RawPatchWithStubAndProtections(new_target, replacement_function, + preamble_stub, stub_size, + bytes_needed); + } + unsigned char* target = reinterpret_cast<unsigned char*>(new_target); + + // Let's disassemble the preamble of the target function to see if we can + // patch, and to see how much of the preamble we need to take. We need 5 + // bytes for our jmp instruction, so let's find the minimum number of + // instructions to get 5 bytes. + MiniDisassembler disassembler; + unsigned int preamble_bytes = 0; + while (preamble_bytes < 5) { + InstructionType instruction_type = + disassembler.Disassemble(target + preamble_bytes, preamble_bytes); + if (IT_JUMP == instruction_type) { + SIDESTEP_ASSERT(false && + "Unable to patch because there is a jump instruction " + "in the first 5 bytes."); + return SIDESTEP_JUMP_INSTRUCTION; + } else if (IT_RETURN == instruction_type) { + SIDESTEP_ASSERT(false && + "Unable to patch because function is too short"); + return SIDESTEP_FUNCTION_TOO_SMALL; + } else if (IT_GENERIC != instruction_type) { + SIDESTEP_ASSERT(false && + "Disassembler encountered unsupported instruction " + "(either unused or unknown"); + return SIDESTEP_UNSUPPORTED_INSTRUCTION; + } + } + + if (NULL != bytes_needed) + *bytes_needed = preamble_bytes + 5; + + // Inv: cbPreamble is the number of bytes (at least 5) that we need to take + // from the preamble to have whole instructions that are 5 bytes or more + // in size total. The size of the stub required is cbPreamble + size of + // jmp (5) + if (preamble_bytes + 5 > stub_size) { + SIDESTEP_ASSERT(false); + return SIDESTEP_INSUFFICIENT_BUFFER; + } + + // First, copy the preamble that we will overwrite. + memcpy(reinterpret_cast<void*>(preamble_stub), + reinterpret_cast<void*>(target), preamble_bytes); + + // Now, make a jmp instruction to the rest of the target function (minus the + // preamble bytes we moved into the stub) and copy it into our preamble-stub. + // find address to jump to, relative to next address after jmp instruction +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4244) +#endif + int relative_offset_to_target_rest + = ((reinterpret_cast<unsigned char*>(target) + preamble_bytes) - + (preamble_stub + preamble_bytes + 5)); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + // jmp (Jump near, relative, displacement relative to next instruction) + preamble_stub[preamble_bytes] = ASM_JMP32REL; + // copy the address + memcpy(reinterpret_cast<void*>(preamble_stub + preamble_bytes + 1), + reinterpret_cast<void*>(&relative_offset_to_target_rest), 4); + + // Inv: preamble_stub points to assembly code that will execute the + // original function by first executing the first cbPreamble bytes of the + // preamble, then jumping to the rest of the function. + + // Overwrite the first 5 bytes of the target function with a jump to our + // replacement function. + // (Jump near, relative, displacement relative to next instruction) + target[0] = ASM_JMP32REL; + + // Find offset from instruction after jmp, to the replacement function. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4244) +#endif + int offset_to_replacement_function = + reinterpret_cast<unsigned char*>(replacement_function) - + reinterpret_cast<unsigned char*>(target) - 5; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + // complete the jmp instruction + memcpy(reinterpret_cast<void*>(target + 1), + reinterpret_cast<void*>(&offset_to_replacement_function), 4); + // Set any remaining bytes that were moved to the preamble-stub to INT3 so + // as not to cause confusion (otherwise you might see some strange + // instructions if you look at the disassembly, or even invalid + // instructions). Also, by doing this, we will break into the debugger if + // some code calls into this portion of the code. If this happens, it + // means that this function cannot be patched using this patcher without + // further thought. + if (preamble_bytes > 5) { + memset(reinterpret_cast<void*>(target + 5), ASM_INT3, preamble_bytes - 5); + } + + // Inv: The memory pointed to by target_function now points to a relative + // jump instruction that jumps over to the preamble_stub. The preamble + // stub contains the first stub_size bytes of the original target + // function's preamble code, followed by a relative jump back to the next + // instruction after the first cbPreamble bytes. + + return SIDESTEP_SUCCESS; +} + +}; // namespace sidestep diff --git a/ceee/testing/sidestep/sidestep.gyp b/ceee/testing/sidestep/sidestep.gyp new file mode 100644 index 0000000..fc125b7 --- /dev/null +++ b/ceee/testing/sidestep/sidestep.gyp @@ -0,0 +1,39 @@ +# Copyright (c) 2010 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'includes': [ + '../../../build/common.gypi', + ], + 'target_defaults': { + 'include_dirs': [ + '../../..', + ], + }, + 'targets': [ + { + 'target_name': 'sidestep', + 'type': 'static_library', + 'dependencies': [], + 'msvs_guid': '92E7FB64-89B3-49B0-BEB5-5FBC39813266', + 'sources': [ + 'auto_testing_hook.h', + 'documentation.h', + 'ia32_modrm_map.cc', + 'ia32_opcode_map.cc', + 'integration.h', + 'mini_disassembler.cc', + 'mini_disassembler.h', + 'mini_disassembler_types.h', + 'preamble_patcher.cc', + 'preamble_patcher.h', + 'preamble_patcher_test.cc', + 'preamble_patcher_with_stub.cc', + ], + }, + ] +} diff --git a/ceee/testing/utils/com_mock.py b/ceee/testing/utils/com_mock.py new file mode 100644 index 0000000..4c63374 --- /dev/null +++ b/ceee/testing/utils/com_mock.py @@ -0,0 +1,166 @@ +#!/bin/env python +# Copyright (c) 2010 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Generates a file with MOCK_METHODX(...) macros for a COM interface. + +For interfaces that will not change, you can run this file by hand. For +interfaces that may still change, you could run it as part of your build +system to generate code, then #include the generated list of methods inside an +appropriate wrapper class. + +Usage: + com_mock.py INTERFACE_NAME HEADER [ADD_HEADERS]* > output-inl.h +""" + +import re +import sys + +# Finds an interface block in a header file generated by IDL. +_INTERFACE_PATTERN = (r'MIDL_INTERFACE\("[A-Za-z0-9\-]+"\)\s*%s\s*:\s*' + r'public\s*(?P<base>\w+)\s*{\s*public:' + r'(?P<method_block>[^}]+)};') + +# Parses out a method from within an interface block. +_METHOD_RE = re.compile(r'virtual\s*(:?/\*[^*]+\*/)?\s*(?P<ret>\w+)\s*' + r'STDMETHODCALLTYPE\s*(?P<name>\w+)\s*\(' + r'(?P<params>[^\)]+)\)\s*=\s*0;') + +# Finds inline C-style comments. +_COMMENTS_RE = re.compile('/\*.*?\*/') + +# Finds __RPC_xyz defines. +_RPC_RE = re.compile(' __RPC\w*') + +# Finds whitespace. +_WHITESPACE_RE = re.compile('\s+') + +# Finds default argument values. +_DEFAULT_ARG_RE = re.compile('\s*=\s*[^,]*') + +# Header for generated files. +_FILE_HEADER = '''\ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Auto-generated by com_mock.py + +''' + +class Mocker(object): + """Given a set of header files, can generate mocky goodness for interfaces + defined in them. + """ + + def __init__(self): + self.headers_ = '' + + def AddHeaders(self, header_files): + """Adds the specified files to the headers to parse.""" + assert header_files + # Slurp all the header files into a single huge big string. + # Yummy nonperformant code - but it probably doesn't need to be. + headers = [] + for f in header_files: + fo = open(f, 'r') + headers.append(fo.read()) + fo.close() + self.headers_ += '\n'.join(headers) + + def _GetInterfaceInfo(self, interface_name): + """Returns a tuple (base_interface, method_block) or None.""" + m = re.search(_INTERFACE_PATTERN % interface_name, self.headers_) + if m: + return (m.group('base'), m.group('method_block')) + else: + return None + + @staticmethod + def _ProcessParams(params): + """Return a tuple (count, cleaned_up_params).""" + if params == ' void': + return (0, '') + else: + params = params.replace('\n', ' ') + params = _COMMENTS_RE.sub('', params) + params = _RPC_RE.sub(' ', params) + params = _WHITESPACE_RE.sub(' ', params) + params = _DEFAULT_ARG_RE.sub(' ', params) + return (params.count(',') + 1, params.strip()) + + def _MockMethodsInBlock(self, block): + """Return a list of mock functions for each of the methods defined + in 'block', which is the text between 'public:' and '};' in an interface + definition. + """ + methods = [] + for m in _METHOD_RE.finditer(block): + return_type = m.group('ret') + name = m.group('name') + params = m.group('params') + (param_count, clean_params) = self._ProcessParams(params) + + method_definition = ('MOCK_METHOD%d_WITH_CALLTYPE(__stdcall, %s, %s(%s));' + % (param_count, name, return_type, clean_params)) + if len(method_definition) > 78: + method_definition = ( + 'MOCK_METHOD%d_WITH_CALLTYPE(__stdcall, %s, %s(\n %s));' % + (param_count, name, return_type, clean_params)) + methods.append(method_definition) + return methods + + def MockInterface(self, name): + """Returns a list of all the mock methods needed for the given + interface (including methods from inherited interfaces, but stopping + short of IDispatch and IUnknown). + """ + info = self._GetInterfaceInfo(name) + if not info: + return '' + + # Generate inherited methods + methods = [] + if info[0] not in ('IUnknown', 'IDispatch'): + methods.extend(self.MockInterface(info[0])) + methods.extend(self._MockMethodsInBlock(info[1])) + return methods + + +def MockMethods(interface_name, header_files): + """Returns a string with a correctly filled-out MOCK_METHODX(...) line + for each method in the given interface, plus all of its inherited interfaces, + terminating when IDispatch or IUnknown is reached. + + You must list all header files required to find the interface itself and + all of the interfaces it inherits from, except IDispatch and IUnknown. + + Header files must be IDL-generated for the pattern matching used in the + code to work correctly. + + Args: + interface_name: 'IWebBrowser2' + header_files: ['c:\\platform_sdk\\files\\Include\\exdisp.h', + 'c:\\platform_sdk\\files\\Include\\oaidl.h'] + + Returns: + 'MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetWindow, HRESULT()); ...' + """ + mocker = Mocker() + mocker.AddHeaders(header_files) + return '\n'.join(mocker.MockInterface(interface_name)) + + +def Main(args): + if not args or len(args) < 2: + print __doc__ + return 1 + else: + print _FILE_HEADER + print MockMethods(args[0], args[1:]) + return 0 + + +if __name__ == '__main__': + sys.exit(Main(sys.argv[1:])) diff --git a/ceee/testing/utils/dispex_mocks.h b/ceee/testing/utils/dispex_mocks.h new file mode 100644 index 0000000..cf4b04d --- /dev/null +++ b/ceee/testing/utils/dispex_mocks.h @@ -0,0 +1,129 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Mock class implementations for IDispatch/IDispatchEx. +#ifndef CEEE_TESTING_UTILS_DISPEX_MOCKS_H_ +#define CEEE_TESTING_UTILS_DISPEX_MOCKS_H_ + +#include <dispex.h> +#include "gmock/gmock.h" +#include "ceee/common/initializing_coclass.h" +#include "ceee/testing/utils/instance_count_mixin.h" + +namespace testing { + +class IDispatchExMockImpl: public IDispatchEx { + public: + // The methods in this class are code generated using this command line: + // [ tools\com_mock.py IDispatchEx "%WindowsSdkDir%\Include\dispex.h" ] + // (and then massaged slightly by hand) +#include "ceee/testing/utils/mock_idispatchex.gen" +}; + +// A mock implementation of IDispatch(Ex) with a handy set of +// functions to set up an expectation for Invoke. +class MockDispatchEx + : public CComObjectRootEx<CComSingleThreadModel>, + public InitializingCoClass<MockDispatchEx>, + public InstanceCountMixin<MockDispatchEx>, + public StrictMock<IDispatchExMockImpl> { + public: + BEGIN_COM_MAP(MockDispatchEx) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY(IDispatchEx) + END_COM_MAP() + + HRESULT Initialize(MockDispatchEx** self) { + *self = this; + return S_OK; + } + + // Expect a function invoke with 0 params. + void ExpectInvoke(DISPID disp_id) { + EXPECT_CALL(*this, Invoke(disp_id, _, _, DISPATCH_METHOD, + Field(&DISPPARAMS::cArgs, Eq(0)), _, _, _)); + } + + // Expect a function invoke with 1 param. + template <class A0> + void ExpectInvoke(DISPID disp_id, const A0& a0) { + EXPECT_CALL(*this, Invoke(disp_id, _, _, DISPATCH_METHOD, + AllOf( + Field(&DISPPARAMS::cArgs, Eq(1)), + DispParamArgEq<0>(CComVariant(a0))), + _, _, _)); + } + + // Expect a function invoke with 2 params. + template <class A0, class A1> + void ExpectInvoke(DISPID disp_id, + const A0& a0, + const A1& a1) { + // Note the reverse order of dispid params from args. + EXPECT_CALL(*this, Invoke(disp_id, _, _, DISPATCH_METHOD, + AllOf( + Field(&DISPPARAMS::cArgs, Eq(2)), + DispParamArgEq<1>(CComVariant(a0)), + DispParamArgEq<0>(CComVariant(a1))), + _, _, _)); + } + + // Expect a function invoke with 3 params. + template <class A0, class A1, class A2> + void ExpectInvoke(DISPID disp_id, + const A0& a0, + const A1& a1, + const A2& a2) { + // Note the reverse order of dispid params from args. + EXPECT_CALL(*this, Invoke(disp_id, _, _, DISPATCH_METHOD, + AllOf( + Field(&DISPPARAMS::cArgs, Eq(3)), + DispParamArgEq<2>(CComVariant(a0)), + DispParamArgEq<1>(CComVariant(a1)), + DispParamArgEq<0>(CComVariant(a2))), + _, _, _)); + } + + // Expect a function invoke with 4 params. + template <class A0, class A1, class A2, class A3> + void ExpectInvoke(DISPID disp_id, + const A0& a0, + const A1& a1, + const A2& a2, + const A3& a3) { + // Note the reverse order of dispid params from args. + EXPECT_CALL(*this, Invoke(disp_id, _, _, DISPATCH_METHOD, + AllOf( + Field(&DISPPARAMS::cArgs, Eq(4)), + DispParamArgEq<3>(CComVariant(a0)), + DispParamArgEq<2>(CComVariant(a1)), + DispParamArgEq<1>(CComVariant(a2)), + DispParamArgEq<0>(CComVariant(a3))), + _, _, _)); + } + + // Expect a function invoke with 5 params. + template <class A0, class A1, class A2, class A3, class A4> + void ExpectInvoke(DISPID disp_id, + const A0& a0, + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4) { + // Note the reverse order of dispid params from args. + EXPECT_CALL(*this, Invoke(disp_id, _, _, DISPATCH_METHOD, + AllOf(Field(&DISPPARAMS::cArgs, Eq(5)), + AllOf( + DispParamArgEq<4>(CComVariant(a0)), + DispParamArgEq<3>(CComVariant(a1)), + DispParamArgEq<2>(CComVariant(a2)), + DispParamArgEq<1>(CComVariant(a3)), + DispParamArgEq<0>(CComVariant(a4)))), + _, _, _)); + } +}; + +} // testing + +#endif // CEEE_TESTING_UTILS_DISPEX_MOCKS_H_ diff --git a/ceee/testing/utils/gflag_utils.cc b/ceee/testing/utils/gflag_utils.cc new file mode 100644 index 0000000..b30553a --- /dev/null +++ b/ceee/testing/utils/gflag_utils.cc @@ -0,0 +1,157 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Implementation of the launch with gflags function. +#include "ceee/testing/utils/gflag_utils.h" + +#include "ceee/testing/utils/nt_internals.h" +#include <shlwapi.h> + +namespace testing { + +using nt_internals::NTSTATUS; +using nt_internals::NtQueryInformationProcess; + +HRESULT WriteProcessGFlags(HANDLE process, DWORD gflags) { + nt_internals::PROCESS_BASIC_INFORMATION basic_info = {}; + ULONG return_len = 0; + NTSTATUS status = + NtQueryInformationProcess(process, + nt_internals::ProcessBasicInformation, + &basic_info, + sizeof(basic_info), + &return_len); + if (0 != status) + return HRESULT_FROM_NT(status); + else if (return_len != sizeof(basic_info)) + return E_UNEXPECTED; + + LPVOID gflags_location = + reinterpret_cast<char*>(basic_info.PebBaseAddress) + kGFlagsPEBOffset; + + SIZE_T num_written = 0; + if (!::WriteProcessMemory(process, + gflags_location, + &gflags, + sizeof(gflags), + &num_written) || + sizeof(gflags) != num_written) + return HRESULT_FROM_WIN32(::GetLastError()); + + return S_OK; +} + +HRESULT CreateProcessWithGFlags(LPCWSTR application_name, + LPWSTR command_line, + LPSECURITY_ATTRIBUTES process_attributes, + LPSECURITY_ATTRIBUTES thread_attributes, + BOOL inherit_handles, + DWORD creation_flags, + LPVOID environment, + LPCWSTR current_directory, + LPSTARTUPINFOW startup_info, + LPPROCESS_INFORMATION proc_info, + DWORD gflags) { + // Create the process, but make sure it's suspended at creation. + BOOL created = ::CreateProcess(application_name, + command_line, + process_attributes, + thread_attributes, + inherit_handles, + creation_flags | CREATE_SUSPENDED, + environment, + current_directory, + startup_info, + proc_info); + if (!created) + return HRESULT_FROM_WIN32(::GetLastError()); + + // If we succeeded, go through and write the new process' + // PEB with the requested gflags. + HRESULT hr = WriteProcessGFlags(proc_info->hProcess, gflags); + if (FAILED(hr)) { + // We failed to write the gflags value, clean up and bail. + ::TerminateProcess(proc_info->hProcess, 0); + ::CloseHandle(proc_info->hProcess); + ::CloseHandle(proc_info->hThread); + + return hr; + } + + // Resume the main thread unless suspended creation was requested. + if (!(creation_flags & CREATE_SUSPENDED)) + ::ResumeThread(proc_info->hThread); + + return hr; +} + +HRESULT RelaunchWithGFlags(DWORD wanted_gflags) { + DWORD current_gflags = nt_internals::RtlGetNtGlobalFlags(); + + // Do we already have all the wanted_gflags? + if (wanted_gflags == (wanted_gflags & current_gflags)) + return S_FALSE; + + wchar_t app_name[MAX_PATH] = {}; + if (!::GetModuleFileName(NULL, app_name, ARRAYSIZE(app_name))) + return HRESULT_FROM_WIN32(::GetLastError()); + + // If there is a GFlags registry entry for our executable, we don't + // want to relaunch because it's futile. The spawned process would pick + // up the registry settings in preference to ours, and we'd effectively + // have a fork bomb on our hands. + wchar_t key_name[MAX_PATH] = + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\" + L"Image File Execution Options\\"; + + if (0 != wcscat_s(key_name, ::PathFindFileName(app_name))) + return E_UNEXPECTED; + + HKEY key = NULL; + LONG err = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, key_name, 0, KEY_READ, &key); + if (err == ERROR_SUCCESS) { + // There's a setting for this executable in registry, does it + // specify global flags? + DWORD data_len = 0; + err = ::RegQueryValueEx(key, L"GlobalFlag", NULL, NULL, NULL, &data_len); + + // Close the key. + ::RegCloseKey(key); + + if (err == ERROR_SUCCESS) { + // We can't relaunch because we'd run the risk of a fork bomb. + return E_FAIL; + } + } + + wchar_t current_dir[MAX_PATH] = {}; + if (!::GetCurrentDirectory(ARRAYSIZE(current_dir), current_dir)) + return HRESULT_FROM_WIN32(::GetLastError()); + + STARTUPINFO startup_info = {}; + ::GetStartupInfo(&startup_info); + PROCESS_INFORMATION proc_info = {}; + HRESULT hr; + hr = CreateProcessWithGFlags(app_name, + ::GetCommandLine(), + NULL, + NULL, + FALSE, + 0, + NULL, + current_dir, + &startup_info, + &proc_info, + current_gflags | wanted_gflags); + if (SUCCEEDED(hr)) { + ::WaitForSingleObject(proc_info.hProcess, INFINITE); + DWORD exit_code = 0; + ::GetExitCodeProcess(proc_info.hProcess, &exit_code); + ::ExitProcess(exit_code); + } + + return hr; +} + +} // namespace testing diff --git a/ceee/testing/utils/gflag_utils.h b/ceee/testing/utils/gflag_utils.h new file mode 100644 index 0000000..fe88721 --- /dev/null +++ b/ceee/testing/utils/gflag_utils.h @@ -0,0 +1,125 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Utilities and declarations to do with GFlags. +#ifndef CEEE_TESTING_UTILS_GFLAG_UTILS_H_ +#define CEEE_TESTING_UTILS_GFLAG_UTILS_H_ + +#include <windows.h> + +namespace testing { + +// Offset to the NtGlobalFlags from observation of running processes. +#ifdef _WIN64 +// For 64 bit processes. +const ptrdiff_t kGFlagsPEBOffset = 0xBC; +#else +// For 32 bit processes. +const ptrdiff_t kGFlagsPEBOffset = 0x68; +#endif + +// Nt global flags values gleaned from MSDN documentation +// at e.g. http://msdn.microsoft.com/en-us/library/cc265893.aspx. + +// Stop on exception +#define FLG_STOP_ON_EXCEPTION 0x00000001 +// Show loader snaps +#define FLG_SHOW_LDR_SNAPS 0x00000002 +// Debug initial command +#define FLG_DEBUG_INITIAL_COMMAND 0x00000004 +// Stop on hung GUI +#define FLG_STOP_ON_HUNG_GUI 0x00000008 +// Enable heap tail checking +#define FLG_HEAP_ENABLE_TAIL_CHECK 0x00000010 +// Enable heap free checking +#define FLG_HEAP_ENABLE_FREE_CHECK 0x00000020 +// Enable heap parameter checking +#define FLG_HEAP_VALIDATE_PARAMETERS 0x00000040 +// Enable heap validation on call +#define FLG_HEAP_VALIDATE_ALL 0x00000080 +// Enable application verifier +#define FLG_APPLICATION_VERIFIER 0x00000100 +// Enable pool tagging (Windows 2000 and Windows XP only) +#define FLG_POOL_ENABLE_TAGGING 0x00000400 +// Enable heap tagging +#define FLG_HEAP_ENABLE_TAGGING 0x00000800 +// Create user mode stack trace database +#define FLG_USER_STACK_TRACE_DB 0x00001000 +// Create kernel mode stack trace database +#define FLG_KERNEL_STACK_TRACE_DB 0x00002000 +// Maintain a list of objects for each type +#define FLG_MAINTAIN_OBJECT_TYPELIST 0x00004000 +// Enable heap tagging by DLL +#define FLG_HEAP_ENABLE_TAG_BY_DLL 0x00008000 +// Disable stack extension +#define FLG_DISABLE_STACK_EXTENSION 0x00010000 +// Enable debugging of Win32 subsystem +#define FLG_ENABLE_CSRDEBUG 0x00020000 +// Enable loading of kernel debugger symbols +#define FLG_ENABLE_KDEBUG_SYMBOL_LOAD 0x00040000 +// Disable paging of kernel stacks +#define FLG_DISABLE_PAGE_KERNEL_STACKS 0x00080000 +// Enable system critical breaks +#define FLG_ENABLE_SYSTEM_CRIT_BREAKS 0x00100000 +// Disable heap coalesce on free +#define FLG_HEAP_DISABLE_COALESCING 0x00200000 +// Enable close exception +#define FLG_ENABLE_CLOSE_EXCEPTIONS 0x00400000 +// Enable exception logging +#define FLG_ENABLE_EXCEPTION_LOGGING 0x00800000 +// Enable object handle type tagging +#define FLG_ENABLE_HANDLE_TYPE_TAGGING 0x01000000 +// Enable page heap +#define FLG_HEAP_PAGE_ALLOCS 0x02000000 +// Debug WinLogon +#define FLG_DEBUG_INITIAL_COMMAND_EX 0x04000000 +// Buffer DbgPrint output +#define FLG_DISABLE_DBGPRINT 0x08000000 +// Early critical section event creation +#define FLG_CRITSEC_EVENT_CREATION 0x10000000 +// Load DLLs top-down (Win64 only) +#define FLG_LDR_TOP_DOWN 0x20000000 +// Enable bad handles detection +#define FLG_ENABLE_HANDLE_EXCEPTIONS 0x40000000 +// Disable protected DLL verification +#define FLG_DISABLE_PROTDLLS 0x80000000 +#define FLG_VALID_BITS 0x7FFFFFFF + + +// Writes gflags to process' PEB. +// @param process a handle the the process, must grant +// PROCESS_QUERY_INFORMATION, PROCESS_VM_WRITE and +// PROCESS_VM_OPERATION. +// @param gflags the set of nt global flags to write. +HRESULT WriteProcessGFlags(HANDLE process, DWORD gflags); + +// Creates a new process with the supplied gflags. +// Parameters are the same as for ::CreateProcess. +HRESULT CreateProcessWithGFlags(LPCWSTR application_name, + LPWSTR command_line, + LPSECURITY_ATTRIBUTES process_attributes, + LPSECURITY_ATTRIBUTES thread_attributes, + BOOL inherit_handles, + DWORD creation_flags, + LPVOID environment, + LPCWSTR current_directory, + LPSTARTUPINFOW startup_info, + LPPROCESS_INFORMATION proc_info, + DWORD gflags); + +// Relaunches the current executable with the same command +// line, environment, current directory, as this process has, +// but with the supplied gflags in addition to the current process' +// gflags. +// @param wanted_gflags the set of global flags requested. +// @return S_FALSE if the current process is already running with +// all of the supplied flags, or an appropriate error otherwise. +// @note On success, this function will not return, but will +// call ExitProcess to propagate the exit code of the +// relaunched child process. +HRESULT RelaunchWithGFlags(DWORD wanted_gflags); + +} // namespace testing + +#endif // CEEE_TESTING_UTILS_GFLAG_UTILS_H_ diff --git a/ceee/testing/utils/gflag_utils_unittest.cc b/ceee/testing/utils/gflag_utils_unittest.cc new file mode 100644 index 0000000..c48bfc7 --- /dev/null +++ b/ceee/testing/utils/gflag_utils_unittest.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Unittests for the gflags utilities. +#include "gtest/gtest.h" +#include "ceee/testing/utils/gflag_utils.h" +#include "ceee/testing/utils/nt_internals.h" + +namespace testing { + +TEST(GFlagUtilsTest, WriteProcessGFlags) { + // Get our own global flags. + ULONG old_gflags = nt_internals::RtlGetNtGlobalFlags(); + + // Toggle one bit. + ULONG new_gflags = old_gflags ^ FLG_SHOW_LDR_SNAPS; + EXPECT_HRESULT_SUCCEEDED(WriteProcessGFlags(::GetCurrentProcess(), + new_gflags)); + EXPECT_EQ(new_gflags, nt_internals::RtlGetNtGlobalFlags()); + + // Restore old global flags. + EXPECT_HRESULT_SUCCEEDED(WriteProcessGFlags(::GetCurrentProcess(), + old_gflags)); + EXPECT_EQ(old_gflags, nt_internals::RtlGetNtGlobalFlags()); +} + +} // namespace testing diff --git a/ceee/testing/utils/instance_count_mixin.cc b/ceee/testing/utils/instance_count_mixin.cc new file mode 100644 index 0000000..c6d8bdc --- /dev/null +++ b/ceee/testing/utils/instance_count_mixin.cc @@ -0,0 +1,64 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Implementation of instance count mixin base class. +#include "ceee/testing/utils/instance_count_mixin.h" +#include "base/logging.h" +#include "gtest/gtest.h" + +namespace { +// Set of existing instances. +testing::InstanceCountMixinBase::InstanceSet instances; +}; + +namespace testing { + +Lock InstanceCountMixinBase::lock_; + +InstanceCountMixinBase::InstanceCountMixinBase() { + AutoLock lock(lock_); + + instances.insert(this); +} + +InstanceCountMixinBase::~InstanceCountMixinBase() { + AutoLock lock(lock_); + + EXPECT_EQ(1, instances.erase(this)); +} + +void InstanceCountMixinBase::GetDescription(std::string* description) const { + *description = "Override GetDescription in your test classes\n" + "to get useful information about the leaked objects."; +} + +void InstanceCountMixinBase::LogLeakedInstances() { + AutoLock lock(lock_); + + InstanceSet::const_iterator it(instances.begin()); + InstanceSet::const_iterator end(instances.end()); + + for (; it != end; ++it) { + std::string description; + (*it)->GetDescription(&description); + LOG(ERROR) << "Leaked: " << description.c_str(); + } +} + +typedef InstanceCountMixinBase::InstanceSet InstanceSet; + +size_t InstanceCountMixinBase::all_instance_count() { + AutoLock lock(lock_); + return instances.size(); +} + +InstanceSet::const_iterator InstanceCountMixinBase::begin() { + return instances.begin(); +} + +InstanceSet::const_iterator InstanceCountMixinBase::end() { + return instances.end(); +} + +} // namespace testing diff --git a/ceee/testing/utils/instance_count_mixin.h b/ceee/testing/utils/instance_count_mixin.h new file mode 100644 index 0000000..11627da --- /dev/null +++ b/ceee/testing/utils/instance_count_mixin.h @@ -0,0 +1,82 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// A mixin class that makes it easy to assert on test object +// instance counts. +#ifndef CEEE_TESTING_UTILS_INSTANCE_COUNT_MIXIN_H_ +#define CEEE_TESTING_UTILS_INSTANCE_COUNT_MIXIN_H_ + +#include <string> +#include <set> + +#include "base/lock.h" + +namespace testing { + +// A base class for the InstanceCountMixin template class below. +// The base class keeps a set of all derived class instances, +// which allows tests to assert on object counts and to enumerate +// leaked objects. +class InstanceCountMixinBase { + public: + InstanceCountMixinBase(); + virtual ~InstanceCountMixinBase(); + + // Override this in subclasses to provide a description of the instance. + // @note If you find that your tests are leaking objects, it can make your + // life easier to override this function in your test classes and invoke + // LogLeakedObjects to get a handle on the type and identity of the leaked + // objects. + virtual void GetDescription(std::string* description) const; + + // Logs the description of every instance to the error log. + static void LogLeakedInstances(); + + // @return the count of all InstanceCountMixinBase-derived instances. + static size_t all_instance_count(); + + typedef std::set<InstanceCountMixinBase*> InstanceSet; + + // @return the start enumerator over the existing instances. + // @note use of this enumerator is not thread safe. + static InstanceSet::const_iterator begin(); + + // @return the end enumerator over the existing instances. + // @note use of this enumerator is not thread safe. + static InstanceSet::const_iterator end(); + + protected: + static Lock lock_; +}; + +// Inherit test classes from this class to get a per-class instance count. +// Usage: +// class TestFoo: public InstanceCountMixin<TestFoo> { +// } +// +// EXPECT_EQ(0, TestFoo::instance_count()); +template <class T> +class InstanceCountMixin : public InstanceCountMixinBase { + public: + InstanceCountMixin() { + AutoLock lock(lock_); + ++instance_count_; + } + ~InstanceCountMixin() { + AutoLock lock(lock_); + --instance_count_; + } + + // @return the number of T instances. + static size_t instance_count() { return instance_count_; } + + private: + static size_t instance_count_; +}; + +template <class T> size_t InstanceCountMixin<T>::instance_count_ = 0; + +} // namespace testing + +#endif // CEEE_TESTING_UTILS_INSTANCE_COUNT_MIXIN_H_ diff --git a/ceee/testing/utils/instance_count_mixin_unittest.cc b/ceee/testing/utils/instance_count_mixin_unittest.cc new file mode 100644 index 0000000..ceaf3c2 --- /dev/null +++ b/ceee/testing/utils/instance_count_mixin_unittest.cc @@ -0,0 +1,79 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Implementation of instance count mixin class. +#include "ceee/testing/utils/instance_count_mixin.h" +#include "gtest/gtest.h" + +namespace testing { + +class TestMixin1 : public InstanceCountMixin<TestMixin1> { + public: +}; +class TestMixin2 : public InstanceCountMixin<TestMixin2> { + public: +}; + +TEST(InstanceCountMixinTest, AllInstanceCounts) { + ASSERT_EQ(0, TestMixin1::all_instance_count()); + ASSERT_EQ(0, TestMixin2::all_instance_count()); + + { + TestMixin1 one; + + ASSERT_EQ(1, TestMixin1::all_instance_count()); + ASSERT_EQ(1, TestMixin2::all_instance_count()); + + TestMixin2 two; + + ASSERT_EQ(2, TestMixin1::all_instance_count()); + ASSERT_EQ(2, TestMixin2::all_instance_count()); + } + + ASSERT_EQ(0, TestMixin1::all_instance_count()); + ASSERT_EQ(0, TestMixin2::all_instance_count()); +} + +TEST(InstanceCountMixinTest, ClassInstanceCounts) { + ASSERT_EQ(0, TestMixin1::instance_count()); + ASSERT_EQ(0, TestMixin2::instance_count()); + + { + TestMixin1 one; + + ASSERT_EQ(1, TestMixin1::instance_count()); + ASSERT_EQ(0, TestMixin2::instance_count()); + + TestMixin2 two; + + ASSERT_EQ(1, TestMixin1::instance_count()); + ASSERT_EQ(1, TestMixin2::instance_count()); + } + + ASSERT_EQ(0, TestMixin1::instance_count()); + ASSERT_EQ(0, TestMixin2::instance_count()); +} + +TEST(InstanceCountMixinTest, Iteration) { + ASSERT_TRUE(TestMixin1::begin() == TestMixin1::end()); + ASSERT_TRUE(TestMixin1::begin() == TestMixin2::begin()); + + TestMixin1 one, two; + TestMixin2 three, four; + + InstanceCountMixinBase::InstanceSet::const_iterator + it(InstanceCountMixinBase::begin()); + InstanceCountMixinBase::InstanceSet::const_iterator + end(InstanceCountMixinBase::end()); + size_t count = 0; + + for (; it != end; ++it) { + ++count; + ASSERT_TRUE(*it == &one || *it == &two || *it == &three || *it == &four); + } + + ASSERT_EQ(4, count); +} + +} // namespace testing diff --git a/ceee/testing/utils/mock_com.h b/ceee/testing/utils/mock_com.h new file mode 100644 index 0000000..36f2d65 --- /dev/null +++ b/ceee/testing/utils/mock_com.h @@ -0,0 +1,229 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Mock objects for various COM interfaces. + +#ifndef CEEE_TESTING_UTILS_MOCK_COM_H_ +#define CEEE_TESTING_UTILS_MOCK_COM_H_ + +#include <atlbase.h> +#include <atlcom.h> +#include <dispex.h> +#include <exdisp.h> +#include <mshtml.h> +#include <objsafe.h> +#include <tlogstg.h> + +#include "third_party/activscp/activscp.h" +#include "third_party/activscp/activdbg.h" + +#include "testing/gmock/include/gmock/gmock.h" + +namespace testing { + +class IOleObjecMockImpl: public IOleObject { + public: + // The methods in this class are code generated using this command line: + // [ ceee\testing\utils\com_mock.py IOleObject \ + // "%WindowsSdkDir%\Include\OleIdl.h" ] +#include "ceee/testing/utils/mock_ioleobject.gen" +}; + +class IServiceProviderMockImpl : public IServiceProvider { + public: + MOCK_METHOD3_WITH_CALLTYPE(__stdcall, QueryService, + HRESULT(REFGUID guidService, REFIID riid, void **ppvObject)); +}; + +class IWebBrowser2MockImpl : public IDispatchImpl<IWebBrowser2, + &IID_IWebBrowser2, + &LIBID_SHDocVw, + 1, + 1> { + public: + // The methods in this class are code generated using this command line: + // [ ceee\testing\utils\com_mock.py IWebBrowser2 \ + // "%WindowsSdkDir%\Include\ExDisp.h" ] +#include "ceee/testing/utils/mock_iwebbrowser2.gen" +}; + +class IObjectSafetyMockImpl : public IObjectSafety { + public: + // The methods in this class are code generated using this command line: + // [ ceee\testing\utils\com_mock.py IObjectSafety \ + // "%WindowsSdkDir%\Include\ObjSafe.h" ] +#include "ceee/testing/utils/mock_iobjectsafety.gen" +}; + +class IActiveScriptMockImpl : public IActiveScript { + public: + // The methods in this class are code generated using this command line: + // [ ceee\testing\utils\com_mock.py IActiveScript \ + // third_party\activescp\activescp.h ] +#include "ceee/testing/utils/mock_iactivescript.gen" +}; + +class IActiveScriptParseMockImpl : public IActiveScriptParse { + public: + // Unfortunately, MOCK_METHODx only exists for x = [0, 10] + // and AddScriptlet has 11 arguments. Lucky for us, this interface + // only has 3 methods. + MOCK_METHOD0_WITH_CALLTYPE(__stdcall, InitNew, HRESULT()); + MOCK_METHOD9_WITH_CALLTYPE(__stdcall, ParseScriptText, HRESULT( + LPCOLESTR pstrCode, LPCOLESTR pstrItemName, IUnknown *punkContext, + LPCOLESTR pstrDelimiter, DWORD dwSourceContextCookie, + ULONG ulStartingLineNumber, DWORD dwFlags, VARIANT *pvarResult, + EXCEPINFO *pexcepinfo)); + + // This will not work with GMock as it will not give any indication + // as to whether it has been called or not. + HRESULT __stdcall AddScriptlet( + LPCOLESTR pstrDefaultName, LPCOLESTR pstrCode, LPCOLESTR pstrItemName, + LPCOLESTR pstrSubItemName, LPCOLESTR pstrEventName, + LPCOLESTR pstrDelimiter, DWORD dwSourceContextCookie, + ULONG ulStartingLineNumber, DWORD dwFlags, BSTR *pbstrName, + EXCEPINFO *pexcepinfo) { + return E_NOTIMPL; + } +}; + +class IActiveScriptSiteMockImpl : public IActiveScriptSite { + public: + // The methods in this class are code generated using this command line: + // [ ceee\testing\utils\com_mock.py IActiveScriptSite \ + // third_party\activescp\activescp.h ] +#include "ceee/testing/utils/mock_iactivescriptsite.gen" +}; + +class IActiveScriptSiteDebugMockImpl : public IActiveScriptSiteDebug { + public: + // The methods in this class are code generated using this command line: + // [ ceee\testing\utils\com_mock.py IActiveScriptSiteDebug \ + // third_party\activescp\activedbg.h ] +#include "ceee/testing/utils/mock_iactivescriptsitedebug.gen" +}; + +class IProcessDebugManagerMockImpl : public IProcessDebugManager { + public: + // The methods in this class are code generated using this command line: + // [ ceee\testing\utils\com_mock.py IProcessDebugManager + // third_party\activscp\activdbg.h ] +#include "ceee/testing/utils/mock_iprocessdebugmanager.gen" +}; + +class IDebugApplicationMockImpl : public IDebugApplication { + public: + // The methods in this class are code generated using this command line: + // [ ceee\testing\utils\com_mock.py IDebugApplication \ + // third_party\activscp\activdbg.h ] +#include "ceee/testing/utils/mock_idebugapplication.gen" +}; + +class IDebugDocumentHelperMockImpl : public IDebugDocumentHelper { + public: + // The methods in this class are code generated using this command line: + // [ ceee\testing\utils\com_mock.py IDebugDocumentHelper \ + // third_party\activscp\activdbg.h ] +#include "ceee/testing/utils/mock_idebugdocumenthelper.gen" +}; + +class ITravelLogStgMockImpl : public ITravelLogStg { + public: + // The methods in this class are code generated using this command line: + // [ ceee\testing\utils\com_mock.py ITravelLogStg \ + // "%WindowsSdkDir%\Include\tlogstg.h" ] +#include "ceee/testing/utils/mock_itravellogstg.gen" +}; + +class MockIServiceProvider + : public CComObjectRootEx<CComSingleThreadModel>, + public testing::StrictMock<IServiceProviderMockImpl> { + DECLARE_NOT_AGGREGATABLE(MockIServiceProvider ) + BEGIN_COM_MAP(MockIServiceProvider) + COM_INTERFACE_ENTRY(IServiceProvider) + END_COM_MAP() + DECLARE_PROTECT_FINAL_CONSTRUCT() + + HRESULT Initialize(MockIServiceProvider** browser) { + *browser = this; + return S_OK; + } +}; + +class MockIWebBrowser2 + : public CComObjectRootEx<CComSingleThreadModel>, + public StrictMock<IWebBrowser2MockImpl>, + public StrictMock<IServiceProviderMockImpl> { + public: + DECLARE_NOT_AGGREGATABLE(MockIWebBrowser2) + BEGIN_COM_MAP(MockIWebBrowser2) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY(IWebBrowser2) + COM_INTERFACE_ENTRY(IWebBrowserApp) + COM_INTERFACE_ENTRY(IWebBrowser) + COM_INTERFACE_ENTRY(IServiceProvider) + END_COM_MAP() + DECLARE_PROTECT_FINAL_CONSTRUCT() + + HRESULT Initialize(MockIWebBrowser2** browser) { + *browser = this; + return S_OK; + } +}; + +class MockIProcessDebugManager + : public CComObjectRootEx<CComSingleThreadModel>, + public StrictMock<IProcessDebugManagerMockImpl> { + public: + BEGIN_COM_MAP(MockIProcessDebugManager) + COM_INTERFACE_ENTRY(IProcessDebugManager) + END_COM_MAP() + + HRESULT Initialize(MockIProcessDebugManager** debug_manager) { + *debug_manager = this; + return S_OK; + } +}; + +class MockIDebugApplication + : public CComObjectRootEx<CComSingleThreadModel>, + public StrictMock<IDebugApplicationMockImpl> { + public: + BEGIN_COM_MAP(MockIDebugApplication) + COM_INTERFACE_ENTRY(IDebugApplication) + END_COM_MAP() + + HRESULT Initialize(MockIDebugApplication** debug_application) { + *debug_application = this; + return S_OK; + } +}; + +class MockIDebugDocumentHelper + : public CComObjectRootEx<CComSingleThreadModel>, + public StrictMock<IDebugDocumentHelperMockImpl> { + public: + BEGIN_COM_MAP(MockIDebugDocumentHelper) + COM_INTERFACE_ENTRY(IDebugDocumentHelper) + END_COM_MAP(); + + HRESULT Initialize(MockIDebugDocumentHelper** debug_document) { + *debug_document = this; + return S_OK; + } +}; + +class MockITravelLogStg : public CComObjectRootEx<CComSingleThreadModel>, + public StrictMock<ITravelLogStgMockImpl> { + public: + DECLARE_NOT_AGGREGATABLE(MockITravelLogStg) + BEGIN_COM_MAP(MockITravelLogStg) + COM_INTERFACE_ENTRY(ITravelLogStg) + END_COM_MAP() + DECLARE_PROTECT_FINAL_CONSTRUCT() +}; + +} // namespace testing + +#endif // CEEE_TESTING_UTILS_MOCK_COM_H_ diff --git a/ceee/testing/utils/mock_iactivescript.gen b/ceee/testing/utils/mock_iactivescript.gen new file mode 100644 index 0000000..8733d80 --- /dev/null +++ b/ceee/testing/utils/mock_iactivescript.gen @@ -0,0 +1,31 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Auto-generated by com_mock.py + +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, SetScriptSite, HRESULT( + IActiveScriptSite *pass)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, GetScriptSite, HRESULT( + REFIID riid, void *__RPC_FAR *ppvObject)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, SetScriptState, HRESULT( + SCRIPTSTATE ss)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetScriptState, HRESULT( + SCRIPTSTATE *pssState)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, Close, HRESULT()); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, AddNamedItem, HRESULT( + LPCOLESTR pstrName, DWORD dwFlags)); +MOCK_METHOD4_WITH_CALLTYPE(__stdcall, AddTypeLib, HRESULT( + REFGUID rguidTypeLib, DWORD dwMajor, DWORD dwMinor, DWORD dwFlags)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, GetScriptDispatch, HRESULT( + LPCOLESTR pstrItemName, IDispatch *__RPC_FAR *ppdisp)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetCurrentScriptThreadID, HRESULT( + SCRIPTTHREADID *pstidThread)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, GetScriptThreadID, HRESULT( + DWORD dwWin32ThreadId, SCRIPTTHREADID *pstidThread)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, GetScriptThreadState, HRESULT( + SCRIPTTHREADID stidThread, SCRIPTTHREADSTATE *pstsState)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, InterruptScriptThread, HRESULT( + SCRIPTTHREADID stidThread, const EXCEPINFO *pexcepinfo, DWORD dwFlags)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, Clone, HRESULT( + IActiveScript *__RPC_FAR *ppscript)); diff --git a/ceee/testing/utils/mock_iactivescriptsite.gen b/ceee/testing/utils/mock_iactivescriptsite.gen new file mode 100644 index 0000000..d6b18ca --- /dev/null +++ b/ceee/testing/utils/mock_iactivescriptsite.gen @@ -0,0 +1,20 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Auto-generated by com_mock.py + +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetLCID, HRESULT(LCID *plcid)); +MOCK_METHOD4_WITH_CALLTYPE(__stdcall, GetItemInfo, HRESULT( + LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown *__RPC_FAR *ppiunkItem, + ITypeInfo *__RPC_FAR *ppti)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetDocVersionString, HRESULT( + BSTR *pbstrVersion)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, OnScriptTerminate, HRESULT( + const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, OnStateChange, HRESULT( + SCRIPTSTATE ssScriptState)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, OnScriptError, HRESULT( + IActiveScriptError *pscripterror)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, OnEnterScript, HRESULT()); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, OnLeaveScript, HRESULT()); diff --git a/ceee/testing/utils/mock_iactivescriptsitedebug.gen b/ceee/testing/utils/mock_iactivescriptsitedebug.gen new file mode 100644 index 0000000..7714dc3 --- /dev/null +++ b/ceee/testing/utils/mock_iactivescriptsitedebug.gen @@ -0,0 +1,16 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Auto-generated by com_mock.py + +MOCK_METHOD4_WITH_CALLTYPE(__stdcall, GetDocumentContextFromPosition, HRESULT( + DWORD dwSourceContext, ULONG uCharacterOffset, ULONG uNumChars, + IDebugDocumentContext *__RPC_FAR *ppsc)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetApplication, HRESULT( + IDebugApplication *__RPC_FAR *ppda)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetRootApplicationNode, HRESULT( + IDebugApplicationNode *__RPC_FAR *ppdanRoot)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, OnScriptErrorDebug, HRESULT( + IActiveScriptErrorDebug *pErrorDebug, BOOL *pfEnterDebugger, + BOOL *pfCallOnScriptErrorWhenContinuing)); diff --git a/ceee/testing/utils/mock_idebugapplication.gen b/ceee/testing/utils/mock_idebugapplication.gen new file mode 100644 index 0000000..50c542f --- /dev/null +++ b/ceee/testing/utils/mock_idebugapplication.gen @@ -0,0 +1,62 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Auto-generated by com_mock.py + +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, ResumeFromBreakPoint, HRESULT( + IRemoteDebugApplicationThread *prptFocus, BREAKRESUMEACTION bra, + ERRORRESUMEACTION era)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, CauseBreak, HRESULT()); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, ConnectDebugger, HRESULT( + IApplicationDebugger *pad)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, DisconnectDebugger, HRESULT()); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetDebugger, HRESULT( + IApplicationDebugger *__RPC_FAR *pad)); +MOCK_METHOD5_WITH_CALLTYPE(__stdcall, CreateInstanceAtApplication, HRESULT( + REFCLSID rclsid, IUnknown *pUnkOuter, DWORD dwClsContext, REFIID riid, + IUnknown *__RPC_FAR *ppvObject)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, QueryAlive, HRESULT()); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, EnumThreads, HRESULT( + IEnumRemoteDebugApplicationThreads *__RPC_FAR *pperdat)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetName, HRESULT(BSTR *pbstrName)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetRootNode, HRESULT( + IDebugApplicationNode *__RPC_FAR *ppdanRoot)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, EnumGlobalExpressionContexts, HRESULT( + IEnumDebugExpressionContexts *__RPC_FAR *ppedec)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, SetName, HRESULT(LPCOLESTR pstrName)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, StepOutComplete, HRESULT()); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, DebugOutput, HRESULT(LPCOLESTR pstr)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, StartDebugSession, HRESULT()); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, HandleBreakPoint, HRESULT( + BREAKREASON br, BREAKRESUMEACTION *pbra)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, Close, HRESULT()); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, GetBreakFlags, HRESULT( + APPBREAKFLAGS *pabf, + IRemoteDebugApplicationThread *__RPC_FAR *pprdatSteppingThread)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetCurrentThread, HRESULT( + IDebugApplicationThread *__RPC_FAR *pat)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, CreateAsyncDebugOperation, HRESULT( + IDebugSyncOperation *psdo, IDebugAsyncOperation *__RPC_FAR *ppado)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, AddStackFrameSniffer, HRESULT( + IDebugStackFrameSniffer *pdsfs, DWORD *pdwCookie)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, RemoveStackFrameSniffer, HRESULT( + DWORD dwCookie)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, QueryCurrentThreadIsDebuggerThread, + HRESULT()); +MOCK_METHOD4_WITH_CALLTYPE(__stdcall, SynchronousCallInDebuggerThread, HRESULT( + IDebugThreadCall *pptc, DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, CreateApplicationNode, HRESULT( + IDebugApplicationNode *__RPC_FAR *ppdanNew)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, FireDebuggerEvent, HRESULT( + REFGUID riid, IUnknown *punk)); +MOCK_METHOD5_WITH_CALLTYPE(__stdcall, HandleRuntimeError, HRESULT( + IActiveScriptErrorDebug *pErrorDebug, IActiveScriptSite *pScriptSite, + BREAKRESUMEACTION *pbra, ERRORRESUMEACTION *perra, + BOOL *pfCallOnScriptError)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, FCanJitDebug, BOOL()); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, FIsAutoJitDebugEnabled, BOOL()); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, AddGlobalExpressionContextProvider, + HRESULT(IProvideExpressionContexts *pdsfs, DWORD *pdwCookie)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, RemoveGlobalExpressionContextProvider, + HRESULT(DWORD dwCookie)); diff --git a/ceee/testing/utils/mock_idebugdocumenthelper.gen b/ceee/testing/utils/mock_idebugdocumenthelper.gen new file mode 100644 index 0000000..35a2e14 --- /dev/null +++ b/ceee/testing/utils/mock_idebugdocumenthelper.gen @@ -0,0 +1,42 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Auto-generated by com_mock.py + +MOCK_METHOD4_WITH_CALLTYPE(__stdcall, Init, HRESULT( + IDebugApplication *pda, LPCOLESTR pszShortName, LPCOLESTR pszLongName, + TEXT_DOC_ATTR docAttr)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, Attach, HRESULT( + IDebugDocumentHelper *pddhParent)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, Detach, HRESULT()); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, AddUnicodeText, HRESULT( + LPCOLESTR pszText)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, AddDBCSText, HRESULT(LPCSTR pszText)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, SetDebugDocumentHost, HRESULT( + IDebugDocumentHost *pddh)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, AddDeferredText, HRESULT( + ULONG cChars, DWORD dwTextStartCookie)); +MOCK_METHOD5_WITH_CALLTYPE(__stdcall, DefineScriptBlock, HRESULT( + ULONG ulCharOffset, ULONG cChars, IActiveScript *pas, BOOL fScriptlet, + DWORD *pdwSourceContext)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, SetDefaultTextAttr, HRESULT( + SOURCE_TEXT_ATTR staTextAttr)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, SetTextAttributes, HRESULT( + ULONG ulCharOffset, ULONG cChars, SOURCE_TEXT_ATTR *pstaTextAttr)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, SetLongName, HRESULT( + LPCOLESTR pszLongName)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, SetShortName, HRESULT( + LPCOLESTR pszShortName)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, SetDocumentAttr, HRESULT( + TEXT_DOC_ATTR pszAttributes)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetDebugApplicationNode, HRESULT( + IDebugApplicationNode *__RPC_FAR *ppdan)); +MOCK_METHOD4_WITH_CALLTYPE(__stdcall, GetScriptBlockInfo, HRESULT( + DWORD dwSourceContext, IActiveScript *__RPC_FAR *ppasd, ULONG *piCharPos, + ULONG *pcChars)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, CreateDebugDocumentContext, HRESULT( + ULONG iCharPos, ULONG cChars, IDebugDocumentContext *__RPC_FAR *ppddc)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, BringDocumentToTop, HRESULT()); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, BringDocumentContextToTop, HRESULT( + IDebugDocumentContext *pddc)); diff --git a/ceee/testing/utils/mock_idispatchex.gen b/ceee/testing/utils/mock_idispatchex.gen new file mode 100644 index 0000000..c2423bf --- /dev/null +++ b/ceee/testing/utils/mock_idispatchex.gen @@ -0,0 +1,34 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Auto-generated by com_mock.py + +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetTypeInfoCount, HRESULT( + UINT *pctinfo)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, GetTypeInfo, HRESULT( + UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)); +MOCK_METHOD5_WITH_CALLTYPE(__stdcall, GetIDsOfNames, HRESULT( + REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, + DISPID *rgDispId)); +MOCK_METHOD8_WITH_CALLTYPE(__stdcall, Invoke, HRESULT( + DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, + DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, + UINT *puArgErr)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, GetDispID, HRESULT( + BSTR bstrName, DWORD grfdex, DISPID *pid)); +MOCK_METHOD7_WITH_CALLTYPE(__stdcall, InvokeEx, HRESULT( + DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, + EXCEPINFO *pei, IServiceProvider *pspCaller)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, DeleteMemberByName, HRESULT( + BSTR bstrName, DWORD grfdex)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, DeleteMemberByDispID, HRESULT( + DISPID id)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, GetMemberProperties, HRESULT( + DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, GetMemberName, HRESULT( + DISPID id, BSTR *pbstrName)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, GetNextDispID, HRESULT( + DWORD grfdex, DISPID id, DISPID *pid)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetNameSpaceParent, HRESULT( + IUnknown **ppunk)); diff --git a/ceee/testing/utils/mock_ihtmldocument2.gen b/ceee/testing/utils/mock_ihtmldocument2.gen new file mode 100644 index 0000000..a7ec3ae --- /dev/null +++ b/ceee/testing/utils/mock_ihtmldocument2.gen @@ -0,0 +1,145 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Auto-generated by com_mock.py + +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Script, HRESULT(IDispatch **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_all, HRESULT( + IHTMLElementCollection **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_body, HRESULT(IHTMLElement **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_activeElement, HRESULT( + IHTMLElement **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_images, HRESULT( + IHTMLElementCollection **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_applets, HRESULT( + IHTMLElementCollection **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_links, HRESULT( + IHTMLElementCollection **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_forms, HRESULT( + IHTMLElementCollection **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_anchors, HRESULT( + IHTMLElementCollection **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_title, HRESULT(BSTR v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_title, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_scripts, HRESULT( + IHTMLElementCollection **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_designMode, HRESULT(BSTR v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_designMode, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_selection, HRESULT( + IHTMLSelectionObject **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_readyState, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_frames, HRESULT( + IHTMLFramesCollection2 **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_embeds, HRESULT( + IHTMLElementCollection **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_plugins, HRESULT( + IHTMLElementCollection **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_alinkColor, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_alinkColor, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_bgColor, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_bgColor, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_fgColor, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_fgColor, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_linkColor, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_linkColor, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_vlinkColor, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_vlinkColor, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_referrer, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_location, HRESULT( + IHTMLLocation **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_lastModified, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_URL, HRESULT(BSTR v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_URL, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_domain, HRESULT(BSTR v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_domain, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_cookie, HRESULT(BSTR v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_cookie, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_expando, HRESULT(VARIANT_BOOL v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_expando, HRESULT(VARIANT_BOOL *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_charset, HRESULT(BSTR v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_charset, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_defaultCharset, HRESULT(BSTR v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_defaultCharset, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_mimeType, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_fileSize, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_fileCreatedDate, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_fileModifiedDate, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_fileUpdatedDate, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_security, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_protocol, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_nameProp, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, write, HRESULT(SAFEARRAY * psarray)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, writeln, HRESULT(SAFEARRAY * psarray)); +MOCK_METHOD5_WITH_CALLTYPE(__stdcall, open, HRESULT( + BSTR url, VARIANT name, VARIANT features, VARIANT replace, IDispatch **pomWindowResult)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, close, HRESULT()); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, clear, HRESULT()); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, queryCommandSupported, HRESULT( + BSTR cmdID, VARIANT_BOOL *pfRet)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, queryCommandEnabled, HRESULT( + BSTR cmdID, VARIANT_BOOL *pfRet)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, queryCommandState, HRESULT( + BSTR cmdID, VARIANT_BOOL *pfRet)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, queryCommandIndeterm, HRESULT( + BSTR cmdID, VARIANT_BOOL *pfRet)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, queryCommandText, HRESULT( + BSTR cmdID, BSTR *pcmdText)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, queryCommandValue, HRESULT( + BSTR cmdID, VARIANT *pcmdValue)); +MOCK_METHOD4_WITH_CALLTYPE(__stdcall, execCommand, HRESULT( + BSTR cmdID, VARIANT_BOOL showUI, VARIANT value, VARIANT_BOOL *pfRet)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, execCommandShowHelp, HRESULT( + BSTR cmdID, VARIANT_BOOL *pfRet)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, createElement, HRESULT( + BSTR eTag, IHTMLElement **newElem)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onhelp, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onhelp, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onclick, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onclick, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_ondblclick, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_ondblclick, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onkeyup, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onkeyup, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onkeydown, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onkeydown, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onkeypress, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onkeypress, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onmouseup, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onmouseup, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onmousedown, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onmousedown, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onmousemove, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onmousemove, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onmouseout, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onmouseout, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onmouseover, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onmouseover, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onreadystatechange, HRESULT( + VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onreadystatechange, HRESULT( + VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onafterupdate, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onafterupdate, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onrowexit, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onrowexit, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onrowenter, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onrowenter, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_ondragstart, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_ondragstart, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onselectstart, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onselectstart, HRESULT(VARIANT *p)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, elementFromPoint, HRESULT( + long x, long y, IHTMLElement **elementHit)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_parentWindow, HRESULT( + IHTMLWindow2 **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_styleSheets, HRESULT( + IHTMLStyleSheetsCollection **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onbeforeupdate, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onbeforeupdate, HRESULT( + VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onerrorupdate, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onerrorupdate, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, toString, HRESULT(BSTR *String)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, createStyleSheet, HRESULT( + BSTR bstrHref, long lIndex, IHTMLStyleSheet **ppnewStyleSheet)); diff --git a/ceee/testing/utils/mock_ihtmlwindow2.gen b/ceee/testing/utils/mock_ihtmlwindow2.gen new file mode 100644 index 0000000..b9641cb --- /dev/null +++ b/ceee/testing/utils/mock_ihtmlwindow2.gen @@ -0,0 +1,95 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Auto-generated by com_mock.py + +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, item, HRESULT( + VARIANT *pvarIndex, VARIANT *pvarResult)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_length, HRESULT(long *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_frames, HRESULT( + IHTMLFramesCollection2 **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_defaultStatus, HRESULT(BSTR v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_defaultStatus, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_status, HRESULT(BSTR v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_status, HRESULT(BSTR *p)); +MOCK_METHOD4_WITH_CALLTYPE(__stdcall, setTimeout, HRESULT( + BSTR expression, long msec, VARIANT *language, long *timerID)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, clearTimeout, HRESULT(long timerID)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, alert, HRESULT(BSTR message = L"")); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, confirm, HRESULT( + BSTR message, VARIANT_BOOL *confirmed)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, prompt, HRESULT( + BSTR message, BSTR defstr, VARIANT *textdata)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Image, HRESULT( + IHTMLImageElementFactory **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_location, HRESULT( + IHTMLLocation **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_history, HRESULT(IOmHistory **p)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, close, HRESULT()); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_opener, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_opener, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_navigator, HRESULT( + IOmNavigator **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_name, HRESULT(BSTR v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_name, HRESULT(BSTR *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_parent, HRESULT(IHTMLWindow2 **p)); +MOCK_METHOD5_WITH_CALLTYPE(__stdcall, open, HRESULT( + BSTR url, BSTR name, BSTR features, VARIANT_BOOL replace, IHTMLWindow2 **pomWindowResult)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_self, HRESULT(IHTMLWindow2 **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_top, HRESULT(IHTMLWindow2 **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_window, HRESULT(IHTMLWindow2 **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, navigate, HRESULT(BSTR url)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onfocus, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onfocus, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onblur, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onblur, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onload, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onload, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onbeforeunload, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onbeforeunload, HRESULT( + VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onunload, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onunload, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onhelp, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onhelp, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onerror, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onerror, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onresize, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onresize, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_onscroll, HRESULT(VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_onscroll, HRESULT(VARIANT *p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_document, HRESULT( + IHTMLDocument2 **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_event, HRESULT(IHTMLEventObj **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get__newEnum, HRESULT(IUnknown **p)); +MOCK_METHOD4_WITH_CALLTYPE(__stdcall, showModalDialog, HRESULT( + BSTR dialog, VARIANT *varArgIn, VARIANT *varOptions, VARIANT *varArgOut)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, showHelp, HRESULT( + BSTR helpURL, VARIANT helpArg, BSTR features)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_screen, HRESULT(IHTMLScreen **p)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Option, HRESULT( + IHTMLOptionElementFactory **p)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, focus, HRESULT()); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_closed, HRESULT(VARIANT_BOOL *p)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, blur, HRESULT()); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, scroll, HRESULT(long x, long y)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_clientInformation, HRESULT( + IOmNavigator **p)); +MOCK_METHOD4_WITH_CALLTYPE(__stdcall, setInterval, HRESULT( + BSTR expression, long msec, VARIANT *language, long *timerID)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, clearInterval, HRESULT(long timerID)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_offscreenBuffering, HRESULT( + VARIANT v)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_offscreenBuffering, HRESULT( + VARIANT *p)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, execScript, HRESULT( + BSTR code, BSTR language, VARIANT *pvarRet)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, toString, HRESULT(BSTR *String)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, scrollBy, HRESULT(long x, long y)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, scrollTo, HRESULT(long x, long y)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, moveTo, HRESULT(long x, long y)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, moveBy, HRESULT(long x, long y)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, resizeTo, HRESULT(long x, long y)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, resizeBy, HRESULT(long x, long y)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_external, HRESULT(IDispatch **p)); diff --git a/ceee/testing/utils/mock_iobjectsafety.gen b/ceee/testing/utils/mock_iobjectsafety.gen new file mode 100644 index 0000000..044ef39 --- /dev/null +++ b/ceee/testing/utils/mock_iobjectsafety.gen @@ -0,0 +1,10 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Auto-generated by com_mock.py + +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, GetInterfaceSafetyOptions, HRESULT( + REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, SetInterfaceSafetyOptions, HRESULT( + REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)); diff --git a/ceee/testing/utils/mock_ioleobject.gen b/ceee/testing/utils/mock_ioleobject.gen new file mode 100644 index 0000000..97df4e6 --- /dev/null +++ b/ceee/testing/utils/mock_ioleobject.gen @@ -0,0 +1,43 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Auto-generated by com_mock.py + +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, SetClientSite, HRESULT( + IOleClientSite *pClientSite)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetClientSite, HRESULT( + IOleClientSite **ppClientSite)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, SetHostNames, HRESULT( + LPCOLESTR szContainerApp, LPCOLESTR szContainerObj)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, Close, HRESULT(DWORD dwSaveOption)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, SetMoniker, HRESULT( + DWORD dwWhichMoniker, IMoniker *pmk)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, GetMoniker, HRESULT( + DWORD dwAssign, DWORD dwWhichMoniker, IMoniker **ppmk)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, InitFromData, HRESULT( + IDataObject *pDataObject, BOOL fCreation, DWORD dwReserved)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, GetClipboardData, HRESULT( + DWORD dwReserved, IDataObject **ppDataObject)); +MOCK_METHOD6_WITH_CALLTYPE(__stdcall, DoVerb, HRESULT( + LONG iVerb, LPMSG lpmsg, IOleClientSite *pActiveSite, LONG lindex, HWND hwndParent, LPCRECT lprcPosRect)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, EnumVerbs, HRESULT( + IEnumOLEVERB **ppEnumOleVerb)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, Update, HRESULT()); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, IsUpToDate, HRESULT()); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetUserClassID, HRESULT(CLSID *pClsid)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, GetUserType, HRESULT( + DWORD dwFormOfType, LPOLESTR *pszUserType)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, SetExtent, HRESULT( + DWORD dwDrawAspect, SIZEL *psizel)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, GetExtent, HRESULT( + DWORD dwDrawAspect, SIZEL *psizel)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, Advise, HRESULT( + IAdviseSink *pAdvSink, DWORD *pdwConnection)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, Unadvise, HRESULT(DWORD dwConnection)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, EnumAdvise, HRESULT( + IEnumSTATDATA **ppenumAdvise)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, GetMiscStatus, HRESULT( + DWORD dwAspect, DWORD *pdwStatus)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, SetColorScheme, HRESULT( + LOGPALETTE *pLogpal)); diff --git a/ceee/testing/utils/mock_iprocessdebugmanager.gen b/ceee/testing/utils/mock_iprocessdebugmanager.gen new file mode 100644 index 0000000..1835269 --- /dev/null +++ b/ceee/testing/utils/mock_iprocessdebugmanager.gen @@ -0,0 +1,16 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Auto-generated by com_mock.py + +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, CreateApplication, HRESULT( + IDebugApplication *__RPC_FAR *ppda)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetDefaultApplication, HRESULT( + IDebugApplication *__RPC_FAR *ppda)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, AddApplication, HRESULT( + IDebugApplication *pda, DWORD *pdwAppCookie)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, RemoveApplication, HRESULT( + DWORD dwAppCookie)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, CreateDebugDocumentHelper, HRESULT( + IUnknown *punkOuter, IDebugDocumentHelper *__RPC_FAR *pddh)); diff --git a/ceee/testing/utils/mock_itravellogstg.gen b/ceee/testing/utils/mock_itravellogstg.gen new file mode 100644 index 0000000..1c948ce --- /dev/null +++ b/ceee/testing/utils/mock_itravellogstg.gen @@ -0,0 +1,20 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Auto-generated by com_mock.py + +MOCK_METHOD5_WITH_CALLTYPE(__stdcall, CreateEntry, HRESULT( + LPCWSTR pszUrl, LPCWSTR pszTitle, ITravelLogEntry *ptleRelativeTo, BOOL fPrepend, ITravelLogEntry **pptle)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, TravelTo, HRESULT( + ITravelLogEntry *ptle)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, EnumEntries, HRESULT( + TLENUMF flags, IEnumTravelLogEntry **ppenum)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, FindEntries, HRESULT( + TLENUMF flags, LPCWSTR pszUrl, IEnumTravelLogEntry **ppenum)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, GetCount, HRESULT( + TLENUMF flags, DWORD *pcEntries)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, RemoveEntry, HRESULT( + ITravelLogEntry *ptle)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, GetRelativeEntry, HRESULT( + int iOffset, ITravelLogEntry **ptle)); diff --git a/ceee/testing/utils/mock_iwebbrowser2.gen b/ceee/testing/utils/mock_iwebbrowser2.gen new file mode 100644 index 0000000..5b4fa976 --- /dev/null +++ b/ceee/testing/utils/mock_iwebbrowser2.gen @@ -0,0 +1,110 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Auto-generated by com_mock.py + +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, GoBack, HRESULT()); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, GoForward, HRESULT()); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, GoHome, HRESULT()); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, GoSearch, HRESULT()); +MOCK_METHOD5_WITH_CALLTYPE(__stdcall, Navigate, HRESULT( + BSTR URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, Refresh, HRESULT()); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, Refresh2, HRESULT(VARIANT *Level)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, Stop, HRESULT()); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Application, HRESULT( + IDispatch **ppDisp)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Parent, HRESULT( + IDispatch **ppDisp)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Container, HRESULT( + IDispatch **ppDisp)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Document, HRESULT( + IDispatch **ppDisp)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_TopLevelContainer, HRESULT( + VARIANT_BOOL *pBool)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Type, HRESULT(BSTR *Type)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Left, HRESULT(long *pl)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_Left, HRESULT(long Left)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Top, HRESULT(long *pl)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_Top, HRESULT(long Top)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Width, HRESULT(long *pl)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_Width, HRESULT(long Width)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Height, HRESULT(long *pl)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_Height, HRESULT(long Height)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_LocationName, HRESULT( + BSTR *LocationName)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_LocationURL, HRESULT( + BSTR *LocationURL)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Busy, HRESULT(VARIANT_BOOL *pBool)); +MOCK_METHOD0_WITH_CALLTYPE(__stdcall, Quit, HRESULT()); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, ClientToWindow, HRESULT( + int *pcx, int *pcy)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, PutProperty, HRESULT( + BSTR Property, VARIANT vtValue)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, GetProperty, HRESULT( + BSTR Property, VARIANT *pvtValue)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Name, HRESULT(BSTR *Name)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_HWND, HRESULT(SHANDLE_PTR *pHWND)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_FullName, HRESULT(BSTR *FullName)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Path, HRESULT(BSTR *Path)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Visible, HRESULT( + VARIANT_BOOL *pBool)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_Visible, HRESULT( + VARIANT_BOOL Value)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_StatusBar, HRESULT( + VARIANT_BOOL *pBool)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_StatusBar, HRESULT( + VARIANT_BOOL Value)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_StatusText, HRESULT( + BSTR *StatusText)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_StatusText, HRESULT( + BSTR StatusText)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_ToolBar, HRESULT(int *Value)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_ToolBar, HRESULT(int Value)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_MenuBar, HRESULT( + VARIANT_BOOL *Value)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_MenuBar, HRESULT( + VARIANT_BOOL Value)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_FullScreen, HRESULT( + VARIANT_BOOL *pbFullScreen)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_FullScreen, HRESULT( + VARIANT_BOOL bFullScreen)); +MOCK_METHOD5_WITH_CALLTYPE(__stdcall, Navigate2, HRESULT( + VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers)); +MOCK_METHOD2_WITH_CALLTYPE(__stdcall, QueryStatusWB, HRESULT( + OLECMDID cmdID, OLECMDF *pcmdf)); +MOCK_METHOD4_WITH_CALLTYPE(__stdcall, ExecWB, HRESULT( + OLECMDID cmdID, OLECMDEXECOPT cmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)); +MOCK_METHOD3_WITH_CALLTYPE(__stdcall, ShowBrowserBar, HRESULT( + VARIANT *pvaClsid, VARIANT *pvarShow, VARIANT *pvarSize)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_ReadyState, HRESULT( + READYSTATE *plReadyState)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Offline, HRESULT( + VARIANT_BOOL *pbOffline)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_Offline, HRESULT( + VARIANT_BOOL bOffline)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Silent, HRESULT( + VARIANT_BOOL *pbSilent)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_Silent, HRESULT( + VARIANT_BOOL bSilent)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_RegisterAsBrowser, HRESULT( + VARIANT_BOOL *pbRegister)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_RegisterAsBrowser, HRESULT( + VARIANT_BOOL bRegister)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_RegisterAsDropTarget, HRESULT( + VARIANT_BOOL *pbRegister)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_RegisterAsDropTarget, HRESULT( + VARIANT_BOOL bRegister)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_TheaterMode, HRESULT( + VARIANT_BOOL *pbRegister)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_TheaterMode, HRESULT( + VARIANT_BOOL bRegister)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_AddressBar, HRESULT( + VARIANT_BOOL *Value)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_AddressBar, HRESULT( + VARIANT_BOOL Value)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, get_Resizable, HRESULT( + VARIANT_BOOL *Value)); +MOCK_METHOD1_WITH_CALLTYPE(__stdcall, put_Resizable, HRESULT( + VARIANT_BOOL Value)); diff --git a/ceee/testing/utils/mock_static.h b/ceee/testing/utils/mock_static.h new file mode 100644 index 0000000..07b4cda --- /dev/null +++ b/ceee/testing/utils/mock_static.h @@ -0,0 +1,161 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Framework for mocking sets of static functions using SideStep, so that +// no code change is needed in the static functions themselves. +// +// Note that tiny functions may get inlined by the compiler in debug builds, +// so this approach is not suitable for tiny functions you need to unit test in +// release mode. Mock classes based on this framework are further limited to +// use from a single thread at a time, they are non-nestable, and only intended +// for testing code. +// +// To define a new static mock class, the structure is as follows: +// +// === cut here === +// MOCK_STATIC_CLASS_BEGIN(MockClassName) +// MOCK_STATIC_INIT_BEGIN(MockClassName) +// MOCK_STATIC_INIT(IsWindow); +// MOCK_STATIC_INIT2(SomeClass::SomeStatic, SomeStatic); +// ... more MOCK_STATIC_INIT(2) macros ... +// MOCK_STATIC_INIT_END() +// +// MOCK_STATIC1(BOOL, CALLBACK, IsWindow, HWND); +// MOCK_STATIC3(BOOL,, SomeStatic, SomeClass*, int, int); +// ... more MOCK_STATICX macros ... +// MOCK_STATIC_CLASS_END(MockClassName) +// === cut here === +// +// Note that each function being mocked appears twice, once in the +// initialization map (between MOCK_STATIC_INIT_BEGIN and _END) and again +// in the method map (the MOCK_STATICX macros). For further details see +// documentation of the macros. + +#ifndef CEEE_TESTING_UTILS_MOCK_STATIC_H_ +#define CEEE_TESTING_UTILS_MOCK_STATIC_H_ + +#include "base/scoped_vector.h" +#include "ceee/testing/sidestep/auto_testing_hook.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace testing { + +// A base class for mock objects that mock sets of static functions. Used via +// MOCK_STATIC_XYZ macros below. +class MockStatic { + public: + virtual ~MockStatic() {} + + protected: + ScopedVector<sidestep::AutoTestingHookBase> hooks_; +}; + +} // namespace testing + +// Begins definition of a static mock class named ClassName. +#define MOCK_STATIC_CLASS_BEGIN(ClassName) \ + class ClassName : public testing::MockStatic { \ + public: \ + virtual ~ClassName() { \ + current_ = NULL; \ + } \ + static ClassName* GetCurrent() { return current_; } \ + private: \ + static ClassName* current_; // NOLINT + +// Begins the initialization map of a static mock class named ClassName. +#define MOCK_STATIC_INIT_BEGIN(ClassName) \ + public: \ + ClassName() { \ + current_ = this; + +// Add this to the initialization map of a static mock class to mock a +// global function named function_name. +#define MOCK_STATIC_INIT(function_name) \ + hooks_.push_back(sidestep::MakeTestingHookHolder(::##function_name, \ + &function_name##Hook)); + +// Add this to the initialization map of a static mock class to mock a +// static function that is not global (e.g. in a namespace or a class function) +// @param function The function to mock +// @param method_name The name of the mock method you are defining for this +// function using one of the MOCK_STATICX macros below. +#define MOCK_STATIC_INIT2(function, method_name) \ + hooks_.push_back(sidestep::MakeTestingHookHolder(function, \ + &method_name##Hook)); + +// Ends the initialization map of a static mock class. +#define MOCK_STATIC_INIT_END() \ + } + +// Each of the following MOCK_STATICX macros defines a mock method taking X +// arguments and a corresponding static function, used as a hook on the +// production function, that redirects calls to your mock method for the +// lifetime of the static mock object. +// +// @param return_type Return type of the function you are mocking. +// @param cconv The calling convention of the function you are mocking. +// @param name The name of the mock method. For global functions, this must +// match the name of the global function. For non-global static functions, +// it must match the method_name you provided in the MOCK_STATIC_INIT2 +// macro. +// @param t1 The type of the 1st parameter of the function. +// @param t2 The type of the 2nd parameter of the function. +// @param t3 The type of the 3rd parameter of the function. +// @param t4 The type of the 4th parameter of the function. +// @param t5 The type of the 5th parameter of the function. +// @param t6 The type of the 6th parameter of the function. +// @param t7 The type of the 7th parameter of the function. +#define MOCK_STATIC0(return_type, cconv, name) \ + MOCK_METHOD0(name, return_type()); \ + static return_type cconv name##Hook () { \ + return GetCurrent()->name(); \ + } // NOLINT +#define MOCK_STATIC1(return_type, cconv, name, t1) \ + MOCK_METHOD1(name, return_type(t1)); \ + static return_type cconv name##Hook (t1 p1) { \ + return GetCurrent()->name(p1); \ + } // NOLINT +#define MOCK_STATIC2(return_type, cconv, name, t1, t2) \ + MOCK_METHOD2(name, return_type(t1, t2)); \ + static return_type cconv name##Hook (t1 p1, t2 p2) { \ + return GetCurrent()->name(p1, p2); \ + } // NOLINT +#define MOCK_STATIC3(return_type, cconv, name, t1, t2, t3) \ + MOCK_METHOD3(name, return_type(t1, t2, t3)); \ + static return_type cconv name##Hook (t1 p1, t2 p2, t3 p3) { \ + return GetCurrent()->name(p1, p2, p3); \ + } // NOLINT +#define MOCK_STATIC4(return_type, cconv, name, t1, t2, t3, t4) \ + MOCK_METHOD4(name, return_type(t1, t2, t3, t4)); \ + static return_type cconv name##Hook (t1 p1, t2 p2, t3 p3, t4 p4) { \ + return GetCurrent()->name(p1, p2, p3, p4); \ + } // NOLINT +#define MOCK_STATIC5(return_type, cconv, name, \ + t1, t2, t3, t4, t5) \ + MOCK_METHOD5(name, return_type(t1, t2, t3, t4, t5)); \ + static return_type cconv name##Hook (t1 p1, t2 p2, t3 p3, t4 p4, t5 p5) { \ + return GetCurrent()->name(p1, p2, p3, p4, p5); \ + } // NOLINT +#define MOCK_STATIC6(return_type, cconv, name, \ + t1, t2, t3, t4, t5, t6) \ + MOCK_METHOD6(name, return_type(t1, t2, t3, t4, t5, t6)); \ + static return_type cconv name##Hook ( \ + t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6) { \ + return GetCurrent()->name(p1, p2, p3, p4, p5, p6); \ + } // NOLINT +#define MOCK_STATIC7(return_type, cconv, name, \ + t1, t2, t3, t4, t5, t6, t7) \ + MOCK_METHOD7(name, return_type(t1, t2, t3, t4, t5, t6, t7)); \ + static return_type cconv name##Hook ( \ + t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6, t7 p7) { \ + return GetCurrent()->name(p1, p2, p3, p4, p5, p6, p7); \ + } // NOLINT + +// Ends definition of a static mock class named ClassName. +#define MOCK_STATIC_CLASS_END(ClassName) \ + }; \ + __declspec(selectany) ClassName* ClassName::current_ = NULL; + +#endif // CEEE_TESTING_UTILS_MOCK_STATIC_H_ diff --git a/ceee/testing/utils/mock_win32.h b/ceee/testing/utils/mock_win32.h new file mode 100644 index 0000000..e022964 --- /dev/null +++ b/ceee/testing/utils/mock_win32.h @@ -0,0 +1,119 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Win32 mocks. + +#ifndef CEEE_TESTING_UTILS_MOCK_WIN32_H_ +#define CEEE_TESTING_UTILS_MOCK_WIN32_H_ + +// atlwin.h re#defines SetWindowLongPtr and it caused a build error here. +#ifdef __ATLWIN_H__ +#error "Do not include <atlwin.h> ahead of this." +#endif + +#include <windows.h> + +#include "ceee/testing/sidestep/auto_testing_hook.h" +#include "ceee/testing/utils/mock_static.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace testing { + +// Mock object for some Kernel32 functions. +MOCK_STATIC_CLASS_BEGIN(MockKernel32) + MOCK_STATIC_INIT_BEGIN(MockKernel32) + MOCK_STATIC_INIT(GetModuleFileName); + MOCK_STATIC_INIT(GetNativeSystemInfo); + MOCK_STATIC_INIT(OpenThread); + MOCK_STATIC_INIT(GetCommandLine); + MOCK_STATIC_INIT_END() + + MOCK_STATIC3(DWORD, WINAPI, GetModuleFileName, HMODULE, LPTSTR, DWORD); + MOCK_STATIC1(void, WINAPI, GetNativeSystemInfo, LPSYSTEM_INFO); + MOCK_STATIC3(HANDLE, CALLBACK, OpenThread, DWORD, BOOL, DWORD); + MOCK_STATIC0(LPTSTR, WINAPI, GetCommandLine); +MOCK_STATIC_CLASS_END(MockKernel32) + +// Separate from MockKernel32 since here be dragons. +// First off, putting the LoadLibrary functions with the rest of MockUser32 +// is not a good idea since we want to avoid hooking these functions whenever +// the others are used. Also, for some undetermined reason, they just don't +// work when coupled in the MockUser32 class. So chances are you cannot use both +// MockLoadLibrary and MockUser32 in the same test... +MOCK_STATIC_CLASS_BEGIN(MockLoadLibrary) + MOCK_STATIC_INIT_BEGIN(MockLoadLibrary) + MOCK_STATIC_INIT(LoadLibrary); + MOCK_STATIC_INIT(GetProcAddress); + MOCK_STATIC_INIT(FreeLibrary); + MOCK_STATIC_INIT_END() + + MOCK_STATIC1(HINSTANCE, WINAPI, LoadLibrary, LPCTSTR); + MOCK_STATIC2(FARPROC, WINAPI, GetProcAddress, HMODULE, LPCSTR); + MOCK_STATIC1(BOOL, WINAPI, FreeLibrary, HMODULE); +MOCK_STATIC_CLASS_END(MockLoadLibrary) + +// Mock object for some User32 functions. +MOCK_STATIC_CLASS_BEGIN(MockUser32) + MOCK_STATIC_INIT_BEGIN(MockUser32) + MOCK_STATIC_INIT(DefWindowProc); + MOCK_STATIC_INIT(EnumChildWindows); + MOCK_STATIC_INIT(EnumWindows); + MOCK_STATIC_INIT(IsWindow); + MOCK_STATIC_INIT(IsWindowVisible); + MOCK_STATIC_INIT(GetClassName); + MOCK_STATIC_INIT(GetForegroundWindow); + MOCK_STATIC_INIT(GetParent); + MOCK_STATIC_INIT(GetTopWindow); + MOCK_STATIC_INIT(GetWindow); + MOCK_STATIC_INIT(GetWindowRect); + MOCK_STATIC_INIT(GetWindowThreadProcessId); + MOCK_STATIC_INIT(MoveWindow); + MOCK_STATIC_INIT(PostMessage); + MOCK_STATIC_INIT(SendMessage); + MOCK_STATIC_INIT(SetProp); + MOCK_STATIC_INIT(SetWindowLongPtr); + MOCK_STATIC_INIT(SetWindowPos); + MOCK_STATIC_INIT_END() + + MOCK_STATIC4(LRESULT, CALLBACK, DefWindowProc, HWND, UINT, WPARAM, LPARAM); + MOCK_STATIC3(BOOL, CALLBACK, EnumChildWindows, HWND, WNDENUMPROC, LPARAM); + MOCK_STATIC2(BOOL, CALLBACK, EnumWindows, WNDENUMPROC, LPARAM); + MOCK_STATIC1(BOOL, CALLBACK, IsWindow, HWND); + MOCK_STATIC1(BOOL, CALLBACK, IsWindowVisible, HWND); + MOCK_STATIC3(int, CALLBACK, GetClassName, HWND, LPTSTR, int); + MOCK_STATIC0(HWND, CALLBACK, GetForegroundWindow); + MOCK_STATIC1(HWND, CALLBACK, GetParent, HWND); + MOCK_STATIC1(HWND, CALLBACK, GetTopWindow, HWND); + MOCK_STATIC2(HWND, CALLBACK, GetWindow, HWND, UINT); + MOCK_STATIC2(BOOL, CALLBACK, GetWindowRect, HWND, LPRECT); + MOCK_STATIC2(DWORD, CALLBACK, GetWindowThreadProcessId, HWND, LPDWORD); + MOCK_STATIC6(BOOL, CALLBACK, MoveWindow, + HWND, int, int, int, int, BOOL); + MOCK_STATIC4(BOOL, CALLBACK, PostMessage, HWND, UINT, WPARAM, LPARAM); + MOCK_STATIC4(LRESULT, CALLBACK, SendMessage, HWND, UINT, WPARAM, LPARAM); + MOCK_STATIC3(BOOL, CALLBACK, SetProp, HWND, LPCWSTR, HANDLE); + MOCK_STATIC3(LONG_PTR, CALLBACK, SetWindowLongPtr, HWND, int, LONG_PTR); + MOCK_STATIC7(BOOL, CALLBACK, SetWindowPos, + HWND, HWND, int, int, int, int, UINT); +MOCK_STATIC_CLASS_END(MockUser32) + +// Static functions for (effectively) mocking GetNativeSystemInfo +class NativeSystemInfoMockTool { + public: + static void SetEnvironment_x86(LPSYSTEM_INFO psys_info) { + memset(psys_info, 0, sizeof(*psys_info)); + psys_info->wProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL; + psys_info->dwNumberOfProcessors = 1; + } + + static void SetEnvironment_x64(LPSYSTEM_INFO psys_info) { + memset(psys_info, 0, sizeof(*psys_info)); + psys_info->wProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64; + psys_info->dwNumberOfProcessors = 4; + } +}; + +} // namespace testing + +#endif // CEEE_TESTING_UTILS_MOCK_WIN32_H_ diff --git a/ceee/testing/utils/mock_window_utils.h b/ceee/testing/utils/mock_window_utils.h new file mode 100644 index 0000000..7150111 --- /dev/null +++ b/ceee/testing/utils/mock_window_utils.h @@ -0,0 +1,40 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// window_utils mocks. + +#ifndef CEEE_TESTING_UTILS_MOCK_WINDOW_UTILS_H_ +#define CEEE_TESTING_UTILS_MOCK_WINDOW_UTILS_H_ + +#include <set> + +#include "ceee/testing/utils/mock_static.h" +#include "ceee/common/window_utils.h" + +namespace testing { + +// Mock object for some User32 functions. +MOCK_STATIC_CLASS_BEGIN(MockWindowUtils) + MOCK_STATIC_INIT_BEGIN(MockWindowUtils) + MOCK_STATIC_INIT2(window_utils::IsWindowClass, IsWindowClass); + MOCK_STATIC_INIT2(window_utils::IsWindowThread, IsWindowThread); + MOCK_STATIC_INIT2(window_utils::GetTopLevelParent, GetTopLevelParent); + MOCK_STATIC_INIT2(window_utils::WindowHasNoThread, WindowHasNoThread); + MOCK_STATIC_INIT2(window_utils::FindDescendentWindow, FindDescendentWindow); + MOCK_STATIC_INIT2(window_utils::FindTopLevelWindows, FindTopLevelWindows); + MOCK_STATIC_INIT_END() + + MOCK_STATIC2(bool, , IsWindowClass, HWND, const std::wstring&); + MOCK_STATIC1(bool, , IsWindowThread, HWND); + MOCK_STATIC1(HWND, , GetTopLevelParent, HWND); + MOCK_STATIC1(bool, , WindowHasNoThread, HWND); + MOCK_STATIC4(bool, , FindDescendentWindow, HWND, const std::wstring&, + bool, HWND*); + MOCK_STATIC2(void, , FindTopLevelWindows, const std::wstring&, + std::set<HWND>*); +MOCK_STATIC_CLASS_END(MockWindowUtils) + +} // namespace testing + +#endif // CEEE_TESTING_UTILS_MOCK_WINDOW_UTILS_H_ diff --git a/ceee/testing/utils/mshtml_mocks.h b/ceee/testing/utils/mshtml_mocks.h new file mode 100644 index 0000000..83b8d00 --- /dev/null +++ b/ceee/testing/utils/mshtml_mocks.h @@ -0,0 +1,16 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Autogenerated mock interface definitions for MSHTML. +// The mocked interfaces are declared in mshtml_mocks.py. To add a new +// interface, edit that file. +#ifndef CEEE_TESTING_UTILS_MSHTML_MOCKS_H_ +#define CEEE_TESTING_UTILS_MSHTML_MOCKS_H_ + +#include <mshtml.h> +#include "gmock/gmock.h" + +#include "mshtml_mocks.gen" // NOLINT + +#endif // CEEE_TESTING_UTILS_MSHTML_MOCKS_H_ diff --git a/ceee/testing/utils/mshtml_mocks.py b/ceee/testing/utils/mshtml_mocks.py new file mode 100644 index 0000000..8691b15 --- /dev/null +++ b/ceee/testing/utils/mshtml_mocks.py @@ -0,0 +1,85 @@ +# Copyright (c) 2010 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +#!python +'''Autogenerates mock interface implementations for MSHTML interfaces.''' +import os +import string +import sys + +import com_mock + +# Adjust our module path so we can import com_mock. +script_dir = os.path.abspath(os.path.dirname(__file__)) +client_root = os.path.normpath(os.path.join(script_dir, '../../..')) + + +# The interfaces we want mocked. These have to be declared in MsHTML.h, +# and this will only work for IDispatch-derived interfaces. +# Any interface "IFoo" named here will have a corresponding IFooMockImpl +# mock implementation class in the generated output file. +_INTERFACES = [ + 'IHTMLDocument2', + 'IHTMLDocument3', + 'IHTMLDOMNode', + 'IHTMLElement', + 'IHTMLElementCollection', + 'IHTMLStyleElement', + 'IHTMLStyleSheet', + 'IHTMLWindow2', +] + + +# Find the path to the MSHTML.h include file. +_MSHTML_PATH = None +include_dirs = os.environ['INCLUDE'].split(';') +for include_dir in include_dirs: + candidate_path = os.path.join(include_dir, 'MsHTML.h') + if os.path.exists(candidate_path): + _MSHTML_PATH = candidate_path +if not _MSHTML_PATH: + print >> sys.stderr, "Could not find MsHTML.h in any INCLUDE path." + sys.exit(2) + + +# Template string for output file header. +_HEADER_TEMPLATE = '''\ +// This file is autogenerated by ${file} ** DO NOT EDIT ** + +''' + +# Template string for output file footer. +_FOOTER_TEMPLATE = '' + +# Template string for mock interface definition. +_INTERFACE_TEMPLATE = '''\ +class ${interface}MockImpl + : public IDispatchImpl<${interface}, + &IID_${interface}, + &LIBID_MSHTML, + 4, 0> { // Version 4.0 of the typelib. + public: +${mocks} +}; + +''' + + +def Main(): + mocker = com_mock.Mocker() + mocker.AddHeaders([_MSHTML_PATH]) + + header_template = string.Template(_HEADER_TEMPLATE) + print header_template.substitute(file = __file__) + + interface_template = string.Template(_INTERFACE_TEMPLATE) + for interface in _INTERFACES: + mocks = '\n'.join(mocker.MockInterface(interface)) + print interface_template.substitute(interface = interface, mocks = mocks) + + footer_template = string.Template(_FOOTER_TEMPLATE) + print footer_template.substitute(file = os.path.abspath(__file__)) + + +if __name__ == '__main__': + Main() diff --git a/ceee/testing/utils/nt_internals.cc b/ceee/testing/utils/nt_internals.cc new file mode 100644 index 0000000..7bfccd0 --- /dev/null +++ b/ceee/testing/utils/nt_internals.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// NT internal data structures and functions that rely upon them. +#include "ceee/testing/utils/nt_internals.h" + +namespace nt_internals { + +const HMODULE ntdll = ::GetModuleHandle(L"ntdll.dll"); + +NtQueryInformationThreadFunc NtQueryInformationThread = + reinterpret_cast<NtQueryInformationThreadFunc>( + ::GetProcAddress(ntdll, "NtQueryInformationThread")); + +NtQueryInformationProcessFunc NtQueryInformationProcess = + reinterpret_cast<NtQueryInformationProcessFunc>( + ::GetProcAddress(ntdll, "NtQueryInformationProcess")); + +NtQueryObjectFunc NtQueryObject = reinterpret_cast<NtQueryObjectFunc>( + ::GetProcAddress(ntdll, "NtQueryObject")); + +RtlGetNtGlobalFlagsFunc RtlGetNtGlobalFlags = + reinterpret_cast<RtlGetNtGlobalFlagsFunc>( + ::GetProcAddress(ntdll, "RtlGetNtGlobalFlags")); + +RtlNtStatusToDosErrorFunc RtlNtStatusToDosError = + reinterpret_cast<RtlNtStatusToDosErrorFunc>( + ::GetProcAddress(ntdll, "RtlNtStatusToDosError")); + +RtlQueryProcessBackTraceInformationFunc RtlQueryProcessBackTraceInformation = + reinterpret_cast<RtlQueryProcessBackTraceInformationFunc>( + ::GetProcAddress(ntdll, "RtlQueryProcessBackTraceInformation")); + +RtlCreateQueryDebugBufferFunc RtlCreateQueryDebugBuffer = + reinterpret_cast<RtlCreateQueryDebugBufferFunc>( + ::GetProcAddress(ntdll, "RtlCreateQueryDebugBuffer")); +RtlQueryProcessDebugInformationFunc RtlQueryProcessDebugInformation = + reinterpret_cast<RtlQueryProcessDebugInformationFunc>( + ::GetProcAddress(ntdll, "RtlQueryProcessDebugInformation")); +RtlDestroyQueryDebugBufferFunc RtlDestroyQueryDebugBuffer = + reinterpret_cast<RtlDestroyQueryDebugBufferFunc>( + ::GetProcAddress(ntdll, "RtlDestroyQueryDebugBuffer")); + + +PNT_TIB GetThreadTIB(HANDLE thread) { + if (!NtQueryInformationThread) + return NULL; + + THREAD_BASIC_INFO info = {}; + ULONG ret_len = 0; + NTSTATUS status = NtQueryInformationThread(thread, + ThreadBasicInformation, + &info, + sizeof(info), + &ret_len); + if (0 != status || ret_len != sizeof(info)) + return NULL; + + return reinterpret_cast<PNT_TIB>(info.TebBaseAddress); +} + +} // namespace nt_internals diff --git a/ceee/testing/utils/nt_internals.h b/ceee/testing/utils/nt_internals.h new file mode 100644 index 0000000..55ec3d4 --- /dev/null +++ b/ceee/testing/utils/nt_internals.h @@ -0,0 +1,206 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// NT internal data structures and functions that rely upon them. +#ifndef CEEE_TESTING_UTILS_NT_INTERNALS_H_ +#define CEEE_TESTING_UTILS_NT_INTERNALS_H_ + +#include <windows.h> + +namespace nt_internals { + +// The structures, constants and function declarations in here are gleaned +// from various sources, primarily from Microsoft symbol information for +// system DLLs, kernel modules and executables, as well as from MSDN help. +// For more information on the Windows NT native API, see e.g. +// the "Windows NT/2000 Native API Reference" by Gary Nebbett, +// ISBN 9781578701995. +typedef LONG NTSTATUS; +typedef DWORD KPRIORITY; +typedef WORD UWORD; + +struct CLIENT_ID { + PVOID UniqueProcess; + PVOID UniqueThread; +}; + +struct THREAD_BASIC_INFO { + NTSTATUS ExitStatus; + PVOID TebBaseAddress; + CLIENT_ID ClientId; + KAFFINITY AffinityMask; + KPRIORITY Priority; + KPRIORITY BasePriority; +}; + +enum THREADINFOCLASS { + ThreadBasicInformation, +}; + +// NtQueryInformationProcess is documented in winternl.h, but we link +// dynamically to it because we might as well. +typedef NTSTATUS (WINAPI *NtQueryInformationThreadFunc)( + IN HANDLE ThreadHandle, + IN THREADINFOCLASS ThreadInformationClass, + OUT PVOID ThreadInformation, + IN ULONG ThreadInformationLength, + OUT PULONG ReturnLength OPTIONAL); +extern NtQueryInformationThreadFunc NtQueryInformationThread; + +typedef struct _PROCESS_BASIC_INFORMATION { + NTSTATUS ExitStatus; + PVOID PebBaseAddress; + ULONG AffinityMask; + ULONG BasePriority; + ULONG_PTR UniqueProcessId; + ULONG_PTR InheritedFromUniqueProcessId; +} PROCESS_BASIC_INFORMATION; + +enum PROCESSINFOCLASS { + ProcessBasicInformation +}; + +typedef NTSTATUS (WINAPI *NtQueryInformationProcessFunc)( + IN HANDLE ProcessHandle, + IN PROCESSINFOCLASS ProcessInformationClass, + OUT PVOID ProcessInformation, + IN ULONG ProcessInformationLength, + OUT PULONG ReturnLength OPTIONAL); +extern NtQueryInformationProcessFunc NtQueryInformationProcess; + +// NtQueryObject is also documented in winternl.h, but we link +// dynamically to it because we might as well. +typedef struct _PUBLIC_OBJECT_BASIC_INFORMATION { + ULONG Attributes; + ACCESS_MASK GrantedAccess; + ULONG HandleCount; + ULONG PointerCount; + ULONG Reserved[10]; // reserved for internal use +} PUBLIC_OBJECT_BASIC_INFORMATION; + +enum OBJECT_INFORMATION_CLASS { + ObjectBasicInformation = 0, + ObjectTypeInformation = 2, +}; + +typedef NTSTATUS (WINAPI *NtQueryObjectFunc)( + IN HANDLE Handle, + IN OBJECT_INFORMATION_CLASS ObjectInformationClass, + OUT PVOID ObjectInformation, + IN ULONG ObjectInformationLength, + OUT PULONG ReturnLength); +extern NtQueryObjectFunc NtQueryObject; + +// @returns the TIB/TEB address for "thread". +extern PNT_TIB GetThreadTIB(HANDLE thread); + +// Debug-related structure declarations. +typedef struct _RTL_PROCESS_BACKTRACE_INFORMATION { + PVOID SymbolicBackTrace; + ULONG TraceCount; + USHORT Index; + USHORT Depth; + PVOID BackTrace[32]; +} RTL_PROCESS_BACKTRACE_INFORMATION, *PRTL_PROCESS_BACKTRACE_INFORMATION; + +typedef struct _RTL_PROCESS_BACKTRACES { + ULONG CommittedMemory; + ULONG ReservedMemory; + ULONG NumberOfBackTraceLookups; + ULONG NumberOfBackTraces; + RTL_PROCESS_BACKTRACE_INFORMATION BackTraces[1]; +} RTL_PROCESS_BACKTRACES, *PRTL_PROCESS_BACKTRACES; + +typedef struct _RTL_PROCESS_MODULE_INFORMATION { + ULONG Section; + PVOID MappedBase; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT OffsetToFileName; + CHAR FullPathName[256]; +} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION; + +typedef struct _RTL_PROCESS_MODULES { + ULONG NumberOfModules; + RTL_PROCESS_MODULE_INFORMATION Modules[1]; +} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES; + +typedef struct _RTL_PROCESS_MODULE_INFORMATION_EX { + ULONG NextOffset; + RTL_PROCESS_MODULE_INFORMATION BaseInfo; + ULONG ImageCheckSum; + ULONG TimeDateStamp; + PVOID DefaultBase; +} RTL_PROCESS_MODULE_INFORMATION_EX, *PRTL_PROCESS_MODULE_INFORMATION_EX; + +typedef struct _RTL_DEBUG_INFORMATION { + HANDLE SectionHandleClient; + PVOID ViewBaseClient; + PVOID ViewBaseTarget; + ULONG ViewBaseDelta; + HANDLE EventPairClient; + PVOID EventPairTarget; + HANDLE TargetProcessId; + HANDLE TargetThreadHandle; + ULONG Flags; + ULONG OffsetFree; + ULONG CommitSize; + ULONG ViewSize; + union { + PRTL_PROCESS_MODULES Modules; + PRTL_PROCESS_MODULE_INFORMATION_EX ModulesEx; + }; + PRTL_PROCESS_BACKTRACES BackTraces; + void * /*PRTL_PROCESS_HEAPS*/ Heaps; + void * /*PRTL_PROCESS_LOCKS*/ Locks; + HANDLE SpecificHeap; + HANDLE TargetProcessHandle; + void * /*RTL_PROCESS_VERIFIER_OPTIONS*/ VerifierOptions; + HANDLE ProcessHeap; + HANDLE CriticalSectionHandle; + HANDLE CriticalSectionOwnerThread; + PVOID Reserved[4]; +} RTL_DEBUG_INFORMATION, *PRTL_DEBUG_INFORMATION; + +// RtlQueryProcessDebugInformation.DebugInfoClassMask constants +#define PDI_MODULES 0x01 +#define PDI_BACKTRACE 0x02 +#define PDI_HEAPS 0x04 +#define PDI_HEAP_TAGS 0x08 +#define PDI_HEAP_BLOCKS 0x10 +#define PDI_LOCKS 0x20 + +typedef NTSTATUS (NTAPI *RtlQueryProcessBackTraceInformationFunc)( + PRTL_DEBUG_INFORMATION debug_info); +typedef PRTL_DEBUG_INFORMATION (NTAPI *RtlCreateQueryDebugBufferFunc)( + IN ULONG size, IN BOOLEAN event_pair); +typedef NTSTATUS (NTAPI *RtlQueryProcessDebugInformationFunc)( + IN ULONG ProcessId, IN ULONG DebugInfoClassMask, + IN OUT PRTL_DEBUG_INFORMATION DebugBuffer); +typedef NTSTATUS (NTAPI *RtlDestroyQueryDebugBufferFunc)( + IN PRTL_DEBUG_INFORMATION DebugBuffer); + +typedef ULONG (NTAPI *RtlGetNtGlobalFlagsFunc)(); +typedef ULONG (NTAPI *RtlNtStatusToDosErrorFunc)(NTSTATUS nt_status); + +// Debug-related runtime functions. +extern RtlQueryProcessBackTraceInformationFunc + RtlQueryProcessBackTraceInformation; +extern RtlCreateQueryDebugBufferFunc + RtlCreateQueryDebugBuffer; +extern RtlQueryProcessDebugInformationFunc + RtlQueryProcessDebugInformation; +extern RtlDestroyQueryDebugBufferFunc + RtlDestroyQueryDebugBuffer; + +extern RtlGetNtGlobalFlagsFunc RtlGetNtGlobalFlags; +extern RtlNtStatusToDosErrorFunc RtlNtStatusToDosError; + +} // namespace nt_internals + +#endif // CEEE_TESTING_UTILS_NT_INTERNALS_H_ diff --git a/ceee/testing/utils/nt_internals_unittest.cc b/ceee/testing/utils/nt_internals_unittest.cc new file mode 100644 index 0000000..d6e43ee --- /dev/null +++ b/ceee/testing/utils/nt_internals_unittest.cc @@ -0,0 +1,103 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Test the functions defined in nt_internals.h. +#include "ceee/testing/utils/nt_internals.h" +#include <iostream> +#include "gtest/gtest.h" +#include "ceee/testing/utils/gflag_utils.h" + +#define STATUS_SUCCESS ((NTSTATUS)0) + +namespace nt_internals { + +TEST(NtInternalsTest, GetThreadTIB) { + ASSERT_TRUE(NULL != GetThreadTIB(::GetCurrentThread())); +} + +TEST(NtInternalsTest, NtQueryInformationThread) { + ASSERT_TRUE(NULL != NtQueryInformationThread); + THREAD_BASIC_INFO info = {}; + ULONG ret_len = 0; + NTSTATUS status = NtQueryInformationThread(::GetCurrentThread(), + ThreadBasicInformation, &info, sizeof(info), &ret_len); + EXPECT_EQ(sizeof(info), ret_len); + EXPECT_TRUE(NULL != info.TebBaseAddress); + EXPECT_EQ(reinterpret_cast<DWORD>(info.ClientId.UniqueThread), + ::GetCurrentThreadId()); + EXPECT_EQ(reinterpret_cast<DWORD>(info.ClientId.UniqueProcess), + ::GetCurrentProcessId()); +} + +TEST(NtInternalsTest, RtlCreateQueryDebugBuffer) { + ASSERT_TRUE(NULL != RtlCreateQueryDebugBuffer); + ASSERT_TRUE(NULL != RtlDestroyQueryDebugBuffer); + + PRTL_DEBUG_INFORMATION debug_info = RtlCreateQueryDebugBuffer(0, 0); + ASSERT_TRUE(NULL != debug_info); + RtlDestroyQueryDebugBuffer(debug_info); +} + +TEST(NtInternalsTest, RtlQueryProcessDebugInformation) { + ASSERT_TRUE(NULL != RtlCreateQueryDebugBuffer); + ASSERT_TRUE(NULL != RtlDestroyQueryDebugBuffer); + ASSERT_TRUE(NULL != RtlQueryProcessDebugInformation); + ASSERT_TRUE(NULL != RtlGetNtGlobalFlags); + + PRTL_DEBUG_INFORMATION debug_info = RtlCreateQueryDebugBuffer(0, 0); + ASSERT_TRUE(NULL != debug_info); + + NTSTATUS status = + RtlQueryProcessDebugInformation(::GetCurrentProcessId(), + PDI_MODULES | PDI_BACKTRACE, + debug_info); + EXPECT_EQ(STATUS_SUCCESS, status); + + ASSERT_TRUE(NULL != debug_info->Modules); + EXPECT_LT(0U, debug_info->Modules->NumberOfModules); + + // The global flag is usually set by our main function, but when + // we e.g. run under app verifier it won't. For some reason we + // get stack backtraces under app verifier even when the backtrace + // flag is not set. + // ASSERT_TRUE(FLG_USER_STACK_TRACE_DB & RtlGetNtGlobalFlags()); + + ASSERT_TRUE(NULL != debug_info->BackTraces); + EXPECT_LT(0U, debug_info->BackTraces->NumberOfBackTraces); + + status = RtlDestroyQueryDebugBuffer(debug_info); + EXPECT_EQ(STATUS_SUCCESS, status); +} + +TEST(NtInternalsTest, RtlQueryProcessBackTraceInformation) { + ASSERT_TRUE(NULL != RtlQueryProcessBackTraceInformation); + ASSERT_TRUE(NULL != RtlGetNtGlobalFlags); + + // Size gleaned from buffers returned from RtlCreateQueryDebugBuffer. + static char buf[0x00400000]; + PRTL_DEBUG_INFORMATION info = reinterpret_cast<PRTL_DEBUG_INFORMATION>(buf); + + memset(buf, 0, sizeof(buf)); + info->OffsetFree = sizeof(RTL_DEBUG_INFORMATION); + info->ViewSize = sizeof(buf); + info->CommitSize = sizeof(buf); + NTSTATUS status = RtlQueryProcessBackTraceInformation(info); + ASSERT_EQ(STATUS_SUCCESS, status); + + // The global flag is usually set by our main function, but when + // we e.g. run under app verifier it won't. For some reason we + // get stack backtraces under app verifier even when the backtrace + // flag is not set. + // ASSERT_TRUE(FLG_USER_STACK_TRACE_DB & RtlGetNtGlobalFlags()); + + ASSERT_TRUE(info->BackTraces != NULL); + ASSERT_LT(0UL, info->BackTraces->NumberOfBackTraces); +} + +TEST(NtInternalsTest, RtlGetNtGlobalFlags) { + // No crash... + RtlGetNtGlobalFlags(); +} + +} // namespace nt_internals diff --git a/ceee/testing/utils/test_utils.cc b/ceee/testing/utils/test_utils.cc new file mode 100644 index 0000000..29b0bd6 --- /dev/null +++ b/ceee/testing/utils/test_utils.cc @@ -0,0 +1,170 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Utilities for testing. + +#include "base/file_path.h" +#include "base/path_service.h" +#include "base/utf_string_conversions.h" +#include "ceee/testing/utils/test_utils.h" + +namespace testing { + +HRESULT GetConnectionCount(IUnknown* container, + REFIID connection_point_id, + size_t* num_connections) { + if (!num_connections) + return E_POINTER; + + *num_connections = 0; + + CComPtr<IConnectionPointContainer> cpc; + HRESULT hr = container->QueryInterface(&cpc); + if (FAILED(hr)) + return hr; + + CHECK(cpc != NULL); + + CComPtr<IConnectionPoint> cp; + hr = cpc->FindConnectionPoint(connection_point_id, &cp); + + CComPtr<IEnumConnections> enum_connections; + if (SUCCEEDED(hr)) + hr = cp->EnumConnections(&enum_connections); + + if (FAILED(hr)) + return hr; + + while (true) { + CONNECTDATA conn = {}; + ULONG fetched = 0; + hr = enum_connections->Next(1, &conn, &fetched); + if (FAILED(hr)) { + CHECK(conn.pUnk == NULL); + return hr; + } + if (hr == S_FALSE) { + CHECK(conn.pUnk == NULL); + return S_OK; + } + CHECK(NULL != conn.pUnk); + conn.pUnk->Release(); + + (*num_connections)++; + } + NOTREACHED(); + return E_UNEXPECTED; +} + + +LogDisabler::LogDisabler() { + initial_log_level_ = logging::GetMinLogLevel(); + logging::SetMinLogLevel(logging::LOG_FATAL + 1); +} + +LogDisabler::~LogDisabler() { + logging::SetMinLogLevel(initial_log_level_); +} + +PathServiceOverrider::PathServiceOverrider(int key, const FilePath& path) { + key_ = key; + PathService::Get(key, &original_path_); + PathService::Override(key, path); +} + +PathServiceOverrider::~PathServiceOverrider() { + PathService::Override(key_, original_path_); +} + + +namespace internal { + +void WriteVariant(const VARIANT& var, ::std::ostream* os) { + *os << "{type="; + + static struct { + VARTYPE vt; + const char *name; + } vartypes[] = { +#define TYP(x) { x, #x }, + TYP(VT_EMPTY) + TYP(VT_NULL) + TYP(VT_I2) + TYP(VT_I4) + TYP(VT_R4) + TYP(VT_R8) + TYP(VT_CY) + TYP(VT_DATE) + TYP(VT_BSTR) + TYP(VT_DISPATCH) + TYP(VT_ERROR) + TYP(VT_BOOL) + TYP(VT_VARIANT) + TYP(VT_UNKNOWN) + TYP(VT_DECIMAL) + TYP(VT_I1) + TYP(VT_UI1) + TYP(VT_UI2) + TYP(VT_UI4) + TYP(VT_I8) + TYP(VT_UI8) + TYP(VT_INT) + TYP(VT_UINT) + TYP(VT_VOID) + TYP(VT_HRESULT) + TYP(VT_PTR) + TYP(VT_SAFEARRAY) + TYP(VT_CARRAY) + TYP(VT_USERDEFINED) + TYP(VT_LPSTR) + TYP(VT_LPWSTR) + TYP(VT_RECORD) + TYP(VT_INT_PTR) + TYP(VT_UINT_PTR) + TYP(VT_FILETIME) + TYP(VT_BLOB) + TYP(VT_STREAM) + TYP(VT_STORAGE) + TYP(VT_STREAMED_OBJECT) + TYP(VT_STORED_OBJECT) + TYP(VT_BLOB_OBJECT) + TYP(VT_CF) + TYP(VT_CLSID) + TYP(VT_VERSIONED_STREAM) + TYP(VT_BSTR_BLOB) + TYP(VT_ILLEGAL) +#undef TYP + }; + + bool found = false; + for (int i = 0; !found && i < ARRAYSIZE(vartypes); ++i) { + if (vartypes[i].vt == (var.vt & VT_TYPEMASK)) { + *os << vartypes[i].name; + found = true; + } + } + + if (!found) + *os << "*ILLEGAL*"; + +#define ATTRBIT(x) if (var.vt & x) *os << "| " #x; + ATTRBIT(VT_VECTOR) + ATTRBIT(VT_ARRAY) + ATTRBIT(VT_BYREF) + ATTRBIT(VT_RESERVED) +#undef ATTRBIT + + CComVariant copy; + if (SUCCEEDED(copy.ChangeType(VT_BSTR, &var))) { + *os << ", value=\"" + << WideToUTF8(std::wstring(copy.bstrVal, ::SysStringLen(copy.bstrVal))) + << "\""; + } + + *os << "}"; +} + +} // namespace testing::internal + +} // namespace testing diff --git a/ceee/testing/utils/test_utils.gyp b/ceee/testing/utils/test_utils.gyp new file mode 100644 index 0000000..7a62688 --- /dev/null +++ b/ceee/testing/utils/test_utils.gyp @@ -0,0 +1,104 @@ +# Copyright (c) 2010 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'includes': [ + '../../../build/common.gypi', + '../../common.gypi', + ], + 'target_defaults': { + 'include_dirs': [ + '..', + ], + }, + 'targets': [ + { + 'target_name': 'test_utils', + 'type': 'static_library', + 'sources': [ + 'gflag_utils.cc', + 'gflag_utils.h', + 'instance_count_mixin.cc', + 'instance_count_mixin.h', + 'mock_com.h', + 'mock_win32.h', + 'mock_static.h', + 'mock_window_utils.h', + 'mshtml_mocks.h', + 'dispex_mocks.h', + 'nt_internals.cc', + 'nt_internals.h', + 'test_utils.cc', + 'test_utils.h', + ], + 'dependencies': [ + 'mshtml_mocks', + '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/testing/gmock.gyp:gmock', + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + 'all_dependent_settings': { + 'include_dirs': [ + '..' + ], + }, + }, + { + 'target_name': 'mshtml_mocks', + 'type': 'none', + 'sources': [ + 'mshtml_mocks.h', + 'mshtml_mocks.py', + 'com_mock.py', + '<(SHARED_INTERMEDIATE_DIR)/mshtml_mocks.gen', + ], + 'actions': [ + { + 'action_name': 'make_mshtml_mocks', + 'msvs_cygwin_shell': 0, + 'msvs_quote_cmd': 0, + 'inputs': [ + 'mshtml_mocks.py', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/mshtml_mocks.gen', + ], + 'action': [ + '<@(python)', + 'mshtml_mocks.py', + '> "<(SHARED_INTERMEDIATE_DIR)/mshtml_mocks.gen"', + ], + }, + ], + # All who use this need to be able to find the .gen file we generate. + 'all_dependent_settings': { + 'include_dirs': ['<(SHARED_INTERMEDIATE_DIR)'], + }, + }, + { + 'target_name': 'test_utils_unittests', + 'type': 'executable', + 'sources': [ + 'gflag_utils_unittest.cc', + 'instance_count_mixin_unittest.cc', + 'nt_internals_unittest.cc', + 'test_utils_unittest.cc', + 'test_utils_unittest_main.cc', + ], + 'dependencies': [ + 'test_utils', + '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/testing/gmock.gyp:gmock', + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + 'libraries': [ + 'oleacc.lib', + 'shlwapi.lib', + ], + } + ] +} diff --git a/ceee/testing/utils/test_utils.h b/ceee/testing/utils/test_utils.h new file mode 100644 index 0000000..28ddb94 --- /dev/null +++ b/ceee/testing/utils/test_utils.h @@ -0,0 +1,314 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Utilities for testing. + +#ifndef CEEE_TESTING_UTILS_TEST_UTILS_H_ +#define CEEE_TESTING_UTILS_TEST_UTILS_H_ + +#include <atlbase.h> +#include <atlcom.h> +#include <vector> + +#include "base/file_path.h" +#include "base/logging.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace testing { + +// Counts the number of connections of type connection_point_id on container. +HRESULT GetConnectionCount(IUnknown* container, + REFIID connection_point_id, + size_t* num_connections); + +// Fires event with dispid |id| on IConnectionPointImpl-derived connection +// point instance |cp| with the |num_args| arguments specified in |args|. +template <typename ConnectionPoint> +void FireEvent(ConnectionPoint *cp, DISPID id, size_t num_args, VARIANT* args) { + typedef std::vector<IUnknown*> SinkVector; + SinkVector sinks; + + // Grab all the sinks. + sinks.insert(sinks.end(), cp->m_vec.begin(), cp->m_vec.end()); + + // Grab a reference to them all in a loop. + // This does not cause an IPC, so we don't need to worry about + // reentrancy on the collection so far. + SinkVector::const_iterator it(sinks.begin()), end(sinks.end()); + for (; it != end; ++it) { + if (*it) + (*it)->AddRef(); + } + + DISPPARAMS dispparams = { args, NULL, num_args, 0 }; + + // And fire the event at them. + for (it = sinks.begin(); it != sinks.end(); ++it) { + CComDispatchDriver sink_disp(*it); + + if (sink_disp != NULL) + sink_disp->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, + &dispparams, NULL, NULL, NULL); + } + + // Release the reference we grabbed above. + for (it = sinks.begin(); it != sinks.end(); ++it) { + if (*it) + (*it)->Release(); + } +} + +// Prevents any logging actions (DCHECKs, killing the process, +// informational output, or anything) for the scope of its lifetime. +class LogDisabler { + public: + LogDisabler(); + ~LogDisabler(); + + private: + int initial_log_level_; +}; + +// Overrides a path in the PathService singleton, replacing the +// original at the end of its lifetime. +class PathServiceOverrider { +public: + PathServiceOverrider(int key, const FilePath& path); + ~PathServiceOverrider(); + +private: + int key_; + FilePath original_path_; +}; + +// GMock action to simplify writing the side effect of copying a string +// to an output parameter. +template <size_t N> +inline SetArrayArgumentActionP2<N, const char*, const char*> + CopyStringToArgument(const char* s) { + return SetArrayArgument<N>(s, s + lstrlenA(s) + 1); +} + +template <size_t N> +inline SetArrayArgumentActionP2<N, const wchar_t*, const wchar_t*> + CopyStringToArgument(const wchar_t* s) { + return SetArrayArgument<N>(s, s + lstrlenW(s) + 1); +} + +// GMock action to simplify writing the side effect of copying a wchar_t string +// to a BSTR output parameter. The wchar_t string must be in scope when the +// expected call is made. +ACTION_TEMPLATE(CopyBSTRToArgument, + HAS_1_TEMPLATE_PARAMS(size_t, N), + AND_1_VALUE_PARAMS(value)) { + StaticAssertTypeEq<const wchar_t*, value_type>(); + *::std::tr1::get<N>(args) = ::SysAllocString(value); +} + +namespace internal { + +template <size_t N, typename InputInterface> +class CopyInterfaceToArgumentAction { + public: + // Constructs an action that sets the variable pointed to by the + // N-th function argument to 'value'. + explicit CopyInterfaceToArgumentAction(InputInterface* value) + : value_(value) {} + + template <typename Result, typename ArgumentTuple> + void Perform(const ArgumentTuple& args) const { + CompileAssertTypesEqual<void, Result>(); + HRESULT hr = const_cast<CComPtr<InputInterface>&>(value_) + .CopyTo(::std::tr1::get<N>(args)); + ASSERT_HRESULT_SUCCEEDED(hr); + } + + private: + CComPtr<InputInterface> value_; +}; + +template <size_t N> +class CopyVariantToArgumentAction { + public: + // Constructs an action that sets the variable pointed to by the + // N-th function argument to 'value'. + explicit CopyVariantToArgumentAction(const VARIANT& value) : value_(value) {} + + template <typename Result, typename ArgumentTuple> + void Perform(const ArgumentTuple& args) const { + CompileAssertTypesEqual<void, Result>(); + HRESULT hr = ::VariantCopy(::std::tr1::get<N>(args), &value_); + ASSERT_HRESULT_SUCCEEDED(hr); + } + + private: + CComVariant value_; +}; + +void WriteVariant(const VARIANT& var, ::std::ostream* os); + +// Comparator for variants. +class VariantMatcher : public ::testing::MatcherInterface<VARIANT> { + public: + explicit VariantMatcher(const VARIANT& value) + : value_(value) {} + + virtual bool MatchAndExplain(VARIANT var, + MatchResultListener* listener) const { + // listener is guaranteed not to be NULL by Google Mock, but its + // stream may be NULL though (it sometimes uses DummyMatchResultListener). + if (listener->stream() != NULL) + DescribeTo(listener->stream()); + return value_ == var; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "equals "; + WriteVariant(value_, os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "does not equal "; + WriteVariant(value_, os); + } + private: + CComVariant value_; +}; + +// Comparator for variant pointers. +class VariantPointerMatcher : public ::testing::MatcherInterface<VARIANT*> { + public: + explicit VariantPointerMatcher(const VARIANT* value) + : value_(value) {} + + virtual bool MatchAndExplain(VARIANT* var, + MatchResultListener* listener) const { + // listener is guaranteed not to be NULL by Google Mock, but its + // stream may be NULL though (it sometimes uses DummyMatchResultListener). + if (listener->stream() != NULL) + DescribeTo(listener->stream()); + return value_ == *var; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "equals "; + WriteVariant(value_, os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "does not equal "; + WriteVariant(value_, os); + } + private: + CComVariant value_; +}; + +template <size_t N> +class DispParamArgMatcher : public ::testing::MatcherInterface<DISPPARAMS*> { + public: + explicit DispParamArgMatcher(const VARIANT& value) + : value_(value) {} + + virtual bool MatchAndExplain(DISPPARAMS* params, + MatchResultListener* listener) const { + // listener is guaranteed not to be NULL by Google Mock, but its + // stream may be NULL though (it sometimes uses DummyMatchResultListener). + if (listener->stream() != NULL) + DescribeTo(listener->stream()); + return params->cArgs >= N && value_ == params->rgvarg[N]; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "equals "; + WriteVariant(value_, os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "does not equal "; + WriteVariant(value_, os); + } + private: + CComVariant value_; +}; + +} // namespace internal + +// GMock action to simplify writing the side effect of copying an interface +// to an output parameter, observing COM reference counting rules. +template <size_t N, typename InputInterface> +PolymorphicAction<internal::CopyInterfaceToArgumentAction<N, InputInterface> > +CopyInterfaceToArgument(InputInterface* itf) { + return MakePolymorphicAction( + internal::CopyInterfaceToArgumentAction<N, InputInterface>(itf)); +} +template <size_t N, typename InputInterface> +PolymorphicAction<internal::CopyInterfaceToArgumentAction<N, InputInterface> > +CopyInterfaceToArgument(const CComPtr<InputInterface> &itf) { + return MakePolymorphicAction( + internal::CopyInterfaceToArgumentAction<N, InputInterface>(itf)); +} + +// GMock action to simplify writing the side effect of copying a variant +// to an output parameter, using variant copy methods. +template <size_t N> +PolymorphicAction<internal::CopyVariantToArgumentAction<N> > +CopyVariantToArgument(const VARIANT& variant) { + return MakePolymorphicAction( + internal::CopyVariantToArgumentAction<N>(variant)); +} + +// GMock matcher to compare variants. +inline Matcher<VARIANT> VariantEq(const VARIANT& var) { + return MakeMatcher(new internal::VariantMatcher(var)); +} + +// GMock matcher to compare variant pointers. +inline Matcher<VARIANT *> VariantPointerEq(const VARIANT* var) { + return MakeMatcher(new internal::VariantPointerMatcher(var)); +} + +// GMock matcher to match argument N in a DISPPARAM pointer. +// This makes it humane to match on parameters to IDispatch::Invoke. +template <size_t N> +inline Matcher<DISPPARAMS*> DispParamArgEq(const VARIANT& var) { + return MakeMatcher(new internal::DispParamArgMatcher<N>(var)); +} + +// Gmock action to add a reference to a COM object you are having a mock +// method return. +ACTION_P(AddRef, p) { + p->AddRef(); +} + +} // namespace testing + +// Allow GMock to print VARIANT values. +inline ::std::ostream& operator<<(::std::ostream& os, const VARIANT& var) { + testing::internal::WriteVariant(var, &os); + return os; +} + +// Sometimes our tests that use SideStep to stub out dependencies fail in +// release builds because one or more of the functions being hooked by +// SideStep is inlined by the compiler, and therefore cannot be hooked. +// In rare cases, we might use a noinline pragma to make such functions always +// hookable, but usually it is easier and better (so as not to impact the +// production code) to simply run the test in debug builds only. +// +// For noinline pragma, see +// http://msdn.microsoft.com/en-us/library/cx053bca(VS.71).aspx +#ifdef _DEBUG +#define TEST_DEBUG_ONLY(test_case_name, test_name) \ + TEST(test_case_name, test_name) +#define TEST_F_DEBUG_ONLY(test_fixture, test_name) \ + TEST_F(test_fixture, test_name) +#else +#define TEST_DEBUG_ONLY(test_case_name, test_name) \ + void DisabledTest##test_case_name##test_name() +#define TEST_F_DEBUG_ONLY TEST_DEBUG_ONLY +#endif + + + +#endif // CEEE_TESTING_UTILS_TEST_UTILS_H_ diff --git a/ceee/testing/utils/test_utils_unittest.cc b/ceee/testing/utils/test_utils_unittest.cc new file mode 100644 index 0000000..4ca4a4e --- /dev/null +++ b/ceee/testing/utils/test_utils_unittest.cc @@ -0,0 +1,143 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Unit tests for test utils. + +#include "ceee/testing/utils/test_utils.h" + +#include "base/scoped_vector.h" +#include "ceee/common/initializing_coclass.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +TEST(LogDisabler, ItDisables) { + testing::LogDisabler disabler; + DCHECK(false); +} + +} // namespace + +namespace { + +using testing::_; +using testing::CopyBSTRToArgument; +using testing::CopyInterfaceToArgument; +using testing::CopyStringToArgument; +using testing::CopyVariantToArgument; +using testing::DoAll; +using testing::Return; + +class IInt : public IUnknown { + public: + virtual int get() const = 0; + virtual void set(int i) = 0; +}; + +// {FA26DE41-C212-4e11-A23F-C7DC405180A2} +const GUID IID_IInt = + { 0xfa26de41, 0xc212, 0x4e11, + { 0xa2, 0x3f, 0xc7, 0xdc, 0x40, 0x51, 0x80, 0xa2 } }; + +class MockInt + : public CComObjectRootEx<CComSingleThreadModel>, + public IInt { + public: + BEGIN_COM_MAP(MockInt) + COM_INTERFACE_ENTRY_IID(IID_IInt, IInt) + END_COM_MAP() + + HRESULT Initialize(MockInt** i) { + *i = this; + return S_OK; + } + + int get() const { return i_; } + void set(int i) { i_ = i; } + + private: + int i_; +}; + +class MockGetter { + public: + MOCK_METHOD1(GetString, void(wchar_t* str)); + MOCK_METHOD1(GetBSTR, void(BSTR* str)); + MOCK_METHOD1(GetIInt, void(IInt** i)); + MOCK_METHOD1(GetVariant, void(VARIANT* var)); +}; + +const wchar_t* kStr = L"hello"; +const int kInt = 5; + +TEST(CopyStringToArgument, ItCopies) { + MockGetter getter; + + EXPECT_CALL(getter, GetString(_)).WillOnce(CopyStringToArgument<0>(kStr)); + + wchar_t* ret = new wchar_t[lstrlenW(kStr) + 1]; + getter.GetString(ret); + ASSERT_EQ(wcscmp(ret, kStr), 0); + delete[] ret; +} + +TEST(CopyBSTRToArgument, ItCopies) { + MockGetter getter; + + EXPECT_CALL(getter, GetBSTR(_)).WillOnce(CopyBSTRToArgument<0>(kStr)); + + CComBSTR expected = kStr; + CComBSTR ret; + getter.GetBSTR(&ret); + ASSERT_EQ(ret, expected); +} + +TEST(CopyInterfaceToArgument, ItCopies) { + MockGetter getter; + + { + // Cause ptr to go out of scope. + MockInt* mock_int; + CComPtr<IInt> int_ptr; + ASSERT_HRESULT_SUCCEEDED( + InitializingCoClass<MockInt>::CreateInitializedIID( + &mock_int, IID_IInt, &int_ptr)); + int_ptr->set(kInt); + + EXPECT_CALL(getter, GetIInt(_)) + .WillOnce(CopyInterfaceToArgument<0>(int_ptr)); + } + + CComPtr<IInt> int_ptr; + getter.GetIInt(&int_ptr); + ASSERT_EQ(int_ptr->get(), kInt); +} + +TEST(CopyVariantToArgument, ItCopies) { + MockGetter getter; + + { + // Cause the variant to go out of scope. + MockInt* mock_int; + CComPtr<IInt> int_ptr; + ASSERT_HRESULT_SUCCEEDED( + InitializingCoClass<MockInt>::CreateInitializedIID( + &mock_int, IID_IInt, &int_ptr)); + int_ptr->set(kInt); + + CComVariant var(int_ptr); + EXPECT_CALL(getter, GetVariant(_)).WillOnce(CopyVariantToArgument<0>(var)); + } + + CComVariant ret; + getter.GetVariant(&ret); + ASSERT_EQ(V_VT(&ret), VT_UNKNOWN); + + CComPtr<IInt> int_ptr; + ASSERT_HRESULT_SUCCEEDED(V_UNKNOWN(&ret)->QueryInterface( + IID_IInt, reinterpret_cast<void**>(&int_ptr))); + ASSERT_EQ(int_ptr->get(), kInt); +} + +} // namespace diff --git a/ceee/testing/utils/test_utils_unittest_main.cc b/ceee/testing/utils/test_utils_unittest_main.cc new file mode 100644 index 0000000..a1203ff --- /dev/null +++ b/ceee/testing/utils/test_utils_unittest_main.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Main function for common unittests. +#include <atlbase.h> +#include <atlcom.h> +#include "gtest/gtest.h" +#include "ceee/testing/utils/gflag_utils.h" + +// The global flags we want these tests to run with. +const DWORD kGlobalFlags = FLG_USER_STACK_TRACE_DB; + +// We're testing ATL code that requires a module object. +class ObligatoryModule: public CAtlDllModuleT<ObligatoryModule> { +}; + +ObligatoryModule g_obligatory_atl_module; + +int main(int argc, char **argv) { + if (!IsDebuggerPresent()) + testing::RelaunchWithGFlags(kGlobalFlags); + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} |