Created compiled stubs in image.

Saves class linker from having to set code pointers when loading
from an image. Added stubs for quick and portable resolution
trampolines, and interpreter-to-interpreter and interpreter-to-quick
entry points. Also added sizing stats output for oat writer.

Change-Id: I3905fae81047742c23d1cf0ca001db798db971b1
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 43f7ff5..ba5fe9f 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -172,6 +172,8 @@
 	src/compiled_method.cc \
 	src/compiler/driver/compiler_driver.cc \
 	src/compiler/llvm/runtime_support_llvm.cc \
+        src/compiler/stubs/portable/stubs.cc \
+        src/compiler/stubs/quick/stubs.cc \
 	src/debugger.cc \
 	src/dex_file.cc \
 	src/dex_file_verifier.cc \
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 9faf3de..0fae424 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -74,8 +74,9 @@
 
 namespace art {
 
-void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
-                                ShadowFrame* shadow_frame, JValue* result);
+extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh,
+                                           const DexFile::CodeItem* code_item,
+                                           ShadowFrame* shadow_frame, JValue* result);
 
 static void ThrowNoClassDefFoundError(const char* fmt, ...)
     __attribute__((__format__(__printf__, 1, 2)))
@@ -201,7 +202,9 @@
       array_iftable_(NULL),
       init_done_(false),
       is_dirty_(false),
-      intern_table_(intern_table) {
+      intern_table_(intern_table),
+      portable_resolution_trampoline_(NULL),
+      quick_resolution_trampoline_(NULL) {
   CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax));
 }
 
@@ -952,6 +955,8 @@
   CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
   CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0U);
   CHECK(oat_file->GetOatHeader().GetImageFileLocation().empty());
+  portable_resolution_trampoline_ = oat_file->GetOatHeader().GetPortableResolutionTrampoline();
+  quick_resolution_trampoline_ = oat_file->GetOatHeader().GetQuickResolutionTrampoline();
   mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
   mirror::ObjectArray<mirror::DexCache>* dex_caches =
       dex_caches_object->AsObjectArray<mirror::DexCache>();
@@ -1038,19 +1043,12 @@
     return;
   }
 
-  // Check if object is a method without its code set and point it to the resolution trampoline.
+  // Set entry points to interpreter for methods in interpreter only mode.
   if (obj->IsMethod()) {
     mirror::AbstractMethod* method = obj->AsMethod();
-    // Install entry point from interpreter.
-    if (!method->IsNative() && !method->IsProxyMethod() &&
-        (method->GetEntryPointFromCompiledCode() == NULL ||
-         Runtime::Current()->GetInstrumentation()->InterpretOnly())) {
-      method->SetEntryPointFromInterpreter(interpreter::EnterInterpreterFromInterpreter);
-    } else {
-      method->SetEntryPointFromInterpreter(artInterpreterToQuickEntry);
-    }
-    if (method->GetEntryPointFromCompiledCode() == NULL) {
-      method->SetEntryPointFromCompiledCode(GetResolutionTrampoline());
+    if (Runtime::Current()->GetInstrumentation()->InterpretOnly() && !method->IsNative()) {
+      method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterEntry);
+      method->SetEntryPointFromCompiledCode(GetInterpreterEntryPoint());
     }
   }
 }
@@ -1612,10 +1610,11 @@
 
   // Install entry point from interpreter.
   Runtime* runtime = Runtime::Current();
-  if (!method->IsNative() && !method->IsProxyMethod() &&
-      (method->GetEntryPointFromCompiledCode() == NULL ||
-       runtime->GetInstrumentation()->InterpretOnly())) {
-    method->SetEntryPointFromInterpreter(interpreter::EnterInterpreterFromInterpreter);
+  bool enter_interpreter = method->GetEntryPointFromCompiledCode() == NULL ||
+                           (runtime->GetInstrumentation()->InterpretOnly() &&
+                           !method->IsNative() && !method->IsProxyMethod());
+  if (enter_interpreter) {
+    method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterEntry);
   } else {
     method->SetEntryPointFromInterpreter(artInterpreterToQuickEntry);
   }
@@ -1627,7 +1626,7 @@
 
   if (method->IsStatic() && !method->IsConstructor()) {
     // For static methods excluding the class initializer, install the trampoline.
-    method->SetEntryPointFromCompiledCode(GetResolutionTrampoline());
+    method->SetEntryPointFromCompiledCode(GetResolutionTrampoline(runtime->GetClassLinker()));
   }
 
   if (method->IsNative()) {
@@ -1635,10 +1634,8 @@
     method->UnregisterNative(Thread::Current());
   }
 
-  if (method->GetEntryPointFromCompiledCode() == NULL ||
-      (runtime->GetInstrumentation()->InterpretOnly() && !method->IsNative() &&
-       !method->IsProxyMethod())) {
-    // No code? You must mean to go into the interpreter.
+  if (enter_interpreter) {
+    // Set entry point from compiled code if there's no code or in interpreter only mode.
     method->SetEntryPointFromCompiledCode(GetInterpreterEntryPoint());
   }
 
diff --git a/src/class_linker.h b/src/class_linker.h
index 79fa8ba..eab1fcc 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -334,6 +334,14 @@
     is_dirty_ = true;
   }
 
+  const void* GetPortableResolutionTrampoline() const {
+    return portable_resolution_trampoline_;
+  }
+
+  const void* GetQuickResolutionTrampoline() const {
+    return quick_resolution_trampoline_;
+  }
+
  private:
   explicit ClassLinker(InternTable*);
 
@@ -593,6 +601,9 @@
 
   InternTable* intern_table_;
 
+  const void* portable_resolution_trampoline_;
+  const void* quick_resolution_trampoline_;
+
   friend class CommonTest;
   friend class ImageWriter;  // for GetClassRoots
   friend class ObjectTest;
diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc
index 1b8f87d..834be64 100644
--- a/src/compiler/driver/compiler_driver.cc
+++ b/src/compiler/driver/compiler_driver.cc
@@ -24,6 +24,7 @@
 #include "base/stl_util.h"
 #include "base/timing_logger.h"
 #include "class_linker.h"
+#include "compiler/stubs/stubs.h"
 #include "dex_compilation_unit.h"
 #include "dex_file-inl.h"
 #include "jni_internal.h"
@@ -448,6 +449,66 @@
   return res;
 }
 
