summaryrefslogtreecommitdiffstats
path: root/chrome_frame/function_stub.cc
diff options
context:
space:
mode:
authorsiggi@chromium.org <siggi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-23 21:09:02 +0000
committersiggi@chromium.org <siggi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-23 21:09:02 +0000
commitd999c7c3f4bd9c7c779b4c06ee148d3d0d227e60 (patch)
treed4fca3d3c24d56a325000a4129d11c35b548abfa /chrome_frame/function_stub.cc
parent722ed48734460567be2c97590a036480c4667815 (diff)
downloadchromium_src-d999c7c3f4bd9c7c779b4c06ee148d3d0d227e60.zip
chromium_src-d999c7c3f4bd9c7c779b4c06ee148d3d0d227e60.tar.gz
chromium_src-d999c7c3f4bd9c7c779b4c06ee148d3d0d227e60.tar.bz2
Reimplementation of FunctionStub to avoid rewriting potentially executing code for a slight improvement in thread safety.
Make VTABLE patching treadsafe to the extent possible. As-is it's now safe against itself running on other threads at lease, as well as against other similar implementations, though the inherent VM operation race is resolved by retrying. BUG=27415 TEST=Included unittests. Review URL: http://codereview.chromium.org/992008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42381 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame/function_stub.cc')
-rw-r--r--chrome_frame/function_stub.cc141
1 files changed, 141 insertions, 0 deletions
diff --git a/chrome_frame/function_stub.cc b/chrome_frame/function_stub.cc
new file mode 100644
index 0000000..59a4029
--- /dev/null
+++ b/chrome_frame/function_stub.cc
@@ -0,0 +1,141 @@
+// 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.
+#include "chrome_frame/function_stub.h"
+
+#include <new>
+#include "base/lock.h"
+#include "base/logging.h"
+
+#ifndef _M_IX86
+#error Only x86 supported right now.
+#endif
+
+namespace {
+typedef enum AsmConstants {
+ POP_EAX = 0x58,
+ PUSH_IND = 0x35ff,
+ PUSH_EAX = 0x50,
+ JUMP_IND = 0x25ff,
+};
+
+// A quick and dirty wrapper class that allows us to defer allocating
+// the executable heap until first use, and to release it teardown.
+class ExecutableHeap {
+ public:
+ ExecutableHeap() : heap_(NULL) {
+ }
+
+ ~ExecutableHeap() {
+ if (heap_ != NULL) {
+ BOOL ret = ::HeapDestroy(heap_);
+ heap_ = NULL;
+ }
+ }
+
+ void* Allocate(size_t size) {
+ if (!heap_)
+ CreateHeap();
+
+ DCHECK(heap_);
+
+ return ::HeapAlloc(heap_, 0, size);
+ }
+
+ void Free(void* ptr) {
+ DCHECK(heap_ != NULL);
+ ::HeapFree(heap_, 0, ptr);
+ }
+
+ void CreateHeap() {
+ AutoLock lock(init_lock_);
+
+ if (heap_ == NULL)
+ heap_ = ::HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
+ }
+
+ private:
+ Lock init_lock_;
+ HANDLE heap_;
+};
+
+// Our executable heap instance, all stubs are allocated from here.
+ExecutableHeap heap_;
+
+} // namespace
+
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+
+bool FunctionStub::is_valid() const {
+ return signature_ == reinterpret_cast<HMODULE>(&__ImageBase) &&
+ !is_bypassed();
+}
+
+FunctionStub::FunctionStub(uintptr_t extra_argument, void* dest)
+ : signature_(reinterpret_cast<HMODULE>(&__ImageBase)),
+ argument_(extra_argument),
+ destination_function_(reinterpret_cast<uintptr_t>(dest)) {
+ bypass_address_ = reinterpret_cast<uintptr_t>(&stub_.pop_return_addr_);
+ Init(&stub_);
+}
+
+FunctionStub::~FunctionStub() {
+}
+
+void FunctionStub::Init(FunctionStubAsm* stub) {
+ DCHECK(stub != NULL);
+
+ stub->jump_to_bypass_ = JUMP_IND;
+ stub->bypass_target_addr_ = reinterpret_cast<uintptr_t>(&bypass_address_);
+ stub->pop_return_addr_ = POP_EAX;
+ stub->push_ = PUSH_IND;
+ stub->arg_addr_ = reinterpret_cast<uintptr_t>(&argument_);
+ stub->push_return_addr_ = PUSH_EAX;
+ stub->jump_to_target = JUMP_IND;
+ stub->target_addr_ = reinterpret_cast<uintptr_t>(&destination_function_);
+
+ // Flush the instruction cache for the newly written code.
+ BOOL ret = ::FlushInstructionCache(::GetCurrentProcess(),
+ stub,
+ sizeof(*stub));
+}
+
+void FunctionStub::BypassStub(void* new_target) {
+ set_bypass_address(reinterpret_cast<uintptr_t>(new_target));
+}
+
+FunctionStub* FunctionStub::Create(uintptr_t extra_argument, void* dest) {
+ DCHECK(dest);
+ FunctionStub* stub =
+ reinterpret_cast<FunctionStub*>(heap_.Allocate(sizeof(FunctionStub)));
+
+ if (stub != NULL)
+ new (stub) FunctionStub(extra_argument, dest);
+
+ return stub;
+}
+
+FunctionStub* FunctionStub::FromCode(void* address) {
+ // Address points to arbitrary code here, which may e.g.
+ // lie at the end of an executable segment, which in turn
+ // may terminate earlier than the last address we probe.
+ // We therefore execute under an SEH, so as not to crash
+ // on failed probes.
+ __try {
+ // Retrieve the candidata function stub.
+ FunctionStub* candidate = CONTAINING_RECORD(address, FunctionStub, stub_);
+ if (candidate->stub_.jump_to_bypass_ == JUMP_IND &&
+ candidate->signature_ == reinterpret_cast<HMODULE>(&__ImageBase)) {
+ return candidate;
+ }
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ }
+
+ return NULL;
+}
+
+bool FunctionStub::Destroy(FunctionStub* stub) {
+ heap_.Free(stub);
+
+ return true;
+}