1// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CHROME_FRAME_VTABLE_PATCH_MANAGER_H_
6#define CHROME_FRAME_VTABLE_PATCH_MANAGER_H_
7
8#include <windows.h>
9
10#include <list>
11
12#include "base/synchronization/lock.h"
13
14struct FunctionStub;
15
16// This namespace provides methods to patch VTable methods of COM interfaces.
17namespace vtable_patch {
18
19// Internal implementation, exposed only for testing.
20namespace internal {
21
22// Replaces *entry with new_proc iff *entry is curr_proc.
23// Returns true iff *entry was rewritten.
24// Note: does not crash on access violation.
25bool ReplaceFunctionPointer(void** entry, void* new_proc, void* curr_proc);
26
27}  // namespace internal
28
29// This structure represents information about one VTable method.
30// We allocate an array of these structures per VTable that we patch to
31// remember the original method. We also use this structure to actually
32// describe the VTable patch functions
33struct MethodPatchInfo {
34  int index_;
35  PROC method_;
36  FunctionStub* stub_;
37};
38
39// Patches methods in the passed in COM interface. The indexes of the
40// methods to patch and the actual patch functions are described in the
41// array pointed to by patches.
42// @param[in] unknown  The pointer of the COM interface to patch
43// @param[in] patches  An array of MethodPatchInfo structures describing
44//  the methods to patch and the patch functions.
45//  The last entry of patches must have index_ set to -1.
46HRESULT PatchInterfaceMethods(void* unknown, MethodPatchInfo* patches);
47
48// Using the patch info provided in |patches| the function goes through the
49// list of patched methods and modifies thunks so that they no longer point
50// to a hook method but rather go straight through to the original target.
51// The thunk itself is not destroyed to support chaining.
52// @param[in] patches  An array of MethodPatchInfo structures describing
53//  the methods to patch and the patch functions.
54//  The last entry of patches must have index_ set to -1.
55HRESULT UnpatchInterfaceMethods(MethodPatchInfo* patches);
56
57// Disabled as we're not using it atm.
58#if 0
59// Used when dynamically patching zero or more (usually more than 1)
60// implementations of a particular interface.
61class DynamicPatchManager {
62 public:
63  explicit DynamicPatchManager(const MethodPatchInfo* patch_prototype);
64  ~DynamicPatchManager();
65
66  // Returns S_OK if the object was successfully patched, S_FALSE if it was
67  // already patched or an error value if something bad happened.
68  HRESULT PatchObject(void* unknown);
69
70  bool UnpatchAll();
71
72 protected:
73  struct PatchedObject {
74    void* vtable_;
75    MethodPatchInfo patch_info_[1];
76
77    // Used to match PatchedObject instances based on the vtable when
78    // searching through the patch list.
79    bool operator==(const PatchedObject& that) const {
80      return vtable_ == that.vtable_;
81    }
82  };
83
84  typedef std::list<PatchedObject*> PatchList;
85  const MethodPatchInfo* patch_prototype_;
86  mutable base::Lock patch_list_lock_;
87  PatchList patch_list_;
88};
89#endif  // disable DynamicPatchManager
90
91}  // namespace vtable_patch
92
93// Begins the declaration of a VTable patch
94// @param IFName The name of the interface to patch
95#define BEGIN_VTABLE_PATCHES(IFName) \
96    vtable_patch::MethodPatchInfo IFName##_PatchInfo[] = {
97// Defines a single method patch in a VTable
98// @param index The index of the method to patch
99// @param PatchFunction The patch function
100#define VTABLE_PATCH_ENTRY(index, PatchFunction) {\
101      index, \
102      reinterpret_cast<PROC>(PatchFunction), \
103      NULL, \
104    },
105
106#define DCHECK_IS_NOT_PATCHED(IFName) \
107    for (vtable_patch::MethodPatchInfo* it = IFName##_PatchInfo; \
108         it->index_ != -1; ++it) { \
109      DCHECK(it->stub_ == NULL); \
110    }
111
112#define DCHECK_IS_PATCHED(IFName) \
113    for (vtable_patch::MethodPatchInfo* it = IFName##_PatchInfo; \
114         it->index_ != -1; ++it) { \
115      DCHECK(it->stub_ != NULL); \
116    }
117
118// Checks if the interface is patched.  Note that only the first method
119// is checked and subsequent methods are assumed to have the same state.
120#define IS_PATCHED(IFName) \
121  (IFName##_PatchInfo[0].stub_ != NULL)
122
123// Ends the declaration of a VTable patch by adding an entry with
124// index set to -1.
125#define END_VTABLE_PATCHES() \
126      -1, NULL, NULL \
127    };
128
129#endif  // CHROME_FRAME_VTABLE_PATCH_MANAGER_H_
130