// Copyright (c) 2009 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.
#ifndef CHROME_FRAME_VTABLE_PATCH_MANAGER_H_
#define CHROME_FRAME_VTABLE_PATCH_MANAGER_H_
#include <windows.h>
#include <list>
#include "base/synchronization/lock.h"
struct FunctionStub;
// This namespace provides methods to patch VTable methods of COM interfaces.
namespace vtable_patch {
// Internal implementation, exposed only for testing.
namespace internal {
// Replaces *entry with new_proc iff *entry is curr_proc.
// Returns true iff *entry was rewritten.
// Note: does not crash on access violation.
bool ReplaceFunctionPointer(void** entry, void* new_proc, void* curr_proc);
} // namespace internal
// This structure represents information about one VTable method.
// We allocate an array of these structures per VTable that we patch to
// remember the original method. We also use this structure to actually
// describe the VTable patch functions
struct MethodPatchInfo {
int index_;
PROC method_;
FunctionStub* stub_;
};
// Patches methods in the passed in COM interface. The indexes of the
// methods to patch and the actual patch functions are described in the
// array pointed to by patches.
// @param[in] unknown The pointer of the COM interface to patch
// @param[in] patches An array of MethodPatchInfo structures describing
// the methods to patch and the patch functions.
// The last entry of patches must have index_ set to -1.
HRESULT PatchInterfaceMethods(void* unknown, MethodPatchInfo* patches);
// Using the patch info provided in |patches| the function goes through the
// list of patched methods and modifies thunks so that they no longer point
// to a hook method but rather go straight through to the original target.
// The thunk itself is not destroyed to support chaining.
// @param[in] patches An array of MethodPatchInfo structures describing
// the methods to patch and the patch functions.
// The last entry of patches must have index_ set to -1.
HRESULT UnpatchInterfaceMethods(MethodPatchInfo* patches);
// Disabled as we're not using it atm.
#if 0
// Used when dynamically patching zero or more (usually more than 1)
// implementations of a particular interface.
class DynamicPatchManager {
public:
explicit DynamicPatchManager(const MethodPatchInfo* patch_prototype);
~DynamicPatchManager();
// Returns S_OK if the object was successfully patched, S_FALSE if it was
// already patched or an error value if something bad happened.
HRESULT PatchObject(void* unknown);
bool UnpatchAll();
protected:
struct PatchedObject {
void* vtable_;
MethodPatchInfo patch_info_[1];
// Used to match PatchedObject instances based on the vtable when
// searching through the patch list.
bool operator==(const PatchedObject& that) const {
return vtable_ == that.vtable_;
}
};
typedef std::list<PatchedObject*> PatchList;
const MethodPatchInfo* patch_prototype_;
mutable base::Lock patch_list_lock_;
PatchList patch_list_;
};
#endif // disable DynamicPatchManager
} // namespace vtable_patch
// Begins the declaration of a VTable patch
// @param IFName The name of the interface to patch
#define BEGIN_VTABLE_PATCHES(IFName) \
vtable_patch::MethodPatchInfo IFName##_PatchInfo[] = {
// Defines a single method patch in a VTable
// @param index The index of the method to patch
// @param PatchFunction The patch function
#define VTABLE_PATCH_ENTRY(index, PatchFunction) {\
index, \
reinterpret_cast<PROC>(PatchFunction), \
NULL, \
},
#define DCHECK_IS_NOT_PATCHED(IFName) \
for (vtable_patch::MethodPatchInfo* it = IFName##_PatchInfo; \
it->index_ != -1; ++it) { \
DCHECK(it->stub_ == NULL); \
}
#define DCHECK_IS_PATCHED(IFName) \
for (vtable_patch::MethodPatchInfo* it = IFName##_PatchInfo; \
it->index_ != -1; ++it) { \
DCHECK(it->stub_ != NULL); \
}
// Checks if the interface is patched. Note that only the first method
// is checked and subsequent methods are assumed to have the same state.
#define IS_PATCHED(IFName) \
(IFName##_PatchInfo[0].stub_ != NULL)
// Ends the declaration of a VTable patch by adding an entry with
// index set to -1.
#define END_VTABLE_PATCHES() \
-1, NULL, NULL \
};
#endif // CHROME_FRAME_VTABLE_PATCH_MANAGER_H_