+const std::vector<uint8_t>* CompilerDriver::CreatePortableResolutionTrampoline() const {
+  switch (instruction_set_) {
+    case kArm:
+    case kThumb2:
+      return arm::CreatePortableResolutionTrampoline();
+    case kMips:
+      return mips::CreatePortableResolutionTrampoline();
+    case kX86:
+      return x86::CreatePortableResolutionTrampoline();
+    default:
+      LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_;
+      return NULL;
+  }
+}
+
+const std::vector<uint8_t>* CompilerDriver::CreateQuickResolutionTrampoline() const {
+  switch (instruction_set_) {
+    case kArm:
+    case kThumb2:
+      return arm::CreateQuickResolutionTrampoline();
+    case kMips:
+      return mips::CreateQuickResolutionTrampoline();
+    case kX86:
+      return x86::CreateQuickResolutionTrampoline();
+    default:
+      LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_;
+      return NULL;
+  }
+}
+
+const std::vector<uint8_t>* CompilerDriver::CreateInterpreterToInterpreterEntry() const {
+  switch (instruction_set_) {
+    case kArm:
+    case kThumb2:
+      return arm::CreateInterpreterToInterpreterEntry();
+    case kMips:
+      return mips::CreateInterpreterToInterpreterEntry();
+    case kX86:
+      return x86::CreateInterpreterToInterpreterEntry();
+    default:
+      LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_;
+      return NULL;
+  }
+}
+
+const std::vector<uint8_t>* CompilerDriver::CreateInterpreterToQuickEntry() const {
+  switch (instruction_set_) {
+    case kArm:
+    case kThumb2:
+      return arm::CreateInterpreterToQuickEntry();
+    case kMips:
+      return mips::CreateInterpreterToQuickEntry();
+    case kX86:
+      return x86::CreateInterpreterToQuickEntry();
+    default:
+      LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_;
+      return NULL;
+  }
+}
+
 void CompilerDriver::CompileAll(jobject class_loader,
                                 const std::vector<const DexFile*>& dex_files) {
   DCHECK(!Runtime::Current()->IsStarted());
diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h
index 9fd3c81..4f77bdb 100644
--- a/src/compiler/driver/compiler_driver.h
+++ b/src/compiler/driver/compiler_driver.h
@@ -98,6 +98,16 @@
 
   CompilerTls* GetTls();
 
+  // Generate the trampolines that are invoked by unresolved direct methods.
+  const std::vector<uint8_t>* CreatePortableResolutionTrampoline() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const std::vector<uint8_t>* CreateQuickResolutionTrampoline() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const std::vector<uint8_t>* CreateInterpreterToQuickEntry() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // A class is uniquely located by its DexFile and the class_defs_ table index into that DexFile
   typedef std::pair<const DexFile*, uint32_t> ClassReference;
 
diff --git a/src/compiler/stubs/portable/stubs.cc b/src/compiler/stubs/portable/stubs.cc
new file mode 100644
index 0000000..db551bf
--- /dev/null
+++ b/src/compiler/stubs/portable/stubs.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "compiler/stubs/stubs.h"
+#include "jni_internal.h"
+#include "oat/utils/arm/assembler_arm.h"
+#include "oat/utils/mips/assembler_mips.h"
+#include "oat/utils/x86/assembler_x86.h"
+#include "oat/runtime/oat_support_entrypoints.h"
+#include "stack_indirect_reference_table.h"
+#include "sirt_ref.h"
+
+#define __ assembler->
+
+namespace art {
+
+namespace arm {
+const std::vector<uint8_t>* CreatePortableResolutionTrampoline() {
+  UniquePtr<ArmAssembler> assembler(static_cast<ArmAssembler*>(Assembler::Create(kArm)));
+  RegList save = (1 << R0) | (1 << R1) | (1 << R2) | (1 << R3) | (1 << LR);
+
+  __ PushList(save);
+  __ LoadFromOffset(kLoadWord, R12, TR, ENTRYPOINT_OFFSET(pPortableResolutionTrampolineFromCode));
+  __ mov(R3, ShifterOperand(TR));  // Pass Thread::Current() in R3
+  __ mov(R2, ShifterOperand(SP));  // Pass sp for Method** callee_addr
+  __ IncreaseFrameSize(12);         // 3 words of space for alignment
+  // Call to resolution trampoline (callee, receiver, callee_addr, Thread*)
+  __ blx(R12);
+  __ mov(R12, ShifterOperand(R0));  // Save code address returned into R12
+  __ DecreaseFrameSize(12);
+  __ PopList(save);
+  __ cmp(R12, ShifterOperand(0));
+  __ bx(R12, NE);                   // If R12 != 0 tail call method's code
+  __ bx(LR);                        // Return to caller to handle exception
+
+  assembler->EmitSlowPaths();
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size());
+  assembler->FinalizeInstructions(code);
+
+  return resolution_trampoline.release();
+}
+} // namespace arm
+
+namespace mips {
+const std::vector<uint8_t>* CreatePortableResolutionTrampoline() {
+  UniquePtr<MipsAssembler> assembler(static_cast<MipsAssembler*>(Assembler::Create(kMips)));
+  // Build frame and save argument registers and RA.
+  __ AddConstant(SP, SP, -32);
+  __ StoreToOffset(kStoreWord, RA, SP, 28);
+  __ StoreToOffset(kStoreWord, A3, SP, 12);
+  __ StoreToOffset(kStoreWord, A2, SP, 8);
+  __ StoreToOffset(kStoreWord, A1, SP, 4);
+  __ StoreToOffset(kStoreWord, A0, SP, 0);
+
+  __ LoadFromOffset(kLoadWord, T9, S1,
+                    ENTRYPOINT_OFFSET(pPortableResolutionTrampolineFromCode));
+  __ Move(A3, S1);  // Pass Thread::Current() in A3
+  __ Move(A2, SP);  // Pass SP for Method** callee_addr
+  __ Jalr(T9); // Call to resolution trampoline (callee, receiver, callee_addr, Thread*)
+
+  // Restore frame, argument registers, and RA.
+  __ LoadFromOffset(kLoadWord, A0, SP, 0);
+  __ LoadFromOffset(kLoadWord, A1, SP, 4);
+  __ LoadFromOffset(kLoadWord, A2, SP, 8);
+  __ LoadFromOffset(kLoadWord, A3, SP, 12);
+  __ LoadFromOffset(kLoadWord, RA, SP, 28);
+  __ AddConstant(SP, SP, 32);
+
+  Label resolve_fail;
+  __ EmitBranch(V0, ZERO, &resolve_fail, true);
+  __ Jr(V0); // If V0 != 0 tail call method's code
+  __ Bind(&resolve_fail, false);
+  __ Jr(RA); // Return to caller to handle exception
+
+  assembler->EmitSlowPaths();
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size());
+  assembler->FinalizeInstructions(code);
+
+  return resolution_trampoline.release();
+}
+} // namespace mips
+
+namespace x86 {
+const std::vector<uint8_t>* CreatePortableResolutionTrampoline() {
+  UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));
+
+  __ pushl(EBP);
+  __ movl(EBP, ESP);          // save ESP
+  __ subl(ESP, Immediate(8));  // Align stack
+  __ movl(EAX, Address(EBP, 8));  // Method* called
+  __ leal(EDX, Address(EBP, 8));  // Method** called_addr
+  __ fs()->pushl(Address::Absolute(Thread::SelfOffset()));  // pass thread
+  __ pushl(EDX);  // pass called_addr
+  __ pushl(ECX);  // pass receiver
+  __ pushl(EAX);  // pass called
+  // Call to resolve method.
+  __ Call(ThreadOffset(ENTRYPOINT_OFFSET(pPortableResolutionTrampolineFromCode)),
+          X86ManagedRegister::FromCpuRegister(ECX));
+  __ leave();
+
+  Label resolve_fail;  // forward declaration
+  __ cmpl(EAX, Immediate(0));
+  __ j(kEqual, &resolve_fail);
+  __ jmp(EAX);
+  // Tail call to intended method.
+  __ Bind(&resolve_fail);
+  __ ret();
+
+  assembler->EmitSlowPaths();
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size());
+  assembler->FinalizeInstructions(code);
+
+  return resolution_trampoline.release();
+}
+} // namespace x86
+
+} // namespace art
diff --git a/src/compiler/stubs/quick/stubs.cc b/src/compiler/stubs/quick/stubs.cc
new file mode 100644
index 0000000..a8e691f
--- /dev/null
+++ b/src/compiler/stubs/quick/stubs.cc
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "compiler/stubs/stubs.h"
+#include "jni_internal.h"
+#include "oat/utils/arm/assembler_arm.h"
+#include "oat/utils/mips/assembler_mips.h"
+#include "oat/utils/x86/assembler_x86.h"
+#include "oat/runtime/oat_support_entrypoints.h"
+#include "stack_indirect_reference_table.h"
+#include "sirt_ref.h"
+
+#define __ assembler->
+
+namespace art {
+
+namespace arm {
+const std::vector<uint8_t>* CreateQuickResolutionTrampoline() {
+  UniquePtr<ArmAssembler> assembler(static_cast<ArmAssembler*>(Assembler::Create(kArm)));
+  // | Out args |
+  // | Method*  | <- SP on entry
+  // | LR       |    return address into caller
+  // | ...      |    callee saves
+  // | R3       |    possible argument
+  // | R2       |    possible argument
+  // | R1       |    possible argument
+  // | R0       |    junk on call to QuickResolutionTrampolineFromCode, holds result Method*
+  // | Method*  |    Callee save Method* set up by QuickResoltuionTrampolineFromCode
+  // Save callee saves and ready frame for exception delivery
+  RegList save = (1 << R1) | (1 << R2) | (1 << R3) | (1 << R5) | (1 << R6) | (1 << R7) | (1 << R8) |
+                 (1 << R10) | (1 << R11) | (1 << LR);
+  // TODO: enable when GetCalleeSaveMethod is available at stub generation time
+  // DCHECK_EQ(save, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetCoreSpillMask());
+  __ PushList(save);
+  __ LoadFromOffset(kLoadWord, R12, TR, ENTRYPOINT_OFFSET(pQuickResolutionTrampolineFromCode));
+  __ mov(R3, ShifterOperand(TR));  // Pass Thread::Current() in R3
+  __ IncreaseFrameSize(8);         // 2 words of space for alignment
+  __ mov(R2, ShifterOperand(SP));  // Pass SP
+  // Call to resolution trampoline (method_idx, receiver, sp, Thread*)
+  __ blx(R12);
+  __ mov(R12, ShifterOperand(R0));  // Save code address returned into R12
+  // Restore registers which may have been modified by GC, "R0" will hold the Method*
+  __ DecreaseFrameSize(4);
+  __ PopList((1 << R0) | save);
+  __ bx(R12);  // Leaf call to method's code
+  __ bkpt(0);
+
+  assembler->EmitSlowPaths();
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size());
+  assembler->FinalizeInstructions(code);
+
+  return resolution_trampoline.release();
+}
+
+const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() {
+  UniquePtr<ArmAssembler> assembler(static_cast<ArmAssembler*>(Assembler::Create(kArm)));
+
+  __ LoadFromOffset(kLoadWord, PC, R0, ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry));
+  __ bkpt(0);
+
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*entry_stub)[0], entry_stub->size());
+  assembler->FinalizeInstructions(code);
+
+  return entry_stub.release();
+}
+
+const std::vector<uint8_t>* CreateInterpreterToQuickEntry() {
+  UniquePtr<ArmAssembler> assembler(static_cast<ArmAssembler*>(Assembler::Create(kArm)));
+
+  __ LoadFromOffset(kLoadWord, PC, R0, ENTRYPOINT_OFFSET(pInterpreterToQuickEntry));
+  __ bkpt(0);
+
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*entry_stub)[0], entry_stub->size());
+  assembler->FinalizeInstructions(code);
+
+  return entry_stub.release();
+}
+} // namespace arm
+
+namespace mips {
+const std::vector<uint8_t>* CreateQuickResolutionTrampoline() {
+  UniquePtr<MipsAssembler> assembler(static_cast<MipsAssembler*>(Assembler::Create(kMips)));
+  // | Out args   |
+  // | Method*    | <- SP on entry
+  // | RA         |    return address into caller
+  // | ...        |    callee saves
+  // | A3         |    possible argument
+  // | A2         |    possible argument
+  // | A1         |    possible argument
+  // | A0/Method* |    Callee save Method* set up by UnresolvedDirectMethodTrampolineFromCode
+  // Save callee saves and ready frame for exception delivery
+  __ AddConstant(SP, SP, -64);
+  __ StoreToOffset(kStoreWord, RA, SP, 60);
+  __ StoreToOffset(kStoreWord, FP, SP, 56);
+  __ StoreToOffset(kStoreWord, GP, SP, 52);
+  __ StoreToOffset(kStoreWord, S7, SP, 48);
+  __ StoreToOffset(kStoreWord, S6, SP, 44);
+  __ StoreToOffset(kStoreWord, S5, SP, 40);
+  __ StoreToOffset(kStoreWord, S4, SP, 36);
+  __ StoreToOffset(kStoreWord, S3, SP, 32);
+  __ StoreToOffset(kStoreWord, S2, SP, 28);
+  __ StoreToOffset(kStoreWord, A3, SP, 12);
+  __ StoreToOffset(kStoreWord, A2, SP, 8);
+  __ StoreToOffset(kStoreWord, A1, SP, 4);
+
+  __ LoadFromOffset(kLoadWord, T9, S1, ENTRYPOINT_OFFSET(pQuickResolutionTrampolineFromCode));
+  __ Move(A3, S1);  // Pass Thread::Current() in A3
+  __ Move(A2, SP);  // Pass SP for Method** callee_addr
+  __ Jalr(T9); // Call to resolution trampoline (method_idx, receiver, sp, Thread*)
+
+  // Restore registers which may have been modified by GC
+  __ LoadFromOffset(kLoadWord, A0, SP, 0);
+  __ LoadFromOffset(kLoadWord, A1, SP, 4);
+  __ LoadFromOffset(kLoadWord, A2, SP, 8);
+  __ LoadFromOffset(kLoadWord, A3, SP, 12);
+  __ LoadFromOffset(kLoadWord, S2, SP, 28);
+  __ LoadFromOffset(kLoadWord, S3, SP, 32);
+  __ LoadFromOffset(kLoadWord, S4, SP, 36);
+  __ LoadFromOffset(kLoadWord, S5, SP, 40);
+  __ LoadFromOffset(kLoadWord, S6, SP, 44);
+  __ LoadFromOffset(kLoadWord, S7, SP, 48);
+  __ LoadFromOffset(kLoadWord, GP, SP, 52);
+  __ LoadFromOffset(kLoadWord, FP, SP, 56);
+  __ LoadFromOffset(kLoadWord, RA, SP, 60);
+  __ AddConstant(SP, SP, 64);
+
+  __ Move(T9, V0); // Put method's code in T9
+  __ Jr(T9);  // Leaf call to method's code
+
+  __ Break();
+
+  assembler->EmitSlowPaths();
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size());
+  assembler->FinalizeInstructions(code);
+
+  return resolution_trampoline.release();
+}
+
+const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() {
+  UniquePtr<MipsAssembler> assembler(static_cast<MipsAssembler*>(Assembler::Create(kMips)));
+
+  __ LoadFromOffset(kLoadWord, T9, A0, ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry));
+  __ Jr(T9);
+  __ Break();
+
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*entry_stub)[0], entry_stub->size());
+  assembler->FinalizeInstructions(code);
+
+  return entry_stub.release();
+}
+
+const std::vector<uint8_t>* CreateInterpreterToQuickEntry() {
+  UniquePtr<MipsAssembler> assembler(static_cast<MipsAssembler*>(Assembler::Create(kMips)));
+
+  __ LoadFromOffset(kLoadWord, T9, A0, ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry));
+  __ Jr(T9);
+  __ Break();
+
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*entry_stub)[0], entry_stub->size());
+  assembler->FinalizeInstructions(code);
+
+  return entry_stub.release();
+}
+} // namespace mips
+
+namespace x86 {
+const std::vector<uint8_t>* CreateQuickResolutionTrampoline() {
+  UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));
+  // Set up the callee save frame to conform with Runtime::CreateCalleeSaveMethod(kRefsAndArgs)
+  // return address
+  __ pushl(EDI);
+  __ pushl(ESI);
+  __ pushl(EBP);
+  __ pushl(EBX);
+  __ pushl(EDX);
+  __ pushl(ECX);
+  __ pushl(EAX);  // <-- callee save Method* to go here
+  __ movl(EDX, ESP);          // save ESP
+  __ fs()->pushl(Address::Absolute(Thread::SelfOffset()));  // pass Thread*
+  __ pushl(EDX);              // pass ESP for Method*
+  __ pushl(ECX);              // pass receiver
+  __ pushl(EAX);              // pass Method*
+
+  // Call to resolve method.
+  __ Call(ThreadOffset(ENTRYPOINT_OFFSET(pQuickResolutionTrampolineFromCode)),
+          X86ManagedRegister::FromCpuRegister(ECX));
+
+  __ movl(EDI, EAX);  // save code pointer in EDI
+  __ addl(ESP, Immediate(16));  // Pop arguments
+  __ popl(EAX);  // Restore args.
+  __ popl(ECX);
+  __ popl(EDX);
+  __ popl(EBX);
+  __ popl(EBP);  // Restore callee saves.
+  __ popl(ESI);
+  // Swap EDI callee save with code pointer
+  __ xchgl(EDI, Address(ESP, 0));
+  // Tail call to intended method.
+  __ ret();
+
+  assembler->EmitSlowPaths();
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > resolution_trampoline(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size());
+  assembler->FinalizeInstructions(code);
+
+  return resolution_trampoline.release();
+}
+
+const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry() {
+  UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));
+
+  __ fs()->jmp(Address::Absolute(ThreadOffset(ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry))));
+
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*entry_stub)[0], entry_stub->size());
+  assembler->FinalizeInstructions(code);
+
+  return entry_stub.release();
+}
+
+const std::vector<uint8_t>* CreateInterpreterToQuickEntry() {
+  UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));
+
+  __ fs()->jmp(Address::Absolute(ThreadOffset(ENTRYPOINT_OFFSET(pInterpreterToQuickEntry))));
+
+  size_t cs = assembler->CodeSize();
+  UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*entry_stub)[0], entry_stub->size());
+  assembler->FinalizeInstructions(code);
+
+  return entry_stub.release();
+}
+} // namespace x86
+
+} // namespace art
diff --git a/src/compiler/stubs/stubs.h b/src/compiler/stubs/stubs.h
new file mode 100644
index 0000000..ebe761d
--- /dev/null
+++ b/src/compiler/stubs/stubs.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SRC_COMPILER_STUBS_STUBS_H_
+#define ART_SRC_COMPILER_STUBS_STUBS_H_
+
+#include "runtime.h"
+
+namespace art {
+
+namespace arm {
+const std::vector<uint8_t>* CreatePortableResolutionTrampoline()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateQuickResolutionTrampoline()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateInterpreterToQuickEntry()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+}
+
+namespace mips {
+const std::vector<uint8_t>* CreatePortableResolutionTrampoline()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateQuickResolutionTrampoline()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateInterpreterToQuickEntry()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+}
+
+namespace x86 {
+const std::vector<uint8_t>* CreatePortableResolutionTrampoline()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateQuickResolutionTrampoline()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateInterpreterToInterpreterEntry()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateInterpreterToQuickEntry()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+}
+
+}  // namespace art
+
+#endif  // ART_SRC_COMPILER_STUBS_STUBS_H_
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
index 3f13d59..2f3edc4 100644
--- a/src/dex2oat.cc
+++ b/src/dex2oat.cc
@@ -280,9 +280,8 @@
     std::string image_file_location;
     uint32_t image_file_location_oat_checksum = 0;
     uint32_t image_file_location_oat_data_begin = 0;
