Add oat file generation and tests to build

- Currently builds boot.oat for host and target
  and target oat files for art tests.
- Added cross compilation support via --strip-prefix option to dex2oat
- Reduced output to prevent build log spam (Compiler::verbose_)
- Added image roots for recovering important pointers on image load
- Redid JNI stub creation and made the stub array an image root
- Fixed JNI stub test by making JNI stub array executable
- Fixed JNI UnregisterNative to having it reinstall the JNI stub
- Fixed ARM JNI stub to generate PIC code (with irogers)
- Fixed JniCompiler to generate PIC code (with irogers)
- Fixed FindNativeMethod to handle recursive calls
- Finished checkFieldType to use Object::InstanceOf
- Fixed thread unsafe access to ClassLinker::{dex_files_,dex_caches_}
- Added ResolvedMethod variant for use with Method* for context
- Fixed ImageWriter to call FixupMethod
- Fixed ImageWriter to rewrite JNI stub references
- Improved error reporting on lack of ANDROID_DATA dir or art-cache dir
- Fixed Runtime::Start to InitLibraries before creating thread peer
- Implemented Space::IsCondemned to skip spaces loaded from images
- Implemented artFindInterfaceMethodInCache,
  allowing interface invocation from managed code

Change-Id: I603e97fa0ac44508ae05a2e47c1cdb4481678d7b
diff --git a/src/assembler.h b/src/assembler.h
index 7b5e77a..173609e 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -395,7 +395,6 @@
                     ManagedRegister scratch) = 0;
   virtual void Call(FrameOffset base, Offset offset,
                     ManagedRegister scratch) = 0;
-  virtual void Call(uintptr_t addr, ManagedRegister scratch) = 0;
 
   // Generate code to check if Thread::Current()->suspend_count_ is non-zero
   // and branch to a SuspendSlowPath if it is. The SuspendSlowPath will continue
diff --git a/src/assembler_arm.cc b/src/assembler_arm.cc
index 591012f..d0fae17 100644
--- a/src/assembler_arm.cc
+++ b/src/assembler_arm.cc
@@ -1752,15 +1752,6 @@
   // TODO: place reference map on call
 }
 
-void ArmAssembler::Call(uintptr_t addr, ManagedRegister mscratch) {
-  ArmManagedRegister scratch = mscratch.AsArm();
-  CHECK(scratch.IsCoreRegister());
-  CHECK(sizeof(uintptr_t) == sizeof(int32_t));
-  LoadImmediate(scratch.AsCoreRegister(), static_cast<int32_t>(addr));
-  blx(scratch.AsCoreRegister());
-  // TODO: place reference map on call
-}
-
 void ArmAssembler::GetCurrentThread(ManagedRegister tr) {
   mov(tr.AsArm().AsCoreRegister(), ShifterOperand(TR));
 }
diff --git a/src/assembler_arm.h b/src/assembler_arm.h
index d4bf93d..cf031c7 100644
--- a/src/assembler_arm.h
+++ b/src/assembler_arm.h
@@ -516,7 +516,6 @@
                     ManagedRegister scratch);
   virtual void Call(FrameOffset base, Offset offset,
                     ManagedRegister scratch);
-  virtual void Call(uintptr_t addr, ManagedRegister scratch);
 
   // Generate code to check if Thread::Current()->suspend_count_ is non-zero
   // and branch to a SuspendSlowPath if it is. The SuspendSlowPath will continue
diff --git a/src/assembler_x86.cc b/src/assembler_x86.cc
index c303fb1..45c0086 100644
--- a/src/assembler_x86.cc
+++ b/src/assembler_x86.cc
@@ -1654,9 +1654,15 @@
 }
 
 void X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister) {
+  // TODO: Needed for:
+  // JniCompilerTest.CompileAndRunIntObjectObjectMethod
+  // JniCompilerTest.CompileAndRunStaticIntObjectObjectMethod
+  // JniCompilerTest.CompileAndRunStaticSynchronizedIntObjectObjectMethod
+  // JniCompilerTest.ReturnGlobalRef
   UNIMPLEMENTED(FATAL);
 }
 
+// TODO: remove this generator of non-PIC code
 void X86Assembler::Call(uintptr_t addr, ManagedRegister mscratch) {
   Register scratch = mscratch.AsX86().AsCpuRegister();
   movl(scratch, Immediate(addr));
diff --git a/src/check_jni.cc b/src/check_jni.cc
index ce4c2d0..61be30e 100644
--- a/src/check_jni.cc
+++ b/src/check_jni.cc
@@ -374,16 +374,11 @@
           JniAbort();
           return;
         } else {
-#if 0
-          Class* field_class = dvmFindLoadedClass(f->signature);
-          if (!obj->GetClass()->InstanceOf(field_class)) {
-            LOG(ERROR) << "JNI ERROR: attempt to set field " << PrettyField(f) << " with value of wrong type: " << PrettyType(java_object);
+          if (!obj->InstanceOf(field_type)) {
+            LOG(ERROR) << "JNI ERROR: attempt to set field " << PrettyField(f) << " with value of wrong type: " << PrettyType(obj);
             JniAbort();
             return;
           }
-#else
-          UNIMPLEMENTED(WARNING) << "need way to get Class* for a given Field*'s type";
-#endif
         }
       }
     } else if (field_type != Runtime::Current()->GetClassLinker()->FindPrimitiveClass(prim)) {
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 5b4c08c..f25bac0 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -71,7 +71,7 @@
 }
 
 ClassLinker::ClassLinker(InternTable* intern_table)
-    : classes_lock_("ClassLinker lock"),
+    : lock_("ClassLinker lock"),
       class_roots_(NULL),
       array_interfaces_(NULL),
       array_iftable_(NULL),
@@ -251,7 +251,6 @@
   // this initializes their dex_cache_ fields and register them in classes_.
   Class* Class_class = FindSystemClass("Ljava/lang/Class;");
   CHECK_EQ(java_lang_Class, Class_class);
-  // No sanity check on size as Class is variably sized
 
   java_lang_reflect_Field->SetStatus(Class::kStatusNotReady);
   Class* Field_class = FindSystemClass("Ljava/lang/reflect/Field;");
@@ -278,43 +277,6 @@
       java_lang_ref_WeakReference->GetAccessFlags() |
           kAccClassIsReference | kAccClassIsWeakReference);
 
-  // Let the heap know some key offsets into java.lang.ref instances
-  // NB we hard code the field indexes here rather than using FindInstanceField
-  // as the types of the field can't be resolved prior to the runtime being
-  // fully initialized
-  Class* java_lang_ref_Reference = FindSystemClass("Ljava/lang/ref/Reference;");
-
-  Field* pendingNext = java_lang_ref_Reference->GetInstanceField(0);
-  CHECK(pendingNext->GetName()->Equals("pendingNext"));
-  CHECK(ResolveType(pendingNext->GetTypeIdx(), pendingNext) ==
-        java_lang_ref_Reference);
-
-  Field* queue = java_lang_ref_Reference->GetInstanceField(1);
-  CHECK(queue->GetName()->Equals("queue"));
-  CHECK(ResolveType(queue->GetTypeIdx(), queue) ==
-        FindSystemClass("Ljava/lang/ref/ReferenceQueue;"));
-
-  Field* queueNext = java_lang_ref_Reference->GetInstanceField(2);
-  CHECK(queueNext->GetName()->Equals("queueNext"));
-  CHECK(ResolveType(queueNext->GetTypeIdx(), queueNext) ==
-        java_lang_ref_Reference);
-
-  Field* referent = java_lang_ref_Reference->GetInstanceField(3);
-  CHECK(referent->GetName()->Equals("referent"));
-  CHECK(ResolveType(referent->GetTypeIdx(), referent) ==
-        java_lang_Object);
-
-  Field* zombie = java_lang_ref_FinalizerReference->GetInstanceField(2);
-  CHECK(zombie->GetName()->Equals("zombie"));
-  CHECK(ResolveType(zombie->GetTypeIdx(), zombie) ==
-        java_lang_Object);
-
-  Heap::SetReferenceOffsets(referent->GetOffset(),
-                            queue->GetOffset(),
-                            queueNext->GetOffset(),
-                            pendingNext->GetOffset(),
-                            zombie->GetOffset());
-
   // Setup the ClassLoaders, adjusting the object_size_ as necessary
   Class* java_lang_ClassLoader = FindSystemClass("Ljava/lang/ClassLoader;");
   CHECK_LT(java_lang_ClassLoader->GetObjectSize(), sizeof(ClassLoader));
