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#include "chrome_frame/function_stub.h"
6
7#include <new>
8
9#include "base/synchronization/lock.h"
10#include "base/logging.h"
11
12#ifndef _M_IX86
13#error Only x86 supported right now.
14#endif
15
16namespace {
17typedef enum AsmConstants {
18  POP_EAX = 0x58,
19  PUSH_IND = 0x35ff,
20  PUSH_EAX = 0x50,
21  JUMP_IND = 0x25ff,
22};
23
24// A quick and dirty wrapper class that allows us to defer allocating
25// the executable heap until first use, and to release it teardown.
26class ExecutableHeap {
27 public:
28  ExecutableHeap() : heap_(NULL) {
29  }
30
31  ~ExecutableHeap() {
32    if (heap_ != NULL) {
33      BOOL ret = ::HeapDestroy(heap_);
34      heap_ = NULL;
35    }
36  }
37
38  void* Allocate(size_t size) {
39    if (!heap_)
40      CreateHeap();
41
42    DCHECK(heap_);
43
44    return ::HeapAlloc(heap_, 0, size);
45  }
46
47  void Free(void* ptr) {
48    DCHECK(heap_ != NULL);
49    ::HeapFree(heap_, 0, ptr);
50  }
51
52  void CreateHeap() {
53    base::AutoLock lock(init_lock_);
54
55    if (heap_ == NULL)
56      heap_ = ::HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
57  }
58
59 private:
60  base::Lock init_lock_;
61  HANDLE heap_;
62};
63
64// Our executable heap instance, all stubs are allocated from here.
65ExecutableHeap heap_;
66
67}  // namespace
68
69extern "C" IMAGE_DOS_HEADER __ImageBase;
70
71bool FunctionStub::is_valid() const {
72  return signature_ == reinterpret_cast<HMODULE>(&__ImageBase) &&
73      !is_bypassed();
74}
75
76FunctionStub::FunctionStub(uintptr_t extra_argument, void* dest)
77    : signature_(reinterpret_cast<HMODULE>(&__ImageBase)),
78      argument_(extra_argument),
79      destination_function_(reinterpret_cast<uintptr_t>(dest)) {
80  bypass_address_ = reinterpret_cast<uintptr_t>(&stub_.pop_return_addr_);
81  Init(&stub_);
82}
83
84FunctionStub::~FunctionStub() {
85}
86
87void FunctionStub::Init(FunctionStubAsm* stub) {
88  DCHECK(stub != NULL);
89
90  stub->jump_to_bypass_ = JUMP_IND;
91  stub->bypass_target_addr_ = reinterpret_cast<uintptr_t>(&bypass_address_);
92  stub->pop_return_addr_ = POP_EAX;
93  stub->push_ = PUSH_IND;
94  stub->arg_addr_ = reinterpret_cast<uintptr_t>(&argument_);
95  stub->push_return_addr_ = PUSH_EAX;
96  stub->jump_to_target = JUMP_IND;
97  stub->target_addr_ = reinterpret_cast<uintptr_t>(&destination_function_);
98
99  // Flush the instruction cache for the newly written code.
100  BOOL ret = ::FlushInstructionCache(::GetCurrentProcess(),
101                                     stub,
102                                     sizeof(*stub));
103}
104
105void FunctionStub::BypassStub(void* new_target) {
106  set_bypass_address(reinterpret_cast<uintptr_t>(new_target));
107}
108
109FunctionStub* FunctionStub::Create(uintptr_t extra_argument, void* dest) {
110  DCHECK(dest);
111  FunctionStub* stub =
112      reinterpret_cast<FunctionStub*>(heap_.Allocate(sizeof(FunctionStub)));
113
114  if (stub != NULL)
115    new (stub) FunctionStub(extra_argument, dest);
116
117  return stub;
118}
119
120FunctionStub* FunctionStub::FromCode(void* address) {
121  // Address points to arbitrary code here, which may e.g.
122  // lie at the end of an executable segment, which in turn
123  // may terminate earlier than the last address we probe.
124  // We therefore execute under an SEH, so as not to crash
125  // on failed probes.
126  __try {
127    // Retrieve the candidata function stub.
128    FunctionStub* candidate = CONTAINING_RECORD(address, FunctionStub, stub_);
129    if (candidate->stub_.jump_to_bypass_ == JUMP_IND &&
130        candidate->signature_ == reinterpret_cast<HMODULE>(&__ImageBase)) {
131      return candidate;
132    }
133  } __except(EXCEPTION_EXECUTE_HANDLER) {
134  }
135
136  return NULL;
137}
138
139bool FunctionStub::Destroy(FunctionStub* stub) {
140  heap_.Free(stub);
141
142  return true;
143}
144