-    Heap* heap = Runtime::Current()->GetHeap();
-    if (heap->GetSpaces().size() > 1) {
-      ImageSpace* image_space = heap->GetImageSpace();
+    if (!driver->IsImage()) {
+      ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
       image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum();
       image_file_location_oat_data_begin =
           reinterpret_cast<uint32_t>(image_space->GetImageHeader().GetOatDataBegin());
diff --git a/src/image_test.cc b/src/image_test.cc
index 8066a90..cd1a34f 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -48,7 +48,8 @@
       dex_files.push_back(java_lang_dex_file_);
       dex_files.push_back(conscrypt_file_);
       VectorOutputStream output_stream(tmp_elf.GetFilename(), oat_contents);
-      bool success_oat = OatWriter::Create(output_stream, dex_files, 0, 0, "", *compiler_driver_.get());
+      bool success_oat = OatWriter::Create(output_stream, dex_files, 0, 0, "",
+                                           *compiler_driver_.get());
       ASSERT_TRUE(success_oat);
 
       // Force all system classes into memory
diff --git a/src/image_writer.cc b/src/image_writer.cc
index 12b756e..4eea1ca 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -81,6 +81,10 @@
   }
   oat_file_ = OatFile::OpenWritable(oat_file.get(), oat_location);
   class_linker->RegisterOatFile(*oat_file_);
+  interpreter_to_interpreter_entry_offset_ = oat_file_->GetOatHeader().GetInterpreterToInterpreterEntryOffset();
+  interpreter_to_quick_entry_offset_ = oat_file_->GetOatHeader().GetInterpreterToQuickEntryOffset();
+  portable_resolution_trampoline_offset_ = oat_file_->GetOatHeader().GetPortableResolutionTrampolineOffset();
+  quick_resolution_trampoline_offset_ = oat_file_->GetOatHeader().GetQuickResolutionTrampolineOffset();
 
   {
     Thread::Current()->TransitionFromSuspendedToRunnable();
@@ -507,17 +511,34 @@
   if (orig->IsAbstract()) {
     // Code for abstract methods is set to the abstract method error stub when we load the image.
     copy->SetEntryPointFromCompiledCode(NULL);
+    copy->SetEntryPointFromInterpreter(reinterpret_cast<EntryPointFromInterpreter*>
+                                       (GetOatAddress(interpreter_to_interpreter_entry_offset_)));
     return;
+  } else {
+    copy->SetEntryPointFromInterpreter(reinterpret_cast<EntryPointFromInterpreter*>
+                                       (GetOatAddress(interpreter_to_quick_entry_offset_)));
   }
 
   if (orig == Runtime::Current()->GetResolutionMethod()) {
-    // The resolution method's code is set to the resolution trampoline when we load the image.
-    copy->SetEntryPointFromCompiledCode(NULL);
+#if defined(ART_USE_PORTABLE_COMPILER)
+    copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_));
+#else
+    copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_));
+#endif
     return;
   }
 
-  // Non-abstract methods have code
-  copy->SetEntryPointFromCompiledCode(GetOatAddress(orig->GetOatCodeOffset()));
+  // Use original code if it exists. Otherwise, set the code pointer to the resolution trampoline.
+  const byte* code = GetOatAddress(orig->GetOatCodeOffset());
+  if (code != NULL) {
+    copy->SetEntryPointFromCompiledCode(code);
+  } else {
+#if defined(ART_USE_PORTABLE_COMPILER)
+    copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_));
+#else
+    copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_));
+#endif
+  }
 
   if (orig->IsNative()) {
     // The native method's pointer is set to a stub to lookup via dlsym when we load the image.
diff --git a/src/image_writer.h b/src/image_writer.h
index 0cccf69..1de3149 100644
--- a/src/image_writer.h
+++ b/src/image_writer.h
@@ -39,7 +39,9 @@
  public:
   explicit ImageWriter(std::set<std::string>* image_classes)
       : oat_file_(NULL), image_end_(0), image_begin_(NULL), image_classes_(image_classes),
-        oat_data_begin_(NULL) {}
+        oat_data_begin_(NULL), interpreter_to_interpreter_entry_offset_(0),
+        interpreter_to_quick_entry_offset_(0), portable_resolution_trampoline_offset_(0),
+        quick_resolution_trampoline_offset_(0) {}
 
   ~ImageWriter() {}
 
@@ -196,7 +198,13 @@
   // Beginning target oat address for the pointers from the output image to its oat file.
   const byte* oat_data_begin_;
 
-  // DexCaches seen while scanning for fixing up CodeAndDirectMethods.
+  // Offset from oat_data_begin_ to the stubs.
+  uint32_t interpreter_to_interpreter_entry_offset_;
+  uint32_t interpreter_to_quick_entry_offset_;
+  uint32_t portable_resolution_trampoline_offset_;
+  uint32_t quick_resolution_trampoline_offset_;
+
+  // DexCaches seen while scanning for fixing up CodeAndDirectMethods
   typedef std::set<mirror::DexCache*> Set;
   Set dex_caches_;
 };
diff --git a/src/instrumentation.cc b/src/instrumentation.cc
index 39fd377..8af0885 100644
--- a/src/instrumentation.cc
+++ b/src/instrumentation.cc
@@ -62,7 +62,7 @@
         if (is_initialized || !method->IsStatic() || method->IsConstructor()) {
           new_code = class_linker->GetOatCodeFor(method);
         } else {
-          new_code = GetResolutionTrampoline();
+          new_code = GetResolutionTrampoline(class_linker);
         }
       } else {  // !uninstall
         if (!interpreter_stubs_installed_ || method->IsNative()) {
@@ -380,7 +380,7 @@
   if (LIKELY(!instrumentation_stubs_installed_)) {
     const void* code = method->GetEntryPointFromCompiledCode();
     DCHECK(code != NULL);
-    if (LIKELY(code != GetResolutionTrampoline())) {
+    if (LIKELY(code != GetResolutionTrampoline(runtime->GetClassLinker()))) {
       return code;
     }
   }
diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc
index 94237a0..c7b9c58 100644
--- a/src/interpreter/interpreter.cc
+++ b/src/interpreter/interpreter.cc
@@ -133,7 +133,7 @@
     }
   } else {
     // Not special, continue with regular interpreter execution.
-    EnterInterpreterFromInterpreter(self, mh, code_item, shadow_frame, result);
+    artInterpreterToInterpreterEntry(self, mh, code_item, shadow_frame, result);
   }
 }
 
@@ -2816,9 +2816,9 @@
   return Execute(self, mh, code_item, shadow_frame, JValue());
 }
 