@@ -339,6 +301,41 @@
 }
 
 void ClassLinker::FinishInit() {
+
+  // Let the heap know some key offsets into java.lang.ref instances
+  // NB we hard code the field indexes here rather than using FindInstanceField
+  // as the types of the field can't be resolved prior to the runtime being
+  // fully initialized
+  Class* java_lang_ref_Reference = FindSystemClass("Ljava/lang/ref/Reference;");
+  Class* java_lang_ref_FinalizerReference = FindSystemClass("Ljava/lang/ref/FinalizerReference;");
+
+  Field* pendingNext = java_lang_ref_Reference->GetInstanceField(0);
+  CHECK(pendingNext->GetName()->Equals("pendingNext"));
+  CHECK_EQ(ResolveType(pendingNext->GetTypeIdx(), pendingNext), java_lang_ref_Reference);
+
+  Field* queue = java_lang_ref_Reference->GetInstanceField(1);
+  CHECK(queue->GetName()->Equals("queue"));
+  CHECK_EQ(ResolveType(queue->GetTypeIdx(), queue),
+           FindSystemClass("Ljava/lang/ref/ReferenceQueue;"));
+
+  Field* queueNext = java_lang_ref_Reference->GetInstanceField(2);
+  CHECK(queueNext->GetName()->Equals("queueNext"));
+  CHECK_EQ(ResolveType(queueNext->GetTypeIdx(), queueNext), java_lang_ref_Reference);
+
+  Field* referent = java_lang_ref_Reference->GetInstanceField(3);
+  CHECK(referent->GetName()->Equals("referent"));
+  CHECK_EQ(ResolveType(referent->GetTypeIdx(), referent), GetClassRoot(kJavaLangObject));
+
+  Field* zombie = java_lang_ref_FinalizerReference->GetInstanceField(2);
+  CHECK(zombie->GetName()->Equals("zombie"));
+  CHECK_EQ(ResolveType(zombie->GetTypeIdx(), zombie), GetClassRoot(kJavaLangObject));
+
+  Heap::SetReferenceOffsets(referent->GetOffset(),
+                            queue->GetOffset(),
+                            queueNext->GetOffset(),
+                            pendingNext->GetOffset(),
+                            zombie->GetOffset());
+
   // ensure all class_roots_ are initialized
   for (size_t i = 0; i < kClassRootsMax; i++) {
     ClassRoot class_root = static_cast<ClassRoot>(i);
@@ -494,7 +491,7 @@
   }
 
   {
-    MutexLock mu(classes_lock_);
+    MutexLock mu(lock_);
     typedef Table::const_iterator It;  // TODO: C++0x auto
     for (It it = classes_.begin(), end = classes_.end(); it != end; ++it) {
       visitor(it->second, arg);
@@ -924,6 +921,7 @@
 }
 
 void ClassLinker::RegisterDexFile(const DexFile& dex_file, DexCache* dex_cache) {
+  MutexLock mu(lock_);
   CHECK(dex_cache != NULL) << dex_file.GetLocation();
   CHECK(dex_cache->GetLocation()->Equals(dex_file.GetLocation()));
   dex_files_.push_back(&dex_file);
@@ -931,6 +929,7 @@
 }
 
 const DexFile& ClassLinker::FindDexFile(const DexCache* dex_cache) const {
+  MutexLock mu(lock_);
   for (size_t i = 0; i != dex_caches_.size(); ++i) {
     if (dex_caches_[i] == dex_cache) {
         return *dex_files_[i];
@@ -941,6 +940,7 @@
 }
 
 DexCache* ClassLinker::FindDexCache(const DexFile& dex_file) const {
+  MutexLock mu(lock_);
   for (size_t i = 0; i != dex_files_.size(); ++i) {
     if (dex_files_[i] == &dex_file) {
         return dex_caches_[i];
@@ -1150,14 +1150,14 @@
 
 bool ClassLinker::InsertClass(const StringPiece& descriptor, Class* klass) {
   size_t hash = StringPieceHash()(descriptor);
-  MutexLock mu(classes_lock_);
+  MutexLock mu(lock_);
   Table::iterator it = classes_.insert(std::make_pair(hash, klass));
   return ((*it).second == klass);
 }
 
 Class* ClassLinker::LookupClass(const StringPiece& descriptor, const ClassLoader* class_loader) {
   size_t hash = StringPieceHash()(descriptor);
-  MutexLock mu(classes_lock_);
+  MutexLock mu(lock_);
   typedef Table::const_iterator It;  // TODO: C++0x auto
   for (It it = classes_.find(hash), end = classes_.end(); it != end; ++it) {
     Class* klass = it->second;
@@ -2125,7 +2125,7 @@
 }
 
 size_t ClassLinker::NumLoadedClasses() const {
-  MutexLock mu(classes_lock_);
+  MutexLock mu(lock_);
   return classes_.size();
 }
 
diff --git a/src/class_linker.h b/src/class_linker.h
index 88871f4..e143fd8 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -104,6 +104,15 @@
                         const ClassLoader* class_loader,
                         bool is_direct);
 
+  Method* ResolveMethod(uint32_t method_idx, const Method* referrer, bool is_direct) {
+    Class* declaring_class = referrer->GetDeclaringClass();
+    DexCache* dex_cache = declaring_class->GetDexCache();
+    // TODO: we could check for a dex cache hit here
+    const ClassLoader* class_loader = declaring_class->GetClassLoader();
+    const DexFile& dex_file = FindDexFile(dex_cache);
+    return ResolveMethod(dex_file, method_idx, dex_cache, class_loader, is_direct);
+  }
+
   Field* ResolveField(uint32_t field_idx, const Method* referrer) {
     Class* declaring_class = referrer->GetDeclaringClass();
     DexCache* dex_cache = declaring_class->GetDexCache();
@@ -113,7 +122,7 @@
     return ResolveField(dex_file, field_idx, dex_cache, class_loader, true);
   }
 
-  // Resolve a method with a given ID from the DexFile, storing the
+  // Resolve a field with a given ID from the DexFile, storing the
   // result in DexCache. The ClassLinker and ClassLoader are used as
   // in ResolveType. What is unique is the is_static argument which is
   // used to determine if we are resolving a static or non-static
@@ -252,16 +261,17 @@
   void CreateReferenceOffsets(Class *klass, bool instance,
                               uint32_t reference_offsets);
 
+  // lock to protect ClassLinker state
+  mutable Mutex lock_;
+
   std::vector<const DexFile*> boot_class_path_;
 
   std::vector<const DexFile*> dex_files_;
-
   std::vector<DexCache*> dex_caches_;
 
   // multimap from a StringPiece hash code of a class descriptor to
   // Class* instances. Results should be compared for a matching
   // Class::descriptor_ and Class::class_loader_.
-  mutable Mutex classes_lock_;
   typedef std::tr1::unordered_multimap<size_t, Class*> Table;
   Table classes_;
 
diff --git a/src/common_test.h b/src/common_test.h
index fdea986..fefbbda 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -110,8 +110,10 @@
     class_linker_ = runtime_->GetClassLinker();
 
 #if defined(__i386__)
+    runtime_->SetJniStubArray(JniCompiler::CreateJniStub(kX86));
     compiler_.reset(new Compiler(kX86));
 #elif defined(__arm__)
+    runtime_->SetJniStubArray(JniCompiler::CreateJniStub(kThumb2));
     compiler_.reset(new Compiler(kThumb2));
 #endif
 
@@ -171,7 +173,7 @@
 
   const DexFile* GetLibCoreDex() {
     std::string libcore_dex_file_name = GetLibCoreDexFileName();
-    return DexFile::OpenZip(libcore_dex_file_name);
+    return DexFile::OpenZip(libcore_dex_file_name, "");
   }
 
   uint32_t FindTypeIdxByDescriptor(const DexFile& dex_file, const StringPiece& descriptor) {
@@ -217,7 +219,7 @@
     filename += "/system/framework/art-test-dex-";
     filename += name;
     filename += ".jar";
-    const DexFile* dex_file = DexFile::OpenZip(filename);
+    const DexFile* dex_file = DexFile::OpenZip(filename, "");
     CHECK(dex_file != NULL) << "Failed to open " << filename;
     return dex_file;
   }
@@ -247,6 +249,7 @@
   void CompileMethod(Method* method) {
     CHECK(method != NULL);
     compiler_->CompileOne(method);
+    MakeExecutable(runtime_->GetJniStubArray());
     MakeExecutable(method->GetCodeArray());
     MakeExecutable(method->GetInvokeStubArray());
   }
diff --git a/src/compiler.cc b/src/compiler.cc
index fa9867d..8a382f5 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -10,7 +10,7 @@
 #include "jni_internal.h"
 #include "runtime.h"
 
-extern bool oatCompileMethod(art::Method*, art::InstructionSet);
+extern bool oatCompileMethod(const art::Compiler& compiler, art::Method*, art::InstructionSet);
 
 namespace art {
 
@@ -31,7 +31,8 @@
   ByteArray* CreateAbstractMethodErrorStub(ThrowAme);
 }
 
-Compiler::Compiler(InstructionSet insns) : instruction_set_(insns), jni_compiler_(insns) {
+Compiler::Compiler(InstructionSet insns) : instruction_set_(insns), jni_compiler_(insns),
+    verbose_(false) {
   if (insns == kArm || insns == kThumb2) {
     abstract_method_error_stub_ = arm::CreateAbstractMethodErrorStub(&ThrowAbstractMethodError);
   } else if (insns == kX86) {
@@ -41,7 +42,15 @@
 
 void Compiler::CompileAll(const ClassLoader* class_loader) {
   Resolve(class_loader);
-  // TODO add verification step
+  // TODO: add verification step
+
+  // TODO: mark all verified classes initialized if they have no <clinit>
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Class* Class_class = class_linker->FindSystemClass("Ljava/lang/Class;");
+  Method* Class_clinit = Class_class->FindDirectMethod("<clinit>", "()V");
+  CHECK(Class_clinit == NULL);
+  Class_class->SetStatus(Class::kStatusInitialized);
+
   Compile(class_loader);
   SetCodeAndDirectMethods(class_loader);
 }
@@ -49,7 +58,7 @@
 void Compiler::CompileOne(Method* method) {
   const ClassLoader* class_loader = method->GetDeclaringClass()->GetClassLoader();
   Resolve(class_loader);
-  // TODO add verification step
+  // TODO: add verification step
   CompileMethod(method);
   SetCodeAndDirectMethods(class_loader);
 }
@@ -131,6 +140,8 @@
 void Compiler::CompileMethod(Method* method) {
   if (method->IsNative()) {
     jni_compiler_.Compile(method);
+    // unregister will install the stub to lookup via dlsym
+    method->UnregisterNative();
   } else if (method->IsAbstract()) {
     DCHECK(abstract_method_error_stub_ != NULL);
     if (instruction_set_ == kX86) {
@@ -140,7 +151,7 @@
       method->SetCode(abstract_method_error_stub_, kArm);
     }
   } else {
-    oatCompileMethod(method, kThumb2);
+    oatCompileMethod(*this, method, kThumb2);
   }
   CHECK(method->GetCode() != NULL);
 
diff --git a/src/compiler.h b/src/compiler.h
index 24889e8..958d9ab 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -20,6 +20,14 @@
   // Compile a single Method
   void CompileOne(Method* method);
 
+  void SetVerbose(bool verbose) {
+    verbose_ = verbose;
+  }
+
+  bool IsVerbose() const {
+    return verbose_;
+  }
+
  private:
   // Attempt to resolve all type, methods, fields, and strings
   // referenced from code in the dex file following PathClassLoader
@@ -41,6 +49,8 @@
   JniCompiler jni_compiler_;
   ByteArray* abstract_method_error_stub_;
 
+  bool verbose_;
+
   DISALLOW_COPY_AND_ASSIGN(Compiler);
 };
 
diff --git a/src/compiler/Compiler.h b/src/compiler/Compiler.h
index c1eadcc..ba97aac 100644
--- a/src/compiler/Compiler.h
+++ b/src/compiler/Compiler.h
@@ -61,12 +61,12 @@
 struct GrowableList;
 struct MIR;
 
-void oatInit(void);
+void oatInit(const Compiler& compiler);
 bool oatArchInit(void);
 void oatArchDump(void);
 bool oatStartup(void);
 void oatShutdown(void);
-bool oatCompileMethod(Method* method, OatInstructionSetType);
+bool oatCompileMethod(const Compiler& compiler, Method* method, OatInstructionSetType);
 void oatDumpStats(void);
 void oatScanAllClassPointers(void (*callback)(void* ptr));
 void oatInitializeSSAConversion(struct CompilationUnit* cUnit);
diff --git a/src/compiler/Dalvik.h b/src/compiler/Dalvik.h
index a9ddcd8..f4f94e2 100644
--- a/src/compiler/Dalvik.h
+++ b/src/compiler/Dalvik.h
@@ -28,6 +28,7 @@
 #include "object.h"
 #include "thread.h"
 #include "class_linker.h"
+#include "compiler.h"
 #include "dex_cache.h"
 #include "utils.h"
 
@@ -51,14 +52,15 @@
 #include "DexOpcodes.h"
 #include "InstrUtils.h"
 
-typedef art::JValue JValue;
-typedef art::Thread Thread;
-typedef art::Class Class;
 typedef art::Array Array;
+typedef art::Class Class;
+typedef art::Compiler Compiler;
+typedef art::Field Field;
+typedef art::JValue JValue;
 typedef art::Method Method;
 typedef art::Object Object;
-typedef art::Field Field;
 typedef art::String String;
+typedef art::Thread Thread;
 
 // From alloc/CardTable.h
 #define GC_CARD_SHIFT 7
diff --git a/src/compiler/Frontend.cc b/src/compiler/Frontend.cc
index 086c0bb..9e6617e 100644
--- a/src/compiler/Frontend.cc
+++ b/src/compiler/Frontend.cc
@@ -669,9 +669,11 @@
 /*
  * Compile a method.
  */
-bool oatCompileMethod(Method* method, art::InstructionSet insnSet)
+bool oatCompileMethod(const Compiler& compiler, Method* method, art::InstructionSet insnSet)
 {
-    LOG(INFO) << "Compiling " << PrettyMethod(method) << "...";
+    if (compiler.IsVerbose()) {
+        LOG(INFO) << "Compiling " << PrettyMethod(method) << "...";
+    }
     oatArenaReset();
 
     CompilationUnit cUnit;
@@ -687,7 +689,7 @@
 
 #if 1
     // FIXME - temp 'till properly integrated
-    oatInit();
+    oatInit(compiler);
 #endif
 
     memset(&cUnit, 0, sizeof(cUnit));
@@ -697,8 +699,8 @@
     cUnit.insnsSize = code_item->insns_size_;
 #if 1
     // TODO: Use command-line argument passing mechanism
-    cUnit.printMe = false;
-    cUnit.printMeVerbose = false;
+    cUnit.printMe = compiler.IsVerbose();
+    cUnit.printMeVerbose = compiler.IsVerbose();
     cUnit.disableOpt = 0 |
          (1 << kLoadStoreElimination) |
          (1 << kLoadHoisting) |
@@ -889,9 +891,11 @@
     method->SetFrameSizeInBytes(cUnit.frameSize);
     method->SetCoreSpillMask(cUnit.coreSpillMask);
     method->SetFpSpillMask(cUnit.fpSpillMask);
-    LOG(INFO) << "Compiled " << PrettyMethod(method)
-              << " code at " << reinterpret_cast<void*>(managed_code->GetData())
-              << " (" << managed_code->GetLength() << " bytes)";
+    if (compiler.IsVerbose()) {
+        LOG(INFO) << "Compiled " << PrettyMethod(method)
+                  << " code at " << reinterpret_cast<void*>(managed_code->GetData())
+                  << " (" << managed_code->GetLength() << " bytes)";
+    }
 #if 0
     oatDumpCFG(&cUnit, "/sdcard/cfg/");
 #endif
@@ -899,7 +903,7 @@
     return true;
 }
 
-void oatInit(void)
+void oatInit(const Compiler& compiler)
 {
 #if 1
     // FIXME - temp hack 'till properly integrated
@@ -907,7 +911,9 @@
     if (initialized)
         return;
     initialized = true;
-    LOG(INFO) << "Initializing compiler";
+    if (compiler.IsVerbose()) {
+        LOG(INFO) << "Initializing compiler";
+    }
 #endif
     if (!oatArchInit()) {
         LOG(FATAL) << "Failed to initialize oat";
diff --git a/src/compiler/Utility.cc b/src/compiler/Utility.cc
index 6254b3d..fbdf62c 100644
--- a/src/compiler/Utility.cc
+++ b/src/compiler/Utility.cc
@@ -78,7 +78,7 @@
         currentArena->next = newArena;
         currentArena = newArena;
         numArenaBlocks++;
-        if (numArenaBlocks > 10) {
+        if (numArenaBlocks > 1000) {
             LOG(INFO) << "Total arena pages: " << numArenaBlocks;
         }
         goto retry;
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
index 64e0aaa..304c7d2 100644
--- a/src/dex2oat.cc
+++ b/src/dex2oat.cc
@@ -46,14 +46,20 @@
           "  --method may be used to limit compilation to a subset of methods.\n"
           "      Example: --method=Ljava/lang/Object;<init>()V\n"
           "\n");
+  fprintf(stderr,
+          "  --strip-prefix may be used to strip a path prefix from dex file names in the\n"
+          "       the generated image to match the target file system layout.\n"
+          "      Example: --strip-prefix=out/target/product/crespo\n"
+          "\n");
   exit(EXIT_FAILURE);
 }
 
 static void OpenDexFiles(std::vector<const char*>& dex_filenames,
-                         std::vector<const DexFile*>& dex_files) {
+                         std::vector<const DexFile*>& dex_files,
+                         const std::string& strip_location_prefix) {
   for (size_t i = 0; i < dex_filenames.size(); i++) {
     const char* dex_filename = dex_filenames[i];
-    const DexFile* dex_file = DexFile::Open(dex_filename);
+    const DexFile* dex_file = DexFile::Open(dex_filename, strip_location_prefix);
     if (dex_file == NULL) {
       fprintf(stderr, "could not open .dex from file %s\n", dex_filename);
       exit(EXIT_FAILURE);
@@ -78,6 +84,8 @@
   std::string boot_image_option;
   std::vector<const char*> boot_dex_filenames;
   uintptr_t image_base = 0;
+  std::string strip_location_prefix;
+
   for (int i = 0; i < argc; i++) {
     const StringPiece option(argv[i]);
     if (option.starts_with("--dex-file=")) {
@@ -101,6 +109,8 @@
       boot_image_option += boot_image_filename;
     } else if (option.starts_with("--boot-dex-file=")) {
       boot_dex_filenames.push_back(option.substr(strlen("--boot-dex-file=")).data());
+    } else if (option.starts_with("--strip-prefix=")) {
+      strip_location_prefix = option.substr(strlen("--strip-prefix=")).data();
     } else {
       fprintf(stderr, "unknown argument %s\n", option.data());
       usage();
@@ -125,10 +135,10 @@
   }
 
   std::vector<const DexFile*> dex_files;
-  OpenDexFiles(dex_filenames, dex_files);
+  OpenDexFiles(dex_filenames, dex_files, strip_location_prefix);
 
   std::vector<const DexFile*> boot_dex_files;
-  OpenDexFiles(boot_dex_filenames, boot_dex_files);
+  OpenDexFiles(boot_dex_filenames, boot_dex_files, strip_location_prefix);
 
   Runtime::Options options;
   if (boot_image_option.empty()) {
@@ -162,6 +172,11 @@
     class_loader = PathClassLoader::Alloc(dex_files);
   }
 
+  // if we loaded an existing image, we will reuse its stub array.
+  if (!runtime->HasJniStubArray()) {
+    runtime->SetJniStubArray(JniCompiler::CreateJniStub(kThumb2));
+  }
+
   Compiler compiler(kThumb2);
   if (method_names.empty()) {
     compiler.CompileAll(class_loader);
diff --git a/src/dex_file.cc b/src/dex_file.cc
index b982291..71fd5a7 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -42,16 +42,17 @@
                         reinterpret_cast<const DexFile::ClassDef*>(NULL));
 }
 
-const DexFile* DexFile::Open(const std::string& filename) {
+const DexFile* DexFile::Open(const std::string& filename,
+                             const std::string& strip_location_prefix) {
   if (filename.size() < 4) {
     LOG(WARNING) << "Ignoring short classpath entry '" << filename << "'";
     return NULL;
   }
   std::string suffix(filename.substr(filename.size() - 4));
   if (suffix == ".zip" || suffix == ".jar" || suffix == ".apk") {
-    return DexFile::OpenZip(filename);
+    return DexFile::OpenZip(filename, strip_location_prefix);
   } else {
-    return DexFile::OpenFile(filename);
+    return DexFile::OpenFile(filename, filename, strip_location_prefix);
   }
 }
 
@@ -69,7 +70,15 @@
 DexFile::PtrCloser::PtrCloser(byte* addr) : addr_(addr) {}
 DexFile::PtrCloser::~PtrCloser() { delete[] addr_; }
 
-const DexFile* DexFile::OpenFile(const std::string& filename) {
+const DexFile* DexFile::OpenFile(const std::string& filename,
+                                 const std::string& original_location,
+                                 const std::string& strip_location_prefix) {
+  StringPiece location = original_location;
+  if (!location.starts_with(strip_location_prefix)) {
+    LOG(ERROR) << filename << " does not start with " << strip_location_prefix;
+    return NULL;
+  }
+  location.remove_prefix(strip_location_prefix.size());
   int fd = open(filename.c_str(), O_RDONLY);  // TODO: scoped_fd
   if (fd == -1) {
     PLOG(ERROR) << "open(\"" << filename << "\", O_RDONLY) failed";
@@ -92,7 +101,7 @@
   close(fd);
   byte* dex_file = reinterpret_cast<byte*>(addr);
   Closer* closer = new MmapCloser(addr, length);
-  return Open(dex_file, length, filename, closer);
+  return Open(dex_file, length, location.ToString(), closer);
 }
 
 static const char* kClassesDex = "classes.dex";
@@ -152,7 +161,8 @@
 };
 
 // Open classes.dex from within a .zip, .jar, .apk, ...
-const DexFile* DexFile::OpenZip(const std::string& filename) {
+const DexFile* DexFile::OpenZip(const std::string& filename,
+                                const std::string& strip_location_prefix) {
 
   // First, look for a ".dex" alongside the jar file.  It will have
   // the same name/path except for the extension.
@@ -161,7 +171,7 @@
   std::string adjacent_dex_filename(filename);
   size_t found = adjacent_dex_filename.find_last_of(".");
   if (found == std::string::npos) {
-    LOG(WARNING) << "No . in filename" << filename;
+    LOG(ERROR) << "No . in filename" << filename;
     return NULL;
   }
   adjacent_dex_filename.replace(adjacent_dex_filename.begin() + found,
@@ -169,7 +179,9 @@
                                 ".dex");
   // Example adjacent_dex_filename = dir/foo.dex
   if (OS::FileExists(adjacent_dex_filename.c_str())) {
-    const DexFile* adjacent_dex_file = DexFile::OpenFile(adjacent_dex_filename);
+    const DexFile* adjacent_dex_file = DexFile::OpenFile(adjacent_dex_filename,
+                                                         filename,
+                                                         strip_location_prefix);
     if (adjacent_dex_file != NULL) {
       // We don't verify anything in this case, because we aren't in
       // the cache and typically the file is in the readonly /system
@@ -182,8 +194,8 @@
   char resolved[PATH_MAX];
   char* absolute_path = realpath(filename.c_str(), resolved);
   if (absolute_path == NULL) {
-      LOG(WARNING) << "Failed to create absolute path for " << filename
-                   << " when looking for classes.dex";
+      LOG(ERROR) << "Failed to create absolute path for " << filename
+                 << " when looking for classes.dex";
       return NULL;
   }
   std::string cache_file(absolute_path+1); // skip leading slash
@@ -194,20 +206,43 @@
 
   const char* data_root = getenv("ANDROID_DATA");
   if (data_root == NULL) {
-    data_root = "/data";
+    if (OS::DirectoryExists("/data")) {
+      data_root = "/data";
+    } else {
+      data_root = "/tmp";
+    }
+  }
+  if (!OS::DirectoryExists(data_root)) {
+    LOG(ERROR) << "Failed to find ANDROID_DATA directory " << data_root;
+    return NULL;
   }
 
-  std::string cache_path_tmp = StringPrintf("%s/art-cache/%s", data_root, cache_file.c_str());
+  std::string art_cache = StringPrintf("%s/art-cache", data_root);
+
+  if (!OS::DirectoryExists(art_cache.c_str())) {
+    if (StringPiece(art_cache).starts_with("/tmp/")) {
+      int result = mkdir(art_cache.c_str(), 0700);
+      if (result != 0) {
+        LOG(ERROR) << "Failed to create art-cache directory " << art_cache;
+        return NULL;
+      }
+    } else {
+      LOG(ERROR) << "Failed to find art-cache directory " << art_cache;
+      return NULL;
+    }
+  }
+
+  std::string cache_path_tmp = StringPrintf("%s/%s", art_cache.c_str(), cache_file.c_str());
   // Example cache_path_tmp = /data/art-cache/parent@dir@foo.jar@classes.dex
 
   UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(filename));
   if (zip_archive.get() == NULL) {
-    LOG(WARNING) << "Failed to open " << filename << " when looking for classes.dex";
+    LOG(ERROR) << "Failed to open " << filename << " when looking for classes.dex";
     return NULL;
   }
   UniquePtr<ZipEntry> zip_entry(zip_archive->Find(kClassesDex));
   if (zip_entry.get() == NULL) {
-    LOG(WARNING) << "Failed to find classes.dex within " << filename;
+    LOG(ERROR) << "Failed to find classes.dex within " << filename;
     return NULL;
   }
 
@@ -216,7 +251,9 @@
 
   while (true) {
     if (OS::FileExists(cache_path.c_str())) {
-      const DexFile* cached_dex_file = DexFile::OpenFile(cache_path);
+      const DexFile* cached_dex_file = DexFile::OpenFile(cache_path,
+                                                         filename,
+                                                         strip_location_prefix);
       if (cached_dex_file != NULL) {
         return cached_dex_file;
       }
@@ -355,7 +392,7 @@
 bool DexFile::CheckMagic(const byte* magic) {
   CHECK(magic != NULL);
   if (memcmp(magic, kDexMagic, sizeof(kDexMagic)) != 0) {
-    LOG(WARNING) << "Unrecognized magic number:"
+    LOG(ERROR) << "Unrecognized magic number:"
             << " " << magic[0]
             << " " << magic[1]
             << " " << magic[2]
@@ -364,7 +401,7 @@
   }
   const byte* version = &magic[sizeof(kDexMagic)];
   if (memcmp(version, kDexMagicVersion, sizeof(kDexMagicVersion)) != 0) {
-    LOG(WARNING) << "Unrecognized version number:"
+    LOG(ERROR) << "Unrecognized version number:"
             << " " << version[0]
             << " " << version[1]
             << " " << version[2]
diff --git a/src/dex_file.h b/src/dex_file.h
index cf0bf20..4448981 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -317,13 +317,17 @@
                                         const ClassPath& class_path);
 
   // Opens .dex file, guessing the format based on file extension
-  static const DexFile* Open(const std::string& filename);
+  static const DexFile* Open(const std::string& filename,
+                             const std::string& strip_location_prefix);
 
   // Opens a .dex file from the file system.
-  static const DexFile* OpenFile(const std::string& filename);
+  static const DexFile* OpenFile(const std::string& filename,
+                                 const std::string& original_location,
+                                 const std::string& strip_location_prefix);
 
   // Opens a .jar, .zip, or .apk file from the file system.
-  static const DexFile* OpenZip(const std::string& filename);
+  static const DexFile* OpenZip(const std::string& filename,
+                                const std::string& strip_location_prefix);
 
   // Opens a .dex file from a new allocated pointer.  location is used
   // to identify the source, for example "/system/framework/core.jar"
diff --git a/src/image.h b/src/image.h
index 02d15de..0694630 100644
--- a/src/image.h
+++ b/src/image.h
@@ -15,7 +15,8 @@
  public:
   ImageHeader() {}
 
-  ImageHeader(uint32_t base_addr) : base_addr_(base_addr) {
+  ImageHeader(uint32_t base_addr, uint32_t image_roots)
+      : base_addr_(base_addr), image_roots_(image_roots) {
     memcpy(magic_, kImageMagic, sizeof(kImageMagic));
     memcpy(version_, kImageVersion, sizeof(kImageVersion));
   }
@@ -34,6 +35,15 @@
     return reinterpret_cast<byte*>(base_addr_);
   }
 
+  enum ImageRoot {
+    kJniStubArray,
+    kImageRootsMax,
+  };
+
+  Object* GetImageRoot(ImageRoot image_root) const {
+    return reinterpret_cast<ObjectArray<Object>*>(image_roots_)->Get(image_root);
+  }
+
  private:
   static const byte kImageMagic[4];
   static const byte kImageVersion[4];
@@ -43,6 +53,11 @@
 
   // required base address for mapping the image.
   uint32_t base_addr_;
+
+  // absolute address of an Object[] of objects needed to reinitialize from an image
+  uint32_t image_roots_;
+
+  friend class ImageWriter;
 };
 
 }  // namespace art
diff --git a/src/image_test.cc b/src/image_test.cc
index 02a7d69..8f3f0b7 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -66,6 +66,8 @@
   ASSERT_TRUE(runtime_.get() != NULL);
   class_linker_ = runtime_->GetClassLinker();
 
+  ASSERT_TRUE(runtime_->GetJniStubArray() != NULL);
+
   ASSERT_EQ(2U, Heap::GetSpaces().size());
   Space* boot_space = Heap::GetBootSpace();
   ASSERT_TRUE(boot_space != NULL);
diff --git a/src/image_writer.cc b/src/image_writer.cc
index c265838..17171bb 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -99,19 +99,34 @@
   }
 }
 
+ObjectArray<Object>* CreateImageRoots() {
+  // build a Object[] of the roots needed to restore the runtime
+  Runtime* runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  Class* object_array_class = class_linker->FindSystemClass("[Ljava/lang/Object;");
+  ObjectArray<Object>* image_roots = ObjectArray<Object>::Alloc(object_array_class,
+                                                                ImageHeader::kImageRootsMax);
+  image_roots->Set(ImageHeader::kJniStubArray, runtime->GetJniStubArray());
+  return image_roots;
+}
+
 void ImageWriter::CalculateNewObjectOffsets() {
+  ObjectArray<Object>* image_roots = CreateImageRoots();
+
   HeapBitmap* heap_bitmap = Heap::GetLiveBits();
   DCHECK(heap_bitmap != NULL);
   DCHECK_EQ(0U, image_top_);
 
-  // leave space for the header, but do not write it yet
+  // leave space for the header, but do not write it yet, we need to
+  // know where image_roots is going to end up
   image_top_ += RoundUp(sizeof(ImageHeader), 8); // 64-bit-alignment
 
   heap_bitmap->Walk(CalculateNewObjectOffsetsCallback, this);  // TODO: add Space-limited Walk
   DCHECK_LT(image_top_, image_->GetLength());
 
-  // return to write header at start of image
-  ImageHeader image_header(reinterpret_cast<uint32_t>(image_base_));
+  // return to write header at start of image with future location of image_roots
+  ImageHeader image_header(reinterpret_cast<uint32_t>(image_base_),
+                           reinterpret_cast<uint32_t>(GetImageAddress(image_roots)));
   memcpy(image_->GetAddress(), &image_header, sizeof(image_header));
 
   // Note that top_ is left at end of used space
@@ -156,6 +171,8 @@
     FixupClass(orig->AsClass(), down_cast<Class*>(copy));
   } else if (orig->IsObjectArray()) {
     FixupObjectArray(orig->AsObjectArray<Object>(), down_cast<ObjectArray<Object>*>(copy));
+  } else if (orig->IsMethod()) {
+    FixupMethod(orig->AsMethod(), down_cast<Method*>(copy));
   } else {
     FixupInstanceFields(orig, copy);
   }
@@ -174,16 +191,22 @@
   const void* copy_code = copy_code_array->GetData();
   // TODO: remember InstructionSet with each code array so we know if we need to do thumb fixup?
   if ((reinterpret_cast<uintptr_t>(orig_code) % 2) == 1) {
-      return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(copy_code) + 1);
+    return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(copy_code) + 1);
   }
   return copy_code;
 }
 
 void ImageWriter::FixupMethod(const Method* orig, Method* copy) {
   FixupInstanceFields(orig, copy);
-  // TODO: convert shorty_ to heap allocated storage
   copy->code_ = FixupCode(copy->code_array_, orig->code_);
   copy->invoke_stub_ = reinterpret_cast<Method::InvokeStub*>(FixupCode(copy->invoke_stub_array_, reinterpret_cast<void*>(orig->invoke_stub_)));
+  if (orig->IsNative()) {
+    ByteArray* orig_jni_stub_array_ = Runtime::Current()->GetJniStubArray();
+    ByteArray* copy_jni_stub_array_ = down_cast<ByteArray*>(GetImageAddress(orig_jni_stub_array_));
+    copy->native_method_ = copy_jni_stub_array_->GetData();
+  } else {
+    DCHECK(copy->native_method_ == NULL);
+  }
 }
 
 void ImageWriter::FixupObjectArray(const ObjectArray<Object>* orig, ObjectArray<Object>* copy) {
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index 11b99c8..eb84f14 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -25,7 +25,20 @@
 ByteArray* CreateJniStub();
 }
 
-JniCompiler::JniCompiler(InstructionSet insns) : jni_stub_(NULL) {
+ByteArray* JniCompiler::CreateJniStub(InstructionSet instruction_set) {
+  switch (instruction_set) {
+    case kArm:
+    case kThumb2:
+      return arm::CreateJniStub();
+    case kX86:
+      return x86::CreateJniStub();
+    default:
+      LOG(FATAL) << "Unknown InstructionSet " << (int) instruction_set;
+      return NULL;
+  }
+}
+
+JniCompiler::JniCompiler(InstructionSet insns) {
   if (insns == kThumb2) {
     // currently only ARM code generation is supported
     instruction_set_ = kArm;
@@ -36,11 +49,6 @@
 
 JniCompiler::~JniCompiler() {}
 
-// Return value helper for jobject return types
-static Object* DecodeJObjectInThread(Thread* thread, jobject obj) {
-  return thread->DecodeJObject(obj);
-}
-
 // Generate the JNI bridge for the given method, general contract:
 // - Arguments are in the managed runtime format, either on stack or in
 //   registers, a reference to the method object is supplied as part of this
@@ -68,17 +76,6 @@
   // Cache of IsStatic as we call it often enough
   const bool is_static = native_method->IsStatic();
 
-  // 0. native_method->RegisterNative(jni_stub_ stuff). Note that jni_stub_ will invoke dlsym.
-  if (jni_stub_ == NULL) {
-    if (instruction_set_ == kArm) {
-      jni_stub_ = arm::CreateJniStub();
-    } else {
-      jni_stub_ = x86::CreateJniStub();
-    }
-  }
-  if (!native_method->IsRegistered()) {
-    native_method->RegisterNative(jni_stub_->GetData());
-  }
   // TODO: Need to make sure that the stub is copied into the image. I.e.,
   // ByteArray* needs to be reachable either as a root or from the object graph.
 
@@ -347,14 +344,17 @@
     jni_conv->ResetIterator(FrameOffset(out_arg_size));
     if (jni_conv->IsCurrentParamInRegister()) {
       __ GetCurrentThread(jni_conv->CurrentParamRegister());
+      __ Call(jni_conv->CurrentParamRegister(),
+              Offset(OFFSETOF_MEMBER(Thread, pDecodeJObjectInThread)),
+              jni_conv->InterproceduralScratchRegister());
     } else {
       __ GetCurrentThread(jni_conv->CurrentParamStackOffset(),
                           jni_conv->InterproceduralScratchRegister());
+      __ Call(jni_conv->CurrentParamStackOffset(),
+              Offset(OFFSETOF_MEMBER(Thread, pDecodeJObjectInThread)),
+              jni_conv->InterproceduralScratchRegister());
     }
 
-    __ Call(reinterpret_cast<uintptr_t>(DecodeJObjectInThread),
-            jni_conv->InterproceduralScratchRegister());
-
     __ DecreaseFrameSize(out_arg_size);
     jni_conv->ResetIterator(FrameOffset(0));
   }
diff --git a/src/jni_compiler.h b/src/jni_compiler.h
index 25514b9..8b5f31a 100644
--- a/src/jni_compiler.h
+++ b/src/jni_compiler.h
@@ -25,6 +25,10 @@
 
   void Compile(Method* method);
 
+  // Stub to perform native method symbol lookup via dlsym
+  // TODO: remove from JniCompiler
+  static ByteArray* CreateJniStub(InstructionSet instruction_set);
+
  private:
   // Copy a single parameter from the managed to the JNI calling convention
   void CopyParameter(Assembler* jni_asm,
@@ -38,8 +42,6 @@
 
   InstructionSet instruction_set_;
 
-  ByteArray* jni_stub_;  // Stub to perform native method symbol lookup
-
   DISALLOW_COPY_AND_ASSIGN(JniCompiler);
 };
 
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 7a5a487..20c403c 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -233,6 +233,10 @@
 }
 
 TEST_F(JniCompilerTest, CompileAndRunIntObjectObjectMethod) {
+#if !defined(__arm__)
+  UNIMPLEMENTED(WARNING) << "needs X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister)";
+  return;
+#endif
   SetupForTest(false, "fooIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClass_fooIOO));
@@ -332,6 +336,10 @@
 
 
 TEST_F(JniCompilerTest, CompileAndRunStaticIntObjectObjectMethod) {
+#if !defined(__arm__)
+  UNIMPLEMENTED(WARNING) << "needs X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister)";
+  return;
+#endif
   SetupForTest(true, "fooSIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClass_fooSIOO));
@@ -382,6 +390,10 @@
 }
 
 TEST_F(JniCompilerTest, CompileAndRunStaticSynchronizedIntObjectObjectMethod) {
+#if !defined(__arm__)
+  UNIMPLEMENTED(WARNING) << "needs X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister)";
+  return;
+#endif
   SetupForTest(true, "fooSSIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClass_fooSSIOO));
@@ -534,6 +546,10 @@
 }
 
 TEST_F(JniCompilerTest, ReturnGlobalRef) {
+#if !defined(__arm__)
+  UNIMPLEMENTED(WARNING) << "needs X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister)";
+  return;
+#endif
   SetupForTest(false, "fooO", "(Ljava/lang/Object;)Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClass_fooO));
   jobject result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, jobj_);
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 56be2f4..b9b1ab0 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -572,7 +572,7 @@
   }
 
   // See section 11.3 "Linking Native Methods" of the JNI spec.
-  void* FindNativeMethod(const Method* m) {
+  void* FindNativeMethod(const Method* m, std::string& detail) {
     std::string jni_short_name(JniShortName(m));
     std::string jni_long_name(JniLongName(m));
     const ClassLoader* declaring_class_loader = m->GetDeclaringClass()->GetClassLoader();
@@ -595,12 +595,9 @@
         return fn;
       }
     }
-    std::string detail;
     detail += "No implementation found for ";
     detail += PrettyMethod(m);
     LOG(ERROR) << detail;
-    Thread::Current()->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;",
-        "%s", detail.c_str());
     return NULL;
   }
 
@@ -2840,8 +2837,18 @@
     CHECK_GE(c->GetStatus(), Class::kStatusInitializing);
   }
 
-  MutexLock mu(libraries_lock);
-  return libraries->FindNativeMethod(m);
+  std::string detail;
+  void* native_method;
+  {
+    MutexLock mu(libraries_lock);
+    native_method = libraries->FindNativeMethod(m, detail);
+  }
+  // throwing can cause libraries_lock to be reacquired
+  if (native_method == NULL) {
+    Thread::Current()->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;",
+        "%s", detail.c_str());
+  }
+  return native_method;
 }
 
 void JavaVMExt::VisitRoots(Heap::RootVisitor* visitor, void* arg) {
diff --git a/src/object.cc b/src/object.cc
index 373c8c2..24fe628 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -513,6 +513,25 @@
   self->PopNativeToManagedRecord(record);
 }
 
+bool Method::IsRegistered() {
+  void* native_method = GetFieldPtr<void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_), false);
+  void* jni_stub = Runtime::Current()->GetJniStubArray()->GetData();
+  return native_method != jni_stub;
+}
+
+void Method::RegisterNative(const void* native_method) {
+  CHECK(IsNative());
+  CHECK(native_method != NULL);
+  SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_),
+                           native_method, false);
+}
+
+void Method::UnregisterNative() {
+  CHECK(IsNative());
+  // restore stub to lookup native pointer via dlsym
+  RegisterNative(Runtime::Current()->GetJniStubArray()->GetData());
+}
+
 void Class::SetStatus(Status new_status) {
   CHECK(new_status > GetStatus() || new_status == kStatusError ||
       !Runtime::Current()->IsStarted());
@@ -790,7 +809,7 @@
       return interface_entry->GetMethodArray()->Get(method->GetMethodIndex());
     }
   }
-  UNIMPLEMENTED(FATAL) << "Need to throw an error of some kind";
+  UNIMPLEMENTED(FATAL) << "Need to throw an error of some kind " << PrettyMethod(method);
   return NULL;
 }
 
diff --git a/src/object.h b/src/object.h
index 3d11009..2f9ea33 100644
--- a/src/object.h
+++ b/src/object.h
@@ -892,22 +892,11 @@
                return_pc_offset_in_bytes, false);
   }
 
-  bool IsRegistered() {
-    return GetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_), false) != NULL;
-  }
+  bool IsRegistered();
 
-  void RegisterNative(const void* native_method) {
-    CHECK(IsNative());
-    CHECK(native_method != NULL);
-    SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_),
-                             native_method, false);
-  }
+  void RegisterNative(const void* native_method);
 
-  void UnregisterNative() {
-    CHECK(IsNative());
-    SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_),
-                             NULL, false);
-  }
+  void UnregisterNative();
 
   static MemberOffset NativeMethodOffset() {
     return OFFSET_OF_OBJECT_MEMBER(Method, native_method_);
diff --git a/src/os.h b/src/os.h
index dda5a30..c36680d 100644
--- a/src/os.h
+++ b/src/os.h
@@ -20,6 +20,9 @@
 
   // Check if a file exists.
   static bool FileExists(const char* name);
+
+  // Check if a directory exists.
+  static bool DirectoryExists(const char* name);
 };
 
 }  // namespace art