-void EnterInterpreterFromInterpreter(Thread* self, MethodHelper& mh,
-                                     const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame,
-                                     JValue* result)
+void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh,
+                                      const DexFile::CodeItem* code_item,
+                                      ShadowFrame* shadow_frame, JValue* result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
     ThrowStackOverflowError(self);
diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h
index bae3b65..20166ac 100644
--- a/src/interpreter/interpreter.h
+++ b/src/interpreter/interpreter.h
@@ -47,9 +47,9 @@
                                        ShadowFrame& shadow_frame)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-extern void EnterInterpreterFromInterpreter(Thread* self, MethodHelper& mh,
-                                            const DexFile::CodeItem* code_item,
-                                            ShadowFrame* shadow_frame, JValue* result)
+extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh,
+                                                const DexFile::CodeItem* code_item,
+                                                ShadowFrame* shadow_frame, JValue* result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 }  // namespace interpreter
diff --git a/src/mirror/abstract_method-inl.h b/src/mirror/abstract_method-inl.h
index d4f0f2c..a823886 100644
--- a/src/mirror/abstract_method-inl.h
+++ b/src/mirror/abstract_method-inl.h
@@ -117,7 +117,8 @@
   if (GetEntryPointFromCompiledCode() == GetInterpreterEntryPoint()) {
     return;
   }
-  if (GetEntryPointFromCompiledCode() == GetResolutionTrampoline()) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  if (GetEntryPointFromCompiledCode() == GetResolutionTrampoline(class_linker)) {
       return;
   }
   DCHECK(IsWithinCode(pc))
diff --git a/src/oat.cc b/src/oat.cc
index 4eb97f5..e606953 100644
--- a/src/oat.cc
+++ b/src/oat.cc
@@ -22,7 +22,7 @@
 namespace art {
 
 const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '0', '5', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '0', '6', '\0' };
 
 OatHeader::OatHeader() {
   memset(this, 0, sizeof(*this));
@@ -57,6 +57,10 @@
   UpdateChecksum(image_file_location.data(), image_file_location_size_);
 
   executable_offset_ = 0;
+  interpreter_to_interpreter_entry_offset_ = 0;
+  interpreter_to_quick_entry_offset_ = 0;
+  portable_resolution_trampoline_offset_ = 0;
+  quick_resolution_trampoline_offset_ = 0;
 }
 
 bool OatHeader::IsValid() const {
@@ -97,6 +101,92 @@
   return executable_offset_;
 }
 
+void OatHeader::SetExecutableOffset(uint32_t executable_offset) {
+  DCHECK_ALIGNED(executable_offset, kPageSize);
+  CHECK_GT(executable_offset, sizeof(OatHeader));
+  DCHECK(IsValid());
+  DCHECK_EQ(executable_offset_, 0U);
+
+  executable_offset_ = executable_offset;
+  UpdateChecksum(&executable_offset_, sizeof(executable_offset));
+}
+
+const void* OatHeader::GetInterpreterToInterpreterEntry() const {
+  return reinterpret_cast<const uint8_t*>(this) + GetInterpreterToInterpreterEntryOffset();
+}
+
+uint32_t OatHeader::GetInterpreterToInterpreterEntryOffset() const {
+  DCHECK(IsValid());
+  CHECK_GE(interpreter_to_interpreter_entry_offset_, executable_offset_);
+  return interpreter_to_interpreter_entry_offset_;
+}
+
+void OatHeader::SetInterpreterToInterpreterEntryOffset(uint32_t offset) {
+  CHECK(offset == 0 || offset >= executable_offset_);
+  DCHECK(IsValid());
+  DCHECK_EQ(interpreter_to_interpreter_entry_offset_, 0U) << offset;
+
+  interpreter_to_interpreter_entry_offset_ = offset;
+  UpdateChecksum(&interpreter_to_interpreter_entry_offset_, sizeof(offset));
+}
+
+const void* OatHeader::GetInterpreterToQuickEntry() const {
+  return reinterpret_cast<const uint8_t*>(this) + GetInterpreterToQuickEntryOffset();
+}
+
+uint32_t OatHeader::GetInterpreterToQuickEntryOffset() const {
+  DCHECK(IsValid());
+  CHECK_GE(interpreter_to_quick_entry_offset_, interpreter_to_interpreter_entry_offset_);
+  return interpreter_to_quick_entry_offset_;
+}
+
+void OatHeader::SetInterpreterToQuickEntryOffset(uint32_t offset) {
+  CHECK(offset == 0 || offset >= interpreter_to_interpreter_entry_offset_);
+  DCHECK(IsValid());
+  DCHECK_EQ(interpreter_to_quick_entry_offset_, 0U) << offset;
+
+  interpreter_to_quick_entry_offset_ = offset;
+  UpdateChecksum(&interpreter_to_quick_entry_offset_, sizeof(offset));
+}
+
+const void* OatHeader::GetPortableResolutionTrampoline() const {
+  return reinterpret_cast<const uint8_t*>(this) + GetPortableResolutionTrampolineOffset();
+}
+
+uint32_t OatHeader::GetPortableResolutionTrampolineOffset() const {
+  DCHECK(IsValid());
+  CHECK_GE(portable_resolution_trampoline_offset_, interpreter_to_quick_entry_offset_);
+  return portable_resolution_trampoline_offset_;
+}
+
+void OatHeader::SetPortableResolutionTrampolineOffset(uint32_t offset) {
+  CHECK(offset == 0 || offset >= interpreter_to_quick_entry_offset_);
+  DCHECK(IsValid());
+  DCHECK_EQ(portable_resolution_trampoline_offset_, 0U) << offset;
+
+  portable_resolution_trampoline_offset_ = offset;
+  UpdateChecksum(&portable_resolution_trampoline_offset_, sizeof(offset));
+}
+
+const void* OatHeader::GetQuickResolutionTrampoline() const {
+  return reinterpret_cast<const uint8_t*>(this) + GetQuickResolutionTrampolineOffset();
+}
+
+uint32_t OatHeader::GetQuickResolutionTrampolineOffset() const {
+  DCHECK(IsValid());
+  CHECK_GE(quick_resolution_trampoline_offset_, portable_resolution_trampoline_offset_);
+  return quick_resolution_trampoline_offset_;
+}
+
+void OatHeader::SetQuickResolutionTrampolineOffset(uint32_t offset) {
+  CHECK(offset == 0 || offset >= portable_resolution_trampoline_offset_);
+  DCHECK(IsValid());
+  DCHECK_EQ(quick_resolution_trampoline_offset_, 0U) << offset;
+
+  quick_resolution_trampoline_offset_ = offset;
+  UpdateChecksum(&quick_resolution_trampoline_offset_, sizeof(offset));
+}
+
 uint32_t OatHeader::GetImageFileLocationOatChecksum() const {
   CHECK(IsValid());
   return image_file_location_oat_checksum_;
@@ -123,16 +213,6 @@
                      GetImageFileLocationSize());
 }
 
-void OatHeader::SetExecutableOffset(uint32_t executable_offset) {
-  DCHECK_ALIGNED(executable_offset, kPageSize);
-  CHECK_GT(executable_offset, sizeof(OatHeader));
-  DCHECK(IsValid());
-  DCHECK_EQ(executable_offset_, 0U);
-
-  executable_offset_ = executable_offset;
-  UpdateChecksum(&executable_offset_, sizeof(executable_offset));
-}
-
 OatMethodOffsets::OatMethodOffsets()
   : code_offset_(0),
     frame_size_in_bytes_(0),
diff --git a/src/oat.h b/src/oat.h
index cf98891..c67a1a6 100644
--- a/src/oat.h
+++ b/src/oat.h
@@ -43,8 +43,20 @@
     return dex_file_count_;
   }
   uint32_t GetExecutableOffset() const;
-  InstructionSet GetInstructionSet() const;
   void SetExecutableOffset(uint32_t executable_offset);
+  const void* GetInterpreterToInterpreterEntry() const;
+  uint32_t GetInterpreterToInterpreterEntryOffset() const;
+  void SetInterpreterToInterpreterEntryOffset(uint32_t offset);
+  const void* GetInterpreterToQuickEntry() const;
+  uint32_t GetInterpreterToQuickEntryOffset() const;
+  void SetInterpreterToQuickEntryOffset(uint32_t offset);
+  const void* GetPortableResolutionTrampoline() const;
+  uint32_t GetPortableResolutionTrampolineOffset() const;
+  void SetPortableResolutionTrampolineOffset(uint32_t offset);
+  const void* GetQuickResolutionTrampoline() const;
+  uint32_t GetQuickResolutionTrampolineOffset() const;
+  void SetQuickResolutionTrampolineOffset(uint32_t offset);
+  InstructionSet GetInstructionSet() const;
   uint32_t GetImageFileLocationOatChecksum() const;
   uint32_t GetImageFileLocationOatDataBegin() const;
   uint32_t GetImageFileLocationSize() const;
@@ -62,6 +74,10 @@
   InstructionSet instruction_set_;
   uint32_t dex_file_count_;
   uint32_t executable_offset_;
+  uint32_t interpreter_to_interpreter_entry_offset_;
+  uint32_t interpreter_to_quick_entry_offset_;
+  uint32_t portable_resolution_trampoline_offset_;
+  uint32_t quick_resolution_trampoline_offset_;
 
   uint32_t image_file_location_oat_checksum_;
   uint32_t image_file_location_oat_data_begin_;
diff --git a/src/oat/runtime/arm/oat_support_entrypoints_arm.cc b/src/oat/runtime/arm/oat_support_entrypoints_arm.cc
index 1a5fe47..2e9453c 100644
--- a/src/oat/runtime/arm/oat_support_entrypoints_arm.cc
+++ b/src/oat/runtime/arm/oat_support_entrypoints_arm.cc
@@ -91,12 +91,26 @@
 extern "C" uint64_t art_quick_shr_long(uint64_t, uint32_t);
 extern "C" uint64_t art_quick_ushr_long(uint64_t, uint32_t);
 
+// Interpreter entrypoints.
+extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh,
+                                                 const DexFile::CodeItem* code_item,
+                                                 ShadowFrame* shadow_frame, JValue* result);
+extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh,
+                                           const DexFile::CodeItem* code_item,
+                                           ShadowFrame* shadow_frame, JValue* result);
+
 // Intrinsic entrypoints.
 extern "C" int32_t __memcmp16(void*, void*, int32_t);
 extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t);
 extern "C" int32_t art_quick_string_compareto(void*, void*);
 
 // Invoke entrypoints.
+extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* called,
+                                                       mirror::Object* receiver,
+                                                       mirror::AbstractMethod** sp, Thread* thread);
+extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called,
+                                                    mirror::Object* receiver,
+                                                    mirror::AbstractMethod** sp, Thread* thread);
 extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
 extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*);
 extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
@@ -187,6 +201,10 @@
   points->pShrLong = art_quick_shr_long;
   points->pUshrLong = art_quick_ushr_long;
 