diff --git a/src/os_linux.cc b/src/os_linux.cc
index 6d84b51..3fae386 100644
--- a/src/os_linux.cc
+++ b/src/os_linux.cc
@@ -36,4 +36,13 @@
   }
 }
 
+bool OS::DirectoryExists(const char* name) {
+  struct stat st;
+  if (stat(name, &st) == 0) {
+    return S_ISDIR(st.st_mode);  // TODO: Deal with symlinks?
+  } else {
+    return false;
+  }
+}
+
 }  // namespace art
diff --git a/src/runtime.cc b/src/runtime.cc
index 0d48aea..c21dcc5 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -30,6 +30,7 @@
       class_linker_(NULL),
       signal_catcher_(NULL),
       java_vm_(NULL),
+      jni_stub_array_(NULL),
       started_(false),
       vfprintf_(NULL),
       exit_(NULL),
@@ -171,7 +172,7 @@
   std::vector<std::string> parsed;
   Split(class_path, ':', parsed);
   for (size_t i = 0; i < parsed.size(); ++i) {
-    const DexFile* dex_file = DexFile::Open(parsed[i]);
+    const DexFile* dex_file = DexFile::Open(parsed[i], "");
     if (dex_file != NULL) {
       class_path_vector.push_back(dex_file);
     }
@@ -339,9 +340,8 @@
 
   // Finish attaching the main thread.
   Thread* main_thread = Thread::Current();
-  main_thread->CreatePeer("main", false);
-
   instance_->InitLibraries();
+  main_thread->CreatePeer("main", false);
   instance_->signal_catcher_ = new SignalCatcher;
 }
 