+  // Interpreter
+  points->pInterpreterToInterpreterEntry = artInterpreterToInterpreterEntry;
+  points->pInterpreterToQuickEntry = artInterpreterToQuickEntry;
+
   // Intrinsics
   points->pIndexOf = art_quick_indexof;
   points->pMemcmp16 = __memcmp16;
@@ -194,6 +212,8 @@
   points->pMemcpy = memcpy;
 
   // Invocation
+  points->pPortableResolutionTrampolineFromCode = artPortableResolutionTrampoline;
+  points->pQuickResolutionTrampolineFromCode = artQuickResolutionTrampoline;
   points->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
   points->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline;
   points->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
diff --git a/src/oat/runtime/arm/runtime_support_arm.S b/src/oat/runtime/arm/runtime_support_arm.S
index b8d2265..f19e8ba 100644
--- a/src/oat/runtime/arm/runtime_support_arm.S
+++ b/src/oat/runtime/arm/runtime_support_arm.S
@@ -246,48 +246,6 @@
 INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck
 INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck
 
-     /*
-     * Portable resolution trampoline.
-     */
-     .extern artPortableResolutionTrampoline
-ENTRY art_portable_resolution_trampoline
-    push   {r0, r1, r2, r3, lr}           @ spill regs
-    .save  {r0, r1, r2, r3, lr}
-    .pad #20
-    .cfi_adjust_cfa_offset 20
-    sub    sp, #12                        @ pad stack pointer to align frame
-    .pad #12
-    .cfi_adjust_cfa_offset 12
-    mov    r3, r9                         @ pass Thread::Current
-    mov    r2, sp                         @ pass stack pointer
-    blx    artPortableResolutionTrampoline   @ (method, receiver, sp, Thread*)
-    mov    r12, r0                        @ save method code pointer result
-    add    sp, #12                        @ remove padding from stack pointer
-    .cfi_adjust_cfa_offset -12
-    pop    {r0, r1, r2, r3, lr}           @ restore regs
-    .cfi_adjust_cfa_offset -20
-    cmp    r12, #0                        @ is method code null?
-    bxne   r12                            @ if non-null, tail call to method's code
-    bx     lr                             @ otherwise, return to caller to handle exception
-END art_portable_resolution_trampoline
-
-    /*
-     * Quick resolution trampoline.
-     */
-     .extern artQuickResolutionTrampoline
-ENTRY art_quick_resolution_trampoline
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME  @ save callee saves in case allocation triggers GC
-    mov    r3, r9                         @ pass Thread::Current
-    mov    r2, sp                         @ pass stack pointer
-    blx    artQuickResolutionTrampoline   @ (called method, receiver, sp, Thread*)
-    mov    r12, r0                        @ save method code pointer result
-    add    sp, #4                         @ set up stack pointer
-    .cfi_adjust_cfa_offset -4
-    pop    {r0-r3, r5-r8, r10-r11, lr}    @ 11 words, r0 will hold method*
-    .cfi_adjust_cfa_offset -44
-    bx     r12                            @ leaf call to method code
-END art_quick_resolution_trampoline
-
     /*
      * Portable invocation stub.
      * On entry:
diff --git a/src/oat/runtime/mips/oat_support_entrypoints_mips.cc b/src/oat/runtime/mips/oat_support_entrypoints_mips.cc
index eb82c42..8e06611 100644
--- a/src/oat/runtime/mips/oat_support_entrypoints_mips.cc
+++ b/src/oat/runtime/mips/oat_support_entrypoints_mips.cc
@@ -93,12 +93,26 @@
 extern "C" uint64_t art_quick_shr_long(uint64_t, uint32_t);
 extern "C" uint64_t art_quick_ushr_long(uint64_t, uint32_t);
 
+// Interpreter entrypoints.
+extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh,
+                                                 const DexFile::CodeItem* code_item,
+                                                 ShadowFrame* shadow_frame, JValue* result);
+extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh,
+                                           const DexFile::CodeItem* code_item,
+                                           ShadowFrame* shadow_frame, JValue* result);
+
 // Intrinsic entrypoints.
 extern "C" int32_t __memcmp16(void*, void*, int32_t);
 extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t);
 extern "C" int32_t art_quick_string_compareto(void*, void*);
 
 // Invoke entrypoints.
+extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* called,
+                                                       mirror::Object* receiver,
+                                                       mirror::AbstractMethod** sp, Thread* thread);
+extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called,
+                                                    mirror::Object* receiver,
+                                                    mirror::AbstractMethod** sp, Thread* thread);
 extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
 extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*);
 extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
@@ -188,6 +202,10 @@
   points->pShrLong = art_quick_shr_long;
   points->pUshrLong = art_quick_ushr_long;
 
+  // Interpreter
+  points->pInterpreterToInterpreterEntry = artInterpreterToInterpreterEntry;
+  points->pInterpreterToQuickEntry = artInterpreterToQuickEntry;
+
   // Intrinsics
   points->pIndexOf = art_quick_indexof;
   points->pMemcmp16 = __memcmp16;
@@ -195,6 +213,8 @@
   points->pMemcpy = memcpy;
 
   // Invocation
+  points->pPortableResolutionTrampolineFromCode = artPortableResolutionTrampoline;
+  points->pQuickResolutionTrampolineFromCode = artQuickResolutionTrampoline;
   points->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
   points->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline;
   points->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
diff --git a/src/oat/runtime/mips/runtime_support_mips.S b/src/oat/runtime/mips/runtime_support_mips.S
index 2eeb662..45d583e 100644
--- a/src/oat/runtime/mips/runtime_support_mips.S
+++ b/src/oat/runtime/mips/runtime_support_mips.S
@@ -413,71 +413,6 @@
 INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck
 
     /*
-     * Portable resolution trampoline.
-     */
-    .extern artPortableResolutionTrampoline
-ENTRY art_portable_resolution_trampoline
-    GENERATE_GLOBAL_POINTER
-    addiu $sp, $sp, -32          # leave room for $a0, $a1, $a2, $a3, and $ra
-    .cfi_adjust_cfa_offset 32
-    sw     $ra, 16($sp)
-    .cfi_rel_offset 31, 16
-    sw     $a3, 12($sp)
-    .cfi_rel_offset 7, 12
-    sw     $a2, 8($sp)
-    .cfi_rel_offset 6, 8
-    sw     $a1, 4($sp)
-    .cfi_rel_offset 5, 4
-    sw     $a0, 0($sp)
-    .cfi_rel_offset 4, 0
-    move  $a3, $s1              # pass Thread::Current()
-    jal   artPortableResolutionTrampoline  # (method, receiver, sp, Thread*)
-    move  $a2, $sp              # pass stack pointer
-    lw    $a0, 0($sp)           # restore registers from stack
-    lw    $a1, 4($sp)
-    lw    $a2, 8($sp)
-    lw    $a3, 12($sp)
-    lw    $ra, 16($sp)
-    beq   $v0, $zero, resolve_fail
-    addiu $sp, $sp, 32          # restore the stack
-    .cfi_adjust_cfa_offset -32
-    jr    $t9                   # leaf call to method's code
-    move  $t9, $v0              # put method code result in $t9
-resolve_fail:
-    jr    $ra
-    nop
-END art_portable_resolution_trampoline
-
-    /*
-     * Quick resolution trampoline.
-     */
-    .extern artQuickResolutionTrampoline
-ENTRY art_quick_resolution_trampoline
-    GENERATE_GLOBAL_POINTER
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    move  $a3, $s1              # pass Thread::Current()
-    jal   artQuickResolutionTrampoline  # (method, receiver, sp, Thread*)
-    move  $a2, $sp              # pass stack pointer
-    move  $t9, $v0              # put method code result in $t9
-    lw    $a0, 0($sp)           # restore registers from stack
-    lw    $a1, 4($sp)
-    lw    $a2, 8($sp)
-    lw    $a3, 12($sp)
-    lw    $s2, 28($sp)
-    lw    $s3, 32($sp)
-    lw    $s4, 36($sp)
-    lw    $s5, 40($sp)
-    lw    $s6, 44($sp)
-    lw    $s7, 48($sp)
-    lw    $gp, 52($sp)
-    lw    $fp, 56($sp)
-    lw    $ra, 60($sp)
-    jr    $t9                   # leaf call to method's code
-    addiu $sp, $sp, 64          # restore the stack
-    .cfi_adjust_cfa_offset -64
-END art_quick_resolution_trampoline
-
-    /*
      * Common invocation stub for portable and quick.
      * On entry:
      *   a0 = method pointer
diff --git a/src/oat/runtime/oat_support_entrypoints.h b/src/oat/runtime/oat_support_entrypoints.h
index 75c9378..c1a2587 100644
--- a/src/oat/runtime/oat_support_entrypoints.h
+++ b/src/oat/runtime/oat_support_entrypoints.h
@@ -17,6 +17,7 @@
 #ifndef ART_SRC_OAT_RUNTIME_OAT_SUPPORT_ENTRYPOINTS_H_
 #define ART_SRC_OAT_RUNTIME_OAT_SUPPORT_ENTRYPOINTS_H_
 
+#include "dex_file-inl.h"
 #include "runtime.h"
 
 #define ENTRYPOINT_OFFSET(x) \
@@ -30,6 +31,8 @@
 class Object;
 }  // namespace mirror
 class DvmDex;
+class MethodHelper;
+class ShadowFrame;
 class Thread;
 
 struct PACKED(4) EntryPoints {
@@ -104,6 +107,14 @@
   uint64_t (*pShrLong)(uint64_t, uint32_t);
   uint64_t (*pUshrLong)(uint64_t, uint32_t);
 
+  // Interpreter
+  void (*pInterpreterToInterpreterEntry)(Thread* self, MethodHelper& mh,
+                                         const DexFile::CodeItem* code_item,
+                                         ShadowFrame* shadow_frame, JValue* result);
+  void (*pInterpreterToQuickEntry)(Thread* self, MethodHelper& mh,
+                                   const DexFile::CodeItem* code_item,
+                                   ShadowFrame* shadow_frame, JValue* result);
+
   // Intrinsics
   int32_t (*pIndexOf)(void*, uint32_t, uint32_t, uint32_t);
   int32_t (*pMemcmp16)(void*, void*, int32_t);
@@ -111,6 +122,10 @@
   void* (*pMemcpy)(void*, const void*, size_t);
 
   // Invocation
+  const void* (*pPortableResolutionTrampolineFromCode)(mirror::AbstractMethod*, mirror::Object*,
+                                                       mirror::AbstractMethod**, Thread*);
+  const void* (*pQuickResolutionTrampolineFromCode)(mirror::AbstractMethod*, mirror::Object*,
+                                                    mirror::AbstractMethod**, Thread*);
   void (*pInvokeDirectTrampolineWithAccessCheck)(uint32_t, void*);
   void (*pInvokeInterfaceTrampoline)(uint32_t, void*);
   void (*pInvokeInterfaceTrampolineWithAccessCheck)(uint32_t, void*);
diff --git a/src/oat/runtime/support_interpreter.cc b/src/oat/runtime/support_interpreter.cc
index 7060a41..55be54f 100644
--- a/src/oat/runtime/support_interpreter.cc
+++ b/src/oat/runtime/support_interpreter.cc
@@ -110,8 +110,9 @@
   return result.GetJ();
 }
 
-void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
-                                ShadowFrame* shadow_frame, JValue* result)
+extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh,
+                                           const DexFile::CodeItem* code_item,
+                                           ShadowFrame* shadow_frame, JValue* result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::AbstractMethod* method = shadow_frame->GetMethod();
   uint16_t arg_offset = (code_item == NULL) ? 0 : code_item->registers_size_ - code_item->ins_size_;
diff --git a/src/oat/runtime/support_stubs.cc b/src/oat/runtime/support_stubs.cc
index 97d5f66..71b67d0 100644
--- a/src/oat/runtime/support_stubs.cc
+++ b/src/oat/runtime/support_stubs.cc
@@ -144,7 +144,7 @@
     // Expect class to at least be initializing.
     DCHECK(called->GetDeclaringClass()->IsInitializing());
     // Don't want infinite recursion.
-    DCHECK(code != GetResolutionTrampoline());
+    DCHECK(code != GetResolutionTrampoline(linker));
     // Set up entry into main method
     *called_addr = called;
   }
@@ -393,7 +393,7 @@
     // Expect class to at least be initializing.
     DCHECK(called->GetDeclaringClass()->IsInitializing());
     // Don't want infinite recursion.
-    DCHECK(code != GetResolutionTrampoline());
+    DCHECK(code != GetResolutionTrampoline(linker));
     // Set up entry into main method
     regs[0] = reinterpret_cast<uintptr_t>(called);
   }
diff --git a/src/oat/runtime/x86/oat_support_entrypoints_x86.cc b/src/oat/runtime/x86/oat_support_entrypoints_x86.cc
index 357bbe0..a90a583 100644
--- a/src/oat/runtime/x86/oat_support_entrypoints_x86.cc
+++ b/src/oat/runtime/x86/oat_support_entrypoints_x86.cc
@@ -75,6 +75,14 @@
 extern "C" uint64_t art_quick_lshr_from_code(uint64_t, uint32_t);
 extern "C" uint64_t art_quick_lushr_from_code(uint64_t, uint32_t);
 
+// Interpreter entrypoints.
+extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh,
+                                                 const DexFile::CodeItem* code_item,
+                                                 ShadowFrame* shadow_frame, JValue* result);
+extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh,
+                                           const DexFile::CodeItem* code_item,
+                                           ShadowFrame* shadow_frame, JValue* result);
+
 // Intrinsic entrypoints.
 extern "C" int32_t art_quick_memcmp16(void*, void*, int32_t);
 extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t);
@@ -82,6 +90,12 @@
 extern "C" void* art_quick_memcpy(void*, const void*, size_t);
 
 // Invoke entrypoints.
+extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* called,
+                                                       mirror::Object* receiver,
+                                                       mirror::AbstractMethod** sp, Thread* thread);
+extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called,
+                                                    mirror::Object* receiver,
+                                                    mirror::AbstractMethod** sp, Thread* thread);
 extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
 extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*);
 extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
@@ -171,6 +185,10 @@
   points->pShrLong = art_quick_lshr_from_code;
   points->pUshrLong = art_quick_lushr_from_code;
 
+  // Interpreter
+  points->pInterpreterToInterpreterEntry = artInterpreterToInterpreterEntry;
+  points->pInterpreterToQuickEntry = artInterpreterToQuickEntry;
+
   // Intrinsics
   points->pIndexOf = art_quick_indexof;
   points->pMemcmp16 = art_quick_memcmp16;
@@ -178,6 +196,8 @@
   points->pMemcpy = art_quick_memcpy;
 
   // Invocation
+  points->pPortableResolutionTrampolineFromCode = artPortableResolutionTrampoline;
+  points->pQuickResolutionTrampolineFromCode = artQuickResolutionTrampoline;
   points->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
   points->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline;
   points->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
diff --git a/src/oat/runtime/x86/runtime_support_x86.S b/src/oat/runtime/x86/runtime_support_x86.S
index 734aad1..ee6db0c 100644
--- a/src/oat/runtime/x86/runtime_support_x86.S
+++ b/src/oat/runtime/x86/runtime_support_x86.S
@@ -301,56 +301,6 @@
 INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck
 INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck
 
-     /*
-     * Portable resolution trampoline.
-     */
-DEFINE_FUNCTION art_portable_resolution_trampoline
-    PUSH ebp                      // stash %ebp
-    movl %esp, %ebp               // save %esp
-    .cfi_def_cfa_register ebp
-    subl LITERAL(8), %esp         // align stack
-    movl 8(%ebp), %eax            // load the called method* into %eax
-    leal 8(%ebp), %edx            // put the called method* address in %edx
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    pushl %edx                    // pass called method* address
-    pushl 12(%ebp)                // pass receiver
-    pushl 8(%ebp)                 // pass method*
-    call SYMBOL(artPortableResolutionTrampoline)  // (method, receiver, sp, Thread*)
-    leave                         // restore the stack and %ebp
-    .cfi_def_cfa esp, 4
-    .cfi_restore ebp
-    cmpl LITERAL(0), %eax         // check if returned method code is null
-    je resolve_fail               // if null, jump to return to handle
-    jmp *%eax                     // otherwise, tail call to intended method
-resolve_fail:
-    ret
-END_FUNCTION art_portable_resolution_trampoline
-
-    /*
-     * Quick resolution trampoline.
-     */
-DEFINE_FUNCTION art_quick_resolution_trampoline
-    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    movl %esp, %edx               // save stack pointer
-    pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
-    .cfi_adjust_cfa_offset 4
-    PUSH edx                      // pass stack pointer
-    PUSH ecx                      // pass receiver
-    PUSH eax                      // pass method*
-    call SYMBOL(artQuickResolutionTrampoline)  // (method_idx, sp, Thread*)
-    movl %eax, %edi               // save returned code pointer in %edi
-    addl LITERAL(16), %esp        // pop arguments
-    .cfi_adjust_cfa_offset -16
-    POP eax                       // restore registers
-    POP ecx
-    POP edx
-    POP ebx
-    POP ebp
-    POP esi
-    xchgl %edi, (%esp)            // swap %edi and code pointer
-    ret                           // tail call to intended method
-END_FUNCTION art_quick_resolution_trampoline
-
     /*
      * Portable invocation stub.
      * On entry:
diff --git a/src/oat_test.cc b/src/oat_test.cc
index dd336d9..c7c063a 100644
--- a/src/oat_test.cc
+++ b/src/oat_test.cc
@@ -68,16 +68,16 @@
   const bool compile = false;  // DISABLED_ due to the time to compile libcore
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
 
+  // TODO: make selectable
+#if defined(ART_USE_PORTABLE_COMPILER)
+  CompilerBackend compiler_backend = kPortable;
+#else
+  CompilerBackend compiler_backend = kQuick;
+#endif
+  compiler_driver_.reset(new CompilerDriver(compiler_backend, kThumb2, false, 2, false,
+                                            NULL, true, true));
   jobject class_loader = NULL;
   if (compile) {
-    // TODO: make selectable
-#if defined(ART_USE_PORTABLE_COMPILER)
-    CompilerBackend compiler_backend = kPortable;
-#else
-    CompilerBackend compiler_backend = kQuick;
-#endif
-    compiler_driver_.reset(new CompilerDriver(compiler_backend, kThumb2, false, 2, false,
-                                              NULL, true, true));
     compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath());
   }
 
@@ -143,7 +143,7 @@
 TEST_F(OatTest, OatHeaderSizeCheck) {
   // If this test is failing and you have to update these constants,
   // it is time to update OatHeader::kOatVersion
-  EXPECT_EQ(36U, sizeof(OatHeader));
+  EXPECT_EQ(52U, sizeof(OatHeader));
   EXPECT_EQ(28U, sizeof(OatMethodOffsets));
 }
 
diff --git a/src/oat_writer.cc b/src/oat_writer.cc
index 8acbfe9..a4bd87d 100644
--- a/src/oat_writer.cc
+++ b/src/oat_writer.cc
@@ -54,13 +54,35 @@
                      uint32_t image_file_location_oat_begin,
                      const std::string& image_file_location,
                      const CompilerDriver* compiler)
-    : compiler_driver_(compiler) {
-  image_file_location_oat_checksum_ = image_file_location_oat_checksum;
-  image_file_location_oat_begin_ = image_file_location_oat_begin;
-  image_file_location_ = image_file_location;
-  dex_files_ = &dex_files;
-  oat_header_ = NULL;
-  executable_offset_padding_length_ = 0;
+  : compiler_driver_(compiler),
+    dex_files_(&dex_files),
+    image_file_location_oat_checksum_(image_file_location_oat_checksum),
+    image_file_location_oat_begin_(image_file_location_oat_begin),
+    image_file_location_(image_file_location),
+    oat_header_(NULL),
+    size_dex_file_alignment_(0),
+    size_executable_offset_alignment_(0),
+    size_oat_header_(0),
+    size_oat_header_image_file_location_(0),
+    size_dex_file_(0),
+    size_interpreter_to_interpreter_entry_(0),
+    size_interpreter_to_quick_entry_(0),
+    size_portable_resolution_trampoline_(0),
+    size_quick_resolution_trampoline_(0),
+    size_stubs_alignment_(0),
+    size_code_size_(0),
+    size_code_(0),
+    size_code_alignment_(0),
+    size_mapping_table_(0),
+    size_vmap_table_(0),
+    size_gc_map_(0),
+    size_oat_dex_file_location_size_(0),
+    size_oat_dex_file_location_data_(0),
+    size_oat_dex_file_location_checksum_(0),
+    size_oat_dex_file_offset_(0),
+    size_oat_dex_file_methods_offsets_(0),
+    size_oat_class_status_(0),
+    size_oat_class_method_offsets_(0) {
 
   size_t offset = InitOatHeader();
   offset = InitOatDexFiles(offset);
@@ -70,6 +92,7 @@
   offset = InitOatCodeDexFiles(offset);
 
   CHECK_EQ(dex_files_->size(), oat_dex_files_.size());
+  CHECK(image_file_location.empty() == compiler->IsImage());
 }
 
 OatWriter::~OatWriter() {
@@ -106,7 +129,9 @@
   // calculate the offsets within OatDexFiles to the DexFiles
   for (size_t i = 0; i != dex_files_->size(); ++i) {
     // dex files are required to be 4 byte aligned
+    size_t original_offset = offset;
     offset = RoundUp(offset, 4);
+    size_dex_file_alignment_ += offset - original_offset;
 
     // set offset in OatDexFile to DexFile
     oat_dex_files_[i]->dex_file_offset_ = offset;
@@ -162,7 +187,33 @@
   // required to be on a new page boundary
   offset = RoundUp(offset, kPageSize);
   oat_header_->SetExecutableOffset(offset);
-  executable_offset_padding_length_ = offset - old_offset;
+  size_executable_offset_alignment_ = offset - old_offset;
+  if (compiler_driver_->IsImage()) {
+    InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
+    oat_header_->SetInterpreterToInterpreterEntryOffset(offset);
+    interpreter_to_interpreter_entry_.reset(compiler_driver_->CreateInterpreterToInterpreterEntry());
+    offset += interpreter_to_interpreter_entry_->size();
+
+    offset = CompiledCode::AlignCode(offset, instruction_set);
+    oat_header_->SetInterpreterToQuickEntryOffset(offset);
+    interpreter_to_quick_entry_.reset(compiler_driver_->CreateInterpreterToQuickEntry());
+    offset += interpreter_to_quick_entry_->size();
+
+    offset = CompiledCode::AlignCode(offset, instruction_set);
+    oat_header_->SetPortableResolutionTrampolineOffset(offset);
+    portable_resolution_trampoline_.reset(compiler_driver_->CreatePortableResolutionTrampoline());
+    offset += portable_resolution_trampoline_->size();
+
+    offset = CompiledCode::AlignCode(offset, instruction_set);
+    oat_header_->SetQuickResolutionTrampolineOffset(offset);
+    quick_resolution_trampoline_.reset(compiler_driver_->CreateQuickResolutionTrampoline());
+    offset += quick_resolution_trampoline_->size();
+  } else {
+    oat_header_->SetInterpreterToInterpreterEntryOffset(0);
+    oat_header_->SetInterpreterToQuickEntryOffset(0);
+    oat_header_->SetPortableResolutionTrampolineOffset(0);
+    oat_header_->SetQuickResolutionTrampolineOffset(0);
+  }
   return offset;
 }
 
@@ -389,11 +440,13 @@
     PLOG(ERROR) << "Failed to write oat header to " << out.GetLocation();
     return false;
   }
+  size_oat_header_ += sizeof(*oat_header_);
 
   if (!out.WriteFully(image_file_location_.data(), image_file_location_.size())) {
     PLOG(ERROR) << "Failed to write oat header image file location to " << out.GetLocation();
     return false;
   }
+  size_oat_header_image_file_location_ += image_file_location_.size();
 
   if (!WriteTables(out)) {
     LOG(ERROR) << "Failed to write oat tables to " << out.GetLocation();
@@ -412,12 +465,64 @@
     return false;
   }
 
+  LOG(INFO) << "size_dex_file_alignment_=" << size_dex_file_alignment_;
+  LOG(INFO) << "size_executable_offset_alignment_=" << size_executable_offset_alignment_;
+  LOG(INFO) << "size_oat_header_=" << size_oat_header_;
+  LOG(INFO) << "size_oat_header_image_file_location_=" << size_oat_header_image_file_location_;
+  LOG(INFO) << "size_dex_file_=" << size_dex_file_;
+  LOG(INFO) << "size_interpreter_to_interpreter_entry_=" << size_interpreter_to_interpreter_entry_;
+  LOG(INFO) << "size_interpreter_to_quick_entry_=" << size_interpreter_to_quick_entry_;
+  LOG(INFO) << "size_portable_resolution_trampoline_=" << size_portable_resolution_trampoline_;
+  LOG(INFO) << "size_quick_resolution_trampoline_=" << size_quick_resolution_trampoline_;
+  LOG(INFO) << "size_stubs_alignment_=" << size_stubs_alignment_;
+  LOG(INFO) << "size_code_size_=" << size_code_size_;
+  LOG(INFO) << "size_code_=" << size_code_;
+  LOG(INFO) << "size_code_alignment_=" << size_code_alignment_;
+  LOG(INFO) << "size_mapping_table_=" << size_mapping_table_;
+  LOG(INFO) << "size_vmap_table_=" << size_vmap_table_;
+  LOG(INFO) << "size_gc_map_=" << size_gc_map_;
+  LOG(INFO) << "size_oat_dex_file_location_size_=" << size_oat_dex_file_location_size_;
+  LOG(INFO) << "size_oat_dex_file_location_data=" << size_oat_dex_file_location_data_;
+  LOG(INFO) << "size_oat_dex_file_location_checksum_=" << size_oat_dex_file_location_checksum_;
+  LOG(INFO) << "size_oat_dex_file_offset_=" << size_oat_dex_file_offset_;
+  LOG(INFO) << "size_oat_dex_file_methods_offsets_=" << size_oat_dex_file_methods_offsets_;
+  LOG(INFO) << "size_oat_class_status_=" << size_oat_class_status_;
+  LOG(INFO) << "size_oat_class_method_offsets=" << size_oat_class_method_offsets_;
+
+  uint32_t size_total =
+      size_dex_file_alignment_ +
+      size_executable_offset_alignment_ +
+      size_oat_header_ +
+      size_oat_header_image_file_location_ +
+      size_dex_file_ +
+      size_interpreter_to_interpreter_entry_ +
+      size_interpreter_to_quick_entry_ +
+      size_portable_resolution_trampoline_ +
+      size_quick_resolution_trampoline_ +
+      size_stubs_alignment_ +
+      size_code_size_ +
+      size_code_ +
+      size_code_alignment_ +
+      size_mapping_table_ +
+      size_vmap_table_ +
+      size_gc_map_ +
+      size_oat_dex_file_location_size_ +
+      size_oat_dex_file_location_data_ +
+      size_oat_dex_file_location_checksum_ +
+      size_oat_dex_file_offset_ +
+      size_oat_dex_file_methods_offsets_ +
+      size_oat_class_status_ +
+      size_oat_class_method_offsets_;
+
+  LOG(INFO) << "size_total=" << size_total;
+  DCHECK_EQ(size_total, static_cast<uint32_t>(out.Seek(0, kSeekCurrent)));
+
   return true;
 }
 
 bool OatWriter::WriteTables(OutputStream& out) {
   for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
-    if (!oat_dex_files_[i]->Write(out)) {
+    if (!oat_dex_files_[i]->Write(this, out)) {
       PLOG(ERROR) << "Failed to write oat dex information to " << out.GetLocation();
       return false;
     }
@@ -436,9 +541,10 @@
       PLOG(ERROR) << "Failed to write dex file " << dex_file->GetLocation() << " to " << out.GetLocation();
       return false;
     }
+    size_dex_file_ += dex_file->GetHeader().file_size_;
   }
   for (size_t i = 0; i != oat_classes_.size(); ++i) {
-    if (!oat_classes_[i]->Write(out)) {
+    if (!oat_classes_[i]->Write(this, out)) {
       PLOG(ERROR) << "Failed to write oat methods information to " << out.GetLocation();
       return false;
     }
@@ -448,13 +554,59 @@
 
 size_t OatWriter::WriteCode(OutputStream& out) {
   uint32_t offset = oat_header_->GetExecutableOffset();
-  off_t new_offset = out.Seek(executable_offset_padding_length_, kSeekCurrent);
+  off_t new_offset = out.Seek(size_executable_offset_alignment_, kSeekCurrent);
   if (static_cast<uint32_t>(new_offset) != offset) {
     PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset
                 << " Expected: " << offset << " File: " << out.GetLocation();
     return 0;
   }
   DCHECK_OFFSET();
+  if (compiler_driver_->IsImage()) {
+    InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
+    if (!out.WriteFully(&(*interpreter_to_interpreter_entry_)[0], interpreter_to_interpreter_entry_->size())) {
+      PLOG(ERROR) << "Failed to write interpreter to interpreter entry to " << out.GetLocation();
+      return false;
+    }
+    size_interpreter_to_interpreter_entry_ += interpreter_to_interpreter_entry_->size();
+    offset += interpreter_to_interpreter_entry_->size();
+    DCHECK_OFFSET();
+
+    uint32_t aligned_offset = CompiledCode::AlignCode(offset, instruction_set);
+    uint32_t alignment_padding = aligned_offset - offset;
+    out.Seek(alignment_padding, kSeekCurrent);
+    size_stubs_alignment_ += alignment_padding;
+    if (!out.WriteFully(&(*interpreter_to_quick_entry_)[0], interpreter_to_quick_entry_->size())) {
+      PLOG(ERROR) << "Failed to write interpreter to quick entry to " << out.GetLocation();
+      return false;
+    }
+    size_interpreter_to_quick_entry_ += interpreter_to_quick_entry_->size();
+    offset += alignment_padding + interpreter_to_quick_entry_->size();
+    DCHECK_OFFSET();
+
+    aligned_offset = CompiledCode::AlignCode(offset, instruction_set);
+    alignment_padding = aligned_offset - offset;
+    out.Seek(alignment_padding, kSeekCurrent);
+    size_stubs_alignment_ += alignment_padding;
+    if (!out.WriteFully(&(*portable_resolution_trampoline_)[0], portable_resolution_trampoline_->size())) {
+      PLOG(ERROR) << "Failed to write portable resolution trampoline to " << out.GetLocation();
+      return false;
+    }
+    size_portable_resolution_trampoline_ += portable_resolution_trampoline_->size();
+    offset += alignment_padding + portable_resolution_trampoline_->size();
+    DCHECK_OFFSET();
+
+    aligned_offset = CompiledCode::AlignCode(offset, instruction_set);
+    alignment_padding = aligned_offset - offset;
+    out.Seek(alignment_padding, kSeekCurrent);
+    size_stubs_alignment_ += alignment_padding;
+    if (!out.WriteFully(&(*quick_resolution_trampoline_)[0], quick_resolution_trampoline_->size())) {
+      PLOG(ERROR) << "Failed to write quick resolution trampoline to " << out.GetLocation();
+      return false;
+    }
+    size_quick_resolution_trampoline_ += quick_resolution_trampoline_->size();
+    offset += alignment_padding + quick_resolution_trampoline_->size();
+    DCHECK_OFFSET();
+  }
   return offset;
 }
 
@@ -547,6 +699,7 @@
     uint32_t aligned_code_delta = aligned_offset - offset;
     if (aligned_code_delta != 0) {
       off_t new_offset = out.Seek(aligned_code_delta, kSeekCurrent);
+      size_code_alignment_ += aligned_code_delta;
       if (static_cast<uint32_t>(new_offset) != aligned_offset) {
         PLOG(ERROR) << "Failed to seek to align oat code. Actual: " << new_offset
                     << " Expected: " << aligned_offset << " File: " << out.GetLocation();
@@ -572,12 +725,14 @@
         ReportWriteFailure("method code size", method_idx, dex_file, out);
         return 0;
       }
+      size_code_size_ += sizeof(code_size);
       offset += sizeof(code_size);
       DCHECK_OFFSET();
       if (!out.WriteFully(&code[0], code_size)) {
         ReportWriteFailure("method code", method_idx, dex_file, out);
         return 0;
       }
+      size_code_ += code_size;
       offset += code_size;
     }
     DCHECK_OFFSET();
@@ -602,6 +757,7 @@
         ReportWriteFailure("mapping table", method_idx, dex_file, out);
         return 0;
       }
+      size_mapping_table_ += mapping_table_size;
       offset += mapping_table_size;
     }
     DCHECK_OFFSET();
@@ -625,6 +781,7 @@
         ReportWriteFailure("vmap table", method_idx, dex_file, out);
         return 0;
       }
+      size_vmap_table_ += vmap_table_size;
       offset += vmap_table_size;
     }
     DCHECK_OFFSET();
@@ -648,6 +805,7 @@
         ReportWriteFailure("GC map", method_idx, dex_file, out);
         return 0;
       }
+      size_gc_map_ += gc_map_size;
       offset += gc_map_size;
     }
     DCHECK_OFFSET();
@@ -683,29 +841,35 @@
                             sizeof(methods_offsets_[0]) * methods_offsets_.size());
 }
 
-bool OatWriter::OatDexFile::Write(OutputStream& out) const {
+bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream& out) const {
   DCHECK_OFFSET_();
   if (!out.WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) {
     PLOG(ERROR) << "Failed to write dex file location length to " << out.GetLocation();
     return false;
   }
+  oat_writer->size_oat_dex_file_location_size_ += sizeof(dex_file_location_size_);
   if (!out.WriteFully(dex_file_location_data_, dex_file_location_size_)) {
     PLOG(ERROR) << "Failed to write dex file location data to " << out.GetLocation();
     return false;
   }
+  oat_writer->size_oat_dex_file_location_data_ += dex_file_location_size_;
   if (!out.WriteFully(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_))) {
     PLOG(ERROR) << "Failed to write dex file location checksum to " << out.GetLocation();
     return false;
   }
+  oat_writer->size_oat_dex_file_location_checksum_ += sizeof(dex_file_location_checksum_);
   if (!out.WriteFully(&dex_file_offset_, sizeof(dex_file_offset_))) {
     PLOG(ERROR) << "Failed to write dex file offset to " << out.GetLocation();
     return false;
   }
+  oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_);
   if (!out.WriteFully(&methods_offsets_[0],
                       sizeof(methods_offsets_[0]) * methods_offsets_.size())) {
     PLOG(ERROR) << "Failed to write methods offsets to " << out.GetLocation();
     return false;
   }
+  oat_writer->size_oat_dex_file_methods_offsets_ +=
+      sizeof(methods_offsets_[0]) * methods_offsets_.size();
   return true;
 }
 
@@ -736,12 +900,13 @@
                             sizeof(method_offsets_[0]) * method_offsets_.size());
 }
 
-bool OatWriter::OatClass::Write(OutputStream& out) const {
+bool OatWriter::OatClass::Write(OatWriter* oat_writer, OutputStream& out) const {
   DCHECK_OFFSET_();
   if (!out.WriteFully(&status_, sizeof(status_))) {
     PLOG(ERROR) << "Failed to write class status to " << out.GetLocation();
     return false;
   }
+  oat_writer->size_oat_class_status_ += sizeof(status_);
   DCHECK_EQ(static_cast<off_t>(GetOatMethodOffsetsOffsetFromOatHeader(0)),
             out.Seek(0, kSeekCurrent));
   if (!out.WriteFully(&method_offsets_[0],
@@ -749,6 +914,7 @@
     PLOG(ERROR) << "Failed to write method offsets to " << out.GetLocation();
     return false;
   }
+  oat_writer->size_oat_class_method_offsets_ += sizeof(method_offsets_[0]) * method_offsets_.size();
   DCHECK_EQ(static_cast<off_t>(GetOatMethodOffsetsOffsetFromOatHeader(method_offsets_.size())),
             out.Seek(0, kSeekCurrent));
   return true;
diff --git a/src/oat_writer.h b/src/oat_writer.h
index e1d76f4..b201d6b 100644
--- a/src/oat_writer.h
+++ b/src/oat_writer.h
@@ -83,7 +83,8 @@
   size_t InitOatDexFiles(size_t offset);
   size_t InitDexFiles(size_t offset);
   size_t InitOatClasses(size_t offset);
-  size_t InitOatCode(size_t offset);
+  size_t InitOatCode(size_t offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   size_t InitOatCodeDexFiles(size_t offset)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   size_t InitOatCodeDexFile(size_t offset,
@@ -120,7 +121,7 @@
     explicit OatDexFile(size_t offset, const DexFile& dex_file);
     size_t SizeOf() const;
     void UpdateChecksum(OatHeader& oat_header) const;
-    bool Write(OutputStream& out) const;
+    bool Write(OatWriter* oat_writer, OutputStream& out) const;
 
     // Offset of start of OatDexFile from beginning of OatHeader. It is
     // used to validate file position when writing.
@@ -144,7 +145,7 @@
     size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const;
     size_t SizeOf() const;
     void UpdateChecksum(OatHeader& oat_header) const;
-    bool Write(OutputStream& out) const;
+    bool Write(OatWriter* oat_writer, OutputStream& out) const;
 
     // Offset of start of OatClass from beginning of OatHeader. It is
     // used to validate file position when writing. For Portable, it
@@ -175,7 +176,35 @@
   OatHeader* oat_header_;
   std::vector<OatDexFile*> oat_dex_files_;
   std::vector<OatClass*> oat_classes_;
-  uint32_t executable_offset_padding_length_;
+  UniquePtr<const std::vector<uint8_t> > interpreter_to_interpreter_entry_;
+  UniquePtr<const std::vector<uint8_t> > interpreter_to_quick_entry_;
+  UniquePtr<const std::vector<uint8_t> > portable_resolution_trampoline_;
+  UniquePtr<const std::vector<uint8_t> > quick_resolution_trampoline_;
+
+  // output stats
+  uint32_t size_dex_file_alignment_;
+  uint32_t size_executable_offset_alignment_;
+  uint32_t size_oat_header_;
+  uint32_t size_oat_header_image_file_location_;
+  uint32_t size_dex_file_;
+  uint32_t size_interpreter_to_interpreter_entry_;
+  uint32_t size_interpreter_to_quick_entry_;
+  uint32_t size_portable_resolution_trampoline_;
+  uint32_t size_quick_resolution_trampoline_;
+  uint32_t size_stubs_alignment_;
+  uint32_t size_code_size_;
+  uint32_t size_code_;
+  uint32_t size_code_alignment_;
+  uint32_t size_mapping_table_;
+  uint32_t size_vmap_table_;
+  uint32_t size_gc_map_;
+  uint32_t size_oat_dex_file_location_size_;
+  uint32_t size_oat_dex_file_location_data_;
+  uint32_t size_oat_dex_file_location_checksum_;
+  uint32_t size_oat_dex_file_offset_;
+  uint32_t size_oat_dex_file_methods_offsets_;
+  uint32_t size_oat_class_status_;
+  uint32_t size_oat_class_method_offsets_;
 
   template <class T> struct MapCompare {
    public:
diff --git a/src/oatdump.cc b/src/oatdump.cc
index 7a99f8d..538e1bb 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -880,7 +880,7 @@
   const void* GetOatCodeBegin(mirror::AbstractMethod* m)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     const void* code = m->GetEntryPointFromCompiledCode();
-    if (code == GetResolutionTrampoline()) {
+    if (code == GetResolutionTrampoline(Runtime::Current()->GetClassLinker())) {
       code = oat_dumper_->GetOatCode(m);
     }
     if (oat_dumper_->GetInstructionSet() == kThumb2) {
diff --git a/src/runtime.cc b/src/runtime.cc
index 45d2988..c21a1c4 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -1109,7 +1109,9 @@
   // TODO: use a special method for resolution method saves
   method->SetDexMethodIndex(DexFile::kDexNoIndex16);
   // When compiling, the code pointer will get set later when the image is loaded.
-  method->SetEntryPointFromCompiledCode(Runtime::Current()->IsCompiler() ? NULL : GetResolutionTrampoline());
+  Runtime* r = Runtime::Current();
+  ClassLinker* cl = r->GetClassLinker();
+  method->SetEntryPointFromCompiledCode(r->IsCompiler() ? NULL : GetResolutionTrampoline(cl));
   return method.get();
 }
 
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 5fc8da5..094e23a 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -34,14 +34,12 @@
 extern "C" void art_jni_dlsym_lookup_stub();
 extern "C" void art_portable_abstract_method_error_stub();
 extern "C" void art_portable_proxy_invoke_handler();
-extern "C" void art_portable_resolution_trampoline();
 extern "C" void art_quick_abstract_method_error_stub();
 extern "C" void art_quick_deoptimize();
 extern "C" void art_quick_instrumentation_entry_from_code(void*);
 extern "C" void art_quick_instrumentation_exit_from_code();
 extern "C" void art_quick_interpreter_entry(void*);
 extern "C" void art_quick_proxy_invoke_handler();
-extern "C" void art_quick_resolution_trampoline();
 extern "C" void art_work_around_app_jni_bugs();
 
 extern "C" double art_l2d(int64_t l);
@@ -373,22 +371,20 @@
   return reinterpret_cast<void*>(art_quick_interpreter_entry);
 }
 
-// Return address of portable resolution trampoline stub.
-static inline void* GetPortableResolutionTrampoline() {
-  return reinterpret_cast<void*>(art_portable_resolution_trampoline);
+static inline const void* GetPortableResolutionTrampoline(ClassLinker* class_linker) {
+  return class_linker->GetPortableResolutionTrampoline();
 }
 
-// Return address of quick resolution trampoline stub.
-static inline void* GetQuickResolutionTrampoline() {
-  return reinterpret_cast<void*>(art_quick_resolution_trampoline);
+static inline const void* GetQuickResolutionTrampoline(ClassLinker* class_linker) {
+  return class_linker->GetQuickResolutionTrampoline();
 }
 
 // Return address of resolution trampoline stub for defined compiler.
-static inline void* GetResolutionTrampoline() {
+static inline const void* GetResolutionTrampoline(ClassLinker* class_linker) {
 #if defined(ART_USE_PORTABLE_COMPILER)
-  return GetPortableResolutionTrampoline();
+  return GetPortableResolutionTrampoline(class_linker);
 #else
-  return GetQuickResolutionTrampoline();
+  return GetQuickResolutionTrampoline(class_linker);
 #endif
 }
 
diff --git a/src/thread.cc b/src/thread.cc
index 91f0d7b..9e86532 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1648,6 +1648,8 @@
   ENTRY_POINT_INFO(pMemcmp16),
   ENTRY_POINT_INFO(pStringCompareTo),
   ENTRY_POINT_INFO(pMemcpy),
+  ENTRY_POINT_INFO(pPortableResolutionTrampolineFromCode),
+  ENTRY_POINT_INFO(pQuickResolutionTrampolineFromCode),
   ENTRY_POINT_INFO(pInvokeDirectTrampolineWithAccessCheck),
   ENTRY_POINT_INFO(pInvokeInterfaceTrampoline),
   ENTRY_POINT_INFO(pInvokeInterfaceTrampolineWithAccessCheck),