@@ -484,6 +484,7 @@
   intern_table_->VisitRoots(visitor, arg);
   java_vm_->VisitRoots(visitor, arg);
   thread_list_->VisitRoots(visitor, arg);
+  visitor(jni_stub_array_, arg);
 
   //(*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg);
   //(*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg);
diff --git a/src/runtime.h b/src/runtime.h
index 5e57018..084e49c 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -20,6 +20,8 @@
 
 namespace art {
 
+template<class T> class PrimitiveArray;
+typedef PrimitiveArray<int8_t> ByteArray;
 class ClassLinker;
 class DexFile;
 class Heap;
@@ -136,6 +138,21 @@
 
   void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
 
+  bool HasJniStubArray() const {
+    return jni_stub_array_ != NULL;
+  }
+
+  ByteArray* GetJniStubArray() const {
+    CHECK(jni_stub_array_ != NULL);
+    return jni_stub_array_;
+  }
+
+  void SetJniStubArray(ByteArray* jni_stub_array) {
+    CHECK(jni_stub_array != NULL);
+    CHECK(jni_stub_array_ == NULL || jni_stub_array_ == jni_stub_array);
+    jni_stub_array_ = jni_stub_array;
+  }
+
  private:
   static void PlatformAbort(const char*, int);
 
@@ -164,6 +181,8 @@
 
   JavaVMExt* java_vm_;
 
+  ByteArray* jni_stub_array_;
+
   bool started_;
 
   // Hooks supported by JNI_CreateJavaVM
diff --git a/src/space.cc b/src/space.cc
index 36e3cf2..38c5720 100644
--- a/src/space.cc
+++ b/src/space.cc
@@ -114,6 +114,9 @@
   image_header_ = reinterpret_cast<ImageHeader*>(map->GetAddress());
   DCHECK_EQ(0, memcmp(&image_header, image_header_, sizeof(ImageHeader)));
 
+  Object* jni_stub_array = image_header.GetImageRoot(ImageHeader::kJniStubArray);
+  Runtime::Current()->SetJniStubArray(down_cast<ByteArray*>(jni_stub_array));
+
   Init(map.release());
   return true;
 }
diff --git a/src/space.h b/src/space.h
index 92b017a..e95d2b0 100644
--- a/src/space.h
+++ b/src/space.h
@@ -59,7 +59,7 @@
   size_t AllocationSize(const Object* obj);
 
   bool IsCondemned() const {
-    return true;  // TODO
+    return mspace_ != NULL;
   }
 
  private:
@@ -97,8 +97,6 @@
 
   byte* limit_;
 
-  // bool is_condemned_;  // TODO: with IsCondemned
-
   DISALLOW_COPY_AND_ASSIGN(Space);
 };
 
diff --git a/src/stub_arm.cc b/src/stub_arm.cc
index 7939780..0711085 100644
--- a/src/stub_arm.cc
+++ b/src/stub_arm.cc
@@ -51,7 +51,7 @@
   __ mov(R0, ShifterOperand(R9));
 
   // Call FindNativeMethod
-  __ LoadImmediate(R12, reinterpret_cast<int32_t>(&FindNativeMethod));
+  __ LoadFromOffset(kLoadWord, R12, TR, OFFSETOF_MEMBER(Thread, pFindNativeMethod));
   __ blx(R12);
 
   // Save result of FindNativeMethod in R12
diff --git a/src/stub_x86.cc b/src/stub_x86.cc
index ce9a875..fd8d0f2 100644
--- a/src/stub_x86.cc
+++ b/src/stub_x86.cc
@@ -21,6 +21,8 @@
   __ pushl(EDI); // Method*
 
   // Call throw_ame to throw AbstractMethodError
+  // TODO: make this PIC (throw_ame will not be in the same location after image load)
+  // TODO: remove X86Assembler::Call(uintptr_t addr, ManagedRegister scratch)
   __ Call(reinterpret_cast<int32_t>(throw_ame), X86ManagedRegister::FromCpuRegister(ECX));
 
   // Because the call above never returns, we do not need to do ESP+=16 here.
@@ -48,6 +50,8 @@
   __ fs()->movl(ECX, Address::Absolute(Thread::SelfOffset()));
   __ pushl(ECX);  // Thread*
 
+  // TODO: make this PIC (FindNativeMethod will not be in the same location after image load)
+  // TODO: remove X86Assembler::Call(uintptr_t addr, ManagedRegister scratch)
   __ Call(reinterpret_cast<int32_t>(&FindNativeMethod), X86ManagedRegister::FromCpuRegister(ECX));
 
   __ addl(ESP, Immediate(16));
diff --git a/src/thread.cc b/src/thread.cc
index 426677e..d879cea 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -48,33 +48,6 @@
     LOG(INFO) << "Info: " << info;
 }
 
-/*
- * TODO: placeholder for a method that can be called by the
- * invoke-interface trampoline to unwind and handle exception.  The
- * trampoline will arrange it so that the caller appears to be the
- * callsite of the failed invoke-interface.  See comments in
- * compiler/runtime_support.S
- */
-extern "C" void artFailedInvokeInterface() {
-    UNIMPLEMENTED(FATAL) << "Unimplemented exception throw";
-}
-
-// TODO: placeholder.  See comments in compiler/runtime_support.S
-extern "C" uint64_t artFindInterfaceMethodInCache(uint32_t method_idx,
-     Object* this_object , Method* caller_method)
-{
-    /*
-     * Note: this_object has not yet been null-checked.  To match
-     * the old-world state, nullcheck this_object and load
-     * Class* this_class = this_object->GetClass().
-     * See comments and possible thrown exceptions in old-world
-     * Interp.cpp:dvmInterpFindInterfaceMethod, and complete with
-     * new-world FindVirtualMethodForInterface.
-     */
-    UNIMPLEMENTED(FATAL) << "Unimplemented invoke interface";
-    return 0LL;
-}
-
 // TODO: placeholder.  This is what generated code will call to throw
 void ThrowException(Thread* thread, Throwable* exception) {
   /*
@@ -146,6 +119,7 @@
 
 // TODO: placeholder
 void StackOverflowFromCode(Method* method) {
+  Thread::Current()->Dump(std::cerr);
   //NOTE: to save code space, this handler needs to look up its own Thread*
   UNIMPLEMENTED(FATAL) << "Stack overflow: " << PrettyMethod(method);
 }
@@ -220,6 +194,38 @@
            (char*)&table[4], size_in_bytes);
 }
 
+/*
+ * TODO: placeholder for a method that can be called by the
+ * invoke-interface trampoline to unwind and handle exception.  The
+ * trampoline will arrange it so that the caller appears to be the
+ * callsite of the failed invoke-interface.  See comments in
+ * runtime_support.S
+ */
+extern "C" void artFailedInvokeInterface() {
+    UNIMPLEMENTED(FATAL) << "Unimplemented exception throw";
+}
+
+// See comments in runtime_support.S
+extern "C" uint64_t artFindInterfaceMethodInCache(uint32_t method_idx,
+     Object* this_object , Method* caller_method)
+{
+  if (this_object == NULL) {
+    ThrowNullPointerFromCode();
+  }
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Method* interface_method = class_linker->ResolveMethod(method_idx, caller_method, false);
+  if (interface_method == NULL) {
+    UNIMPLEMENTED(FATAL) << "Could not resolve interface method. Throw error and unwind";
+  }
+  Method* method = this_object->GetClass()->FindVirtualMethodForInterface(interface_method);
+  const void* code = method->GetCode();
+
+  uint32_t method_uint = reinterpret_cast<uint32_t>(method);
+  uint64_t code_uint = reinterpret_cast<uint32_t>(code);
+  uint64_t result = ((code_uint << 32) | method_uint);
+  return result;
+}
+
 // TODO: move to more appropriate location
 /*
  * Float/double conversion requires clamping to min and max of integer form.  If
@@ -251,6 +257,11 @@
         return (int64_t)f;
 }
 
+// Return value helper for jobject return types
+static Object* DecodeJObjectInThread(Thread* thread, jobject obj) {
+  return thread->DecodeJObject(obj);
+}
+
 void Thread::InitFunctionPointers() {
 #if defined(__arm__)
   pShlLong = art_shl_long;
@@ -312,6 +323,8 @@
   pThrowRuntimeExceptionFromCode = ThrowRuntimeExceptionFromCode;
   pThrowInternalErrorFromCode = ThrowInternalErrorFromCode;
   pThrowNoSuchMethodFromCode = ThrowNoSuchMethodFromCode;
+  pFindNativeMethod = FindNativeMethod;
+  pDecodeJObjectInThread = DecodeJObjectInThread;
   pDebugMe = DebugMe;
 }
 
diff --git a/src/thread.h b/src/thread.h
index 8539962..66eba85 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -224,6 +224,8 @@
   void (*pThrowRuntimeExceptionFromCode)(int32_t);
   void (*pThrowInternalErrorFromCode)(int32_t);
   void (*pThrowNoSuchMethodFromCode)(int32_t);
+  void* (*pFindNativeMethod)(Thread* thread);
+  Object* (*pDecodeJObjectInThread)(Thread* thread, jobject obj);
 
   class StackVisitor {
    public: