Working dex2oat and oatexec

adb shell dex2oatd --dex-file=/system/framework/core.jar     --image=/system/framework/boot.oat --base=0x50000000 "'--method=Ljava/lang/System;logI(Ljava/lang/String;)V'" "'--method=Ljava/lang/System;log(CLjava/lang/String;Ljava/lang/Throwable;)V'"
adb shell dex2oatd --boot-dex-file=/system/framework/core.jar --boot=/system/framework/boot.oat --dex-file=/system/framework/art-test-dex-HelloWorld.jar --image=/system/framework/art-test-dex-HelloWorld.oat
adb shell oatexecd -Xbootclasspath:/system/framework/core.jar -Xbootimage:/system/framework/boot.oat -classpath /system/framework/art-test-dex-HelloWorld.jar -Ximage:/system/framework/art-test-dex-HelloWorld.oat HelloWorld

09-05 17:58:18.912  2385  2385 I System  : Hello, world!

Change-Id: I53e534068584f0c3a837313e4d517a0e4a7154fc
diff --git a/src/check_jni.cc b/src/check_jni.cc
index dc58565..0486284 100644
--- a/src/check_jni.cc
+++ b/src/check_jni.cc
@@ -447,7 +447,7 @@
     ScopedJniThreadState ts(mEnv);
     const Method* m = DecodeMethod(ts, mid);
     if (*expectedType != m->GetShorty()[0]) {
-      LOG(ERROR) << "JNI ERROR: expected return type '%s' calling " << PrettyMethod(m);
+      LOG(ERROR) << "JNI ERROR: expected return type '" << *expectedType << "' calling " << PrettyMethod(m);
       JniAbort();
     } else if (isStatic && !m->IsStatic()) {
       if (isStatic) {
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 7cd320e..211b87d 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -56,12 +56,14 @@
 };
 
 ClassLinker* ClassLinker::Create(const std::vector<const DexFile*>& boot_class_path,
-    InternTable* intern_table, Space* space) {
+                                 const std::vector<const DexFile*>& class_path,
+                                 InternTable* intern_table, Space* space) {
+  CHECK_NE(boot_class_path.size(), 0U);
   UniquePtr<ClassLinker> class_linker(new ClassLinker(intern_table));
   if (space == NULL) {
-    class_linker->Init(boot_class_path);
+    class_linker->Init(boot_class_path, class_path);
   } else {
-    class_linker->Init(boot_class_path, space);
+    class_linker->Init(boot_class_path, class_path, space);
   }
   // TODO: check for failure during initialization
   return class_linker.release();
@@ -76,7 +78,8 @@
       intern_table_(intern_table) {
 }
 
-void ClassLinker::Init(const std::vector<const DexFile*>& boot_class_path) {
+void ClassLinker::Init(const std::vector<const DexFile*>& boot_class_path,
+                       const std::vector<const DexFile*>& class_path) {
   CHECK(!init_done_);
 
   // java_lang_Class comes first, its needed for AllocClass
@@ -160,13 +163,18 @@
 
   // now that these are registered, we can use AllocClass() and AllocObjectArray
 
-  // setup boot_class_path_ now that we can use AllocObjectArray to
-  // create DexCache instances
+  // setup boot_class_path_ and register class_path now that we can
+  // use AllocObjectArray to create DexCache instances
   for (size_t i = 0; i != boot_class_path.size(); ++i) {
     const DexFile* dex_file = boot_class_path[i];
     CHECK(dex_file != NULL);
     AppendToBootClassPath(*dex_file);
   }
+  for (size_t i = 0; i != class_path.size(); ++i) {
+    const DexFile* dex_file = class_path[i];
+    CHECK(dex_file != NULL);
+    RegisterDexFile(*dex_file);
+  }
 
   // Field and Method are necessary so that FindClass can link members
   Class* java_lang_reflect_Field = AllocClass(java_lang_Class, sizeof(FieldClass));
@@ -366,16 +374,13 @@
   typedef std::tr1::unordered_map<std::string, ClassRoot> Table;
   Table descriptor_to_class_root;
 
-  struct DexCacheHash {
-    size_t operator()(art::DexCache* const& obj) const {
-      return reinterpret_cast<size_t>(&obj);
-    }
-  };
   typedef std::tr1::unordered_set<DexCache*, DexCacheHash> Set;
   Set dex_caches;
 };
 
-void ClassLinker::Init(const std::vector<const DexFile*>& boot_class_path, Space* space) {
+void ClassLinker::Init(const std::vector<const DexFile*>& boot_class_path,
+                       const std::vector<const DexFile*>& class_path,
+                       Space* space) {
   CHECK(!init_done_);
 
   HeapBitmap* heap_bitmap = Heap::GetLiveBits();
@@ -400,6 +405,7 @@
   }
 
   // reinit intern table
+  // TODO: remove interned_array, make all strings in image interned (and remove space argument)
   ObjectArray<Object>* interned_array = space->GetImageHeader().GetInternedArray();
   for (int32_t i = 0; i < interned_array->GetLength(); i++) {
     String* string = interned_array->Get(i)->AsString();
@@ -418,14 +424,27 @@
     std::string location = dex_cache->GetLocation()->ToModifiedUtf8();
     location_to_dex_cache[location] = dex_cache;
   }
+  CHECK_EQ(boot_class_path.size() + class_path.size(),
+           location_to_dex_cache.size());
 
   // reinit boot_class_path with DexFile arguments and found DexCaches
   for (size_t i = 0; i != boot_class_path.size(); ++i) {
     const DexFile* dex_file = boot_class_path[i];
     CHECK(dex_file != NULL);
     DexCache* dex_cache = location_to_dex_cache[dex_file->GetLocation()];
+    CHECK(dex_cache != NULL) << dex_file->GetLocation();
     AppendToBootClassPath(*dex_file, dex_cache);
   }
+
+  // register class_path with DexFile arguments and found DexCaches
+  for (size_t i = 0; i != class_path.size(); ++i) {
+    const DexFile* dex_file = class_path[i];
+    CHECK(dex_file != NULL);
+    DexCache* dex_cache = location_to_dex_cache[dex_file->GetLocation()];
+    CHECK(dex_cache != NULL) << dex_file->GetLocation();
+    RegisterDexFile(*dex_file, dex_cache);
+  }
+
   String::SetClass(GetClassRoot(kJavaLangString));
   Field::SetClass(GetClassRoot(kJavaLangReflectField));
   Method::SetClass(GetClassRoot(kJavaLangReflectMethod));
@@ -452,10 +471,15 @@
     return;
   }
   Class* klass = obj->AsClass();
-  CHECK(klass->GetClassLoader() == NULL);
+  // TODO: restore ClassLoader's list of DexFiles after image load
+  // CHECK(klass->GetClassLoader() == NULL);
+  const ClassLoader* class_loader = klass->GetClassLoader();
+  if (class_loader != NULL) {
+    // TODO: replace this hack with something based on command line arguments
+    Thread::Current()->SetClassLoaderOverride(class_loader);
+  }
 
   std::string descriptor = klass->GetDescriptor()->ToModifiedUtf8();
-
   // restore class to ClassLinker::classes_ table
   state->class_linker->InsertClass(descriptor, klass);
 
@@ -900,7 +924,7 @@
 }
 
 void ClassLinker::AppendToBootClassPath(const DexFile& dex_file, DexCache* dex_cache) {
-  CHECK(dex_cache != NULL);
+  CHECK(dex_cache != NULL) << dex_file.GetLocation();
   boot_class_path_.push_back(&dex_file);
   RegisterDexFile(dex_file, dex_cache);
 }
@@ -910,7 +934,8 @@
 }
 
 void ClassLinker::RegisterDexFile(const DexFile& dex_file, DexCache* dex_cache) {
-  CHECK(dex_cache != NULL);
+  CHECK(dex_cache != NULL) << dex_file.GetLocation();
+  CHECK(dex_cache->GetLocation()->Equals(dex_file.GetLocation()));
   dex_files_.push_back(&dex_file);
   dex_caches_.push_back(dex_cache);
 }
@@ -921,7 +946,7 @@
         return *dex_files_[i];
     }
   }
-  CHECK(false) << "Could not find DexFile";
+  CHECK(false) << "Failed to find DexFile for DexCache " << dex_cache->GetLocation()->ToModifiedUtf8();
   return *dex_files_[-1];
 }
 
@@ -931,7 +956,7 @@
         return dex_caches_[i];
     }
   }
-  CHECK(false) << "Could not find DexCache";
+  CHECK(false) << "Failed to find DexCache for DexFile " << dex_file.GetLocation();
   return NULL;
 }
 
@@ -1544,7 +1569,7 @@
     return false;
   }
   if (!klass->CanAccess(super)) {
-    LG << "Superclass " << super->GetDescriptor()->ToModifiedUtf8() << " is  inaccessible";  // TODO: IllegalAccessError
+    LG << "Superclass " << super->GetDescriptor()->ToModifiedUtf8() << " is inaccessible";  // TODO: IllegalAccessError
     return false;
   }
 #ifndef NDEBUG
diff --git a/src/class_linker.h b/src/class_linker.h
index f63ab3c..4f4ffc7 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -26,7 +26,8 @@
  public:
   // Initializes the class linker using DexFile and an optional boot Space.
   static ClassLinker* Create(const std::vector<const DexFile*>& boot_class_path,
-      InternTable* intern_table, Space* boot_space);
+                             const std::vector<const DexFile*>& class_path,
+                             InternTable* intern_table, Space* boot_space);
 
   ~ClassLinker();
 
@@ -149,10 +150,13 @@
   ClassLinker(InternTable*);
 
   // Initialize class linker from DexFile instances.
-  void Init(const std::vector<const DexFile*>& boot_class_path_);
+  void Init(const std::vector<const DexFile*>& boot_class_path_,
+            const std::vector<const DexFile*>& class_path_);
 
   // Initialize class linker from pre-initialized space.
-  void Init(const std::vector<const DexFile*>& boot_class_path_, Space* space);
+  void Init(const std::vector<const DexFile*>& boot_class_path_,
+            const std::vector<const DexFile*>& class_path_,
+            Space* space);
   static void InitCallback(Object* obj, void *arg);
   struct InitCallbackState;
 
diff --git a/src/class_loader.cc b/src/class_loader.cc
index 9769136..8f8e829 100644
--- a/src/class_loader.cc
+++ b/src/class_loader.cc
@@ -18,6 +18,7 @@
 Class* PathClassLoader::dalvik_system_PathClassLoader_ = NULL;
 
 const PathClassLoader* PathClassLoader::Alloc(std::vector<const DexFile*> dex_files) {
+  DCHECK(dalvik_system_PathClassLoader_ != NULL);
   PathClassLoader* p = down_cast<PathClassLoader*>(dalvik_system_PathClassLoader_->AllocObject());
   p->SetClassPath(dex_files);
   return p;
diff --git a/src/common_test.h b/src/common_test.h
index 7916640..98d9127 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -85,7 +85,8 @@
       setenv("ANDROID_ROOT", root.c_str(), 1);
     }
 
-    android_data_ = (is_host_ ? "/tmp/art-data-XXXXXX" : "/mnt/sdcard/art-data-XXXXXX");
+    // On target, Cannot use /mnt/sdcard because it is mounted noexec, so use subdir of art-cache
+    android_data_ = (is_host_ ? "/tmp/art-data-XXXXXX" : "/data/art-cache/art-data-XXXXXX");
     if (mkdtemp(&android_data_[0]) == NULL) {
       PLOG(FATAL) << "mkdtemp(\"" << &android_data_[0] << "\") failed";
     }
@@ -99,8 +100,12 @@
 
     boot_class_path_.push_back(java_lang_dex_file_.get());
 
-    runtime_.reset(Runtime::Create(boot_class_path_));
+    Runtime::Options options;
+    options.push_back(std::make_pair("bootclasspath", &boot_class_path_));
+    options.push_back(std::make_pair("-Xcheck:jni", reinterpret_cast<void*>(NULL)));
+    runtime_.reset(Runtime::Create(options, false));
     ASSERT_TRUE(runtime_.get() != NULL);
+    runtime_->Start();
     class_linker_ = runtime_->GetClassLinker();
 
     Heap::VerifyHeap();  // Check for heap corruption before the test
@@ -167,7 +172,7 @@
         return i;
       }
     }
-    CHECK(false) << "Could not find type index for " << descriptor;
+    CHECK(false) << "Failed to find type index for " << descriptor;
     return 0;
   }
 
@@ -181,11 +186,11 @@
         return i;
       }
     }
-    CHECK(false) << "Could not find field index for " << class_descriptor << " " << field_name;
+    CHECK(false) << "Failed to find field index for " << class_descriptor << " " << field_name;
     return 0;
   }
 
-  const PathClassLoader* AllocPathClassLoader(const DexFile* dex_file) {
+  const ClassLoader* AllocPathClassLoader(const DexFile* dex_file) {
     CHECK(dex_file != NULL);
     class_linker_->RegisterDexFile(*dex_file);
     std::vector<const DexFile*> dex_files;
@@ -204,7 +209,7 @@
     filename += name;
     filename += ".jar";
     const DexFile* dex_file = DexFile::OpenZip(filename);
-    CHECK(dex_file != NULL) << "Could not open " << filename;
+    CHECK(dex_file != NULL) << "Failed to open " << filename;
     return dex_file;
   }
 
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
new file mode 100644
index 0000000..4d071a7
--- /dev/null
+++ b/src/dex2oat.cc
@@ -0,0 +1,224 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+
+#include "class_linker.h"
+#include "class_loader.h"
+#include "compiler.h"
+#include "image_writer.h"
+#include "runtime.h"
+#include "stringpiece.h"
+
+namespace art {
+
+static void usage() {
+  fprintf(stderr,
+          "Usage: dex2oat [options]...\n"
+          "\n");
+  fprintf(stderr,
+          "  --dex-file=<dex-file>: specifies a .dex files to compile. At least one .dex\n"
+          "      but more than one may be included. \n"
+          "      Example: --dex-file=/system/framework/core.jar\n"
+          "\n");
+  fprintf(stderr,
+          "  --image=<file>: specifies the required output image filename.\n"
+          "      Example: --image=/system/framework/boot.oat\n"
+          "\n");
+  fprintf(stderr,
+          "  --base=<hex-address>: specifies the base address when creating a boot image.\n"
+          "      Example: --base=0x50000000\n"
+          "\n");
+  fprintf(stderr,
+          "  --boot=<oat-file>: provide the oat file for the boot class path.\n"
+          "      Example: --boot=/system/framework/boot.oat\n"
+          "\n");
+  // TODO: remove this by making boot image contain boot DexFile information?
+  fprintf(stderr,
+          "  --boot-dex-file=<dex-file>: specifies a .dex file that is part of the boot\n"
+          "       image specified with --boot. \n"
+          "      Example: --boot-dex-file=/system/framework/core.jar\n"
+          "\n");
+  fprintf(stderr,
+          "  --method may be used to limit compilation to a subset of methods.\n"
+          "      Example: --method=Ljava/lang/Object;<init>()V\n"
+          "\n");
+  exit(EXIT_FAILURE);
+}
+
+static void OpenDexFiles(std::vector<const char*>& dex_filenames,
+                         std::vector<const DexFile*>& dex_files) {
+  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);
+    if (dex_file == NULL) {
+      fprintf(stderr, "could not open .dex from file %s\n", dex_filename);
+      exit(EXIT_FAILURE);
+    }
+    dex_files.push_back(dex_file);
+  }
+}
+
+int dex2oat(int argc, char** argv) {
+  // Skip over argv[0].
+  argv++;
+  argc--;
+
+  if (argc == 0) {
+    fprintf(stderr, "no arguments specified\n");
+    usage();
+  }
+
+  std::vector<const char*> dex_filenames;
+  std::vector<const char*> method_names;
+  const char* image_filename = NULL;
+  std::string boot_image_option;
+  std::vector<const char*> boot_dex_filenames;
+  uintptr_t image_base = 0;
+  for (int i = 0; i < argc; i++) {
+    const StringPiece option(argv[i]);
+    if (option.starts_with("--dex-file=")) {
+      dex_filenames.push_back(option.substr(strlen("--dex-file=")).data());
+    } else if (option.starts_with("--method=")) {
+      method_names.push_back(option.substr(strlen("--method=")).data());
+    } else if (option.starts_with("--image=")) {
+      image_filename = option.substr(strlen("--image=")).data();
+    } else if (option.starts_with("--base=")) {
+      const char* image_base_str = option.substr(strlen("--base=")).data();
+      char* end;
+      image_base = strtoul(image_base_str, &end, 16);
+      if (end == image_base_str || *end != '\0') {
+        fprintf(stderr, "could not parse hexadecimal value for option %s\n", option.data());
+        usage();
+      }
+    } else if (option.starts_with("--boot=")) {
+      const char* boot_image_filename = option.substr(strlen("--boot=")).data();
+      boot_image_option.clear();
+      boot_image_option += "-Xbootimage:";
+      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 {
+      fprintf(stderr, "unknown argument %s\n", option.data());
+      usage();
+    }
+  }
+
+  if (image_filename == NULL) {
+   fprintf(stderr, "--image file name not specified\n");
+   return EXIT_FAILURE;
+  }
+
+  if (boot_image_option.empty()) {
+    if (image_base == 0) {
+      fprintf(stderr, "non-zero --base not specified\n");
+      return EXIT_FAILURE;
+    }
+  } else {
+    if (boot_dex_filenames.empty()) {
+      fprintf(stderr, "no --boot-dex-file specified with --boot\n");
+      return EXIT_FAILURE;
+    }
+  }
+
+  std::vector<const DexFile*> dex_files;
+  OpenDexFiles(dex_filenames, dex_files);
+
+  std::vector<const DexFile*> boot_dex_files;
+  OpenDexFiles(boot_dex_filenames, boot_dex_files);
+
+  Runtime::Options options;
+  if (boot_image_option.empty()) {
+    options.push_back(std::make_pair("bootclasspath", &dex_files));
+  } else {
+    options.push_back(std::make_pair("bootclasspath", &boot_dex_files));
+    options.push_back(std::make_pair(boot_image_option.c_str(), reinterpret_cast<void*>(NULL)));
+  }
+  UniquePtr<Runtime> runtime(Runtime::Create(options, false));
+  if (runtime.get() == NULL) {
+    fprintf(stderr, "could not create runtime\n");
+    return EXIT_FAILURE;
+  }
+  ClassLinker* class_linker = runtime->GetClassLinker();
+
+  // If we have an existing boot image, position new space after it
+  if (!boot_image_option.empty()) {
+    Space* boot_space = Heap::GetBootSpace();
+    CHECK(boot_space != NULL);
+    image_base = RoundUp(reinterpret_cast<uintptr_t>(boot_space->GetLimit()), kPageSize);
+  }
+
+  // ClassLoader creation needs to come after Runtime::Create
+  const ClassLoader* class_loader;
+  if (boot_image_option.empty()) {
+    class_loader = NULL;
+  } else {
+    for (size_t i = 0; i < dex_files.size(); i++) {
+      class_linker->RegisterDexFile(*dex_files[i]);
+    }
+    class_loader = PathClassLoader::Alloc(dex_files);
+  }
+
+  Compiler compiler;
+  if (method_names.empty()) {
+    compiler.CompileAll(class_loader);
+  } else {
+    for (size_t i = 0; i < method_names.size(); i++) {
+      // names are actually class_descriptor + name + signature.
+      // example: Ljava/lang/Object;<init>()V
+      StringPiece method_name = method_names[i];
+      size_t end_of_class_descriptor = method_name.find(';');
+      if (end_of_class_descriptor == method_name.npos) {
+        fprintf(stderr, "could not find class descriptor in method %s\n", method_name.data());
+        return EXIT_FAILURE;
+      }
+      end_of_class_descriptor++;  // want to include ;
+      std::string class_descriptor = method_name.substr(0, end_of_class_descriptor).ToString();
+      size_t end_of_name = method_name.find('(', end_of_class_descriptor);
+      if (end_of_name == method_name.npos) {
+        fprintf(stderr, "could not find start of method signature in method %s\n", method_name.data());
+        return EXIT_FAILURE;
+      }
+      std::string name = method_name.substr(end_of_class_descriptor,
+                                            end_of_name - end_of_class_descriptor).ToString();
+      std::string signature = method_name.substr(end_of_name).ToString();
+
+      Class* klass = class_linker->FindClass(class_descriptor, class_loader);
+      if (klass == NULL) {
+        fprintf(stderr, "could not find class for descriptor %s in method %s\n",
+                class_descriptor.c_str(), method_name.data());
+        return EXIT_FAILURE;
+      }
+      Method* method = klass->FindDirectMethod(name, signature);
+      if (method == NULL) {
+          method = klass->FindVirtualMethod(name, signature);
+      }
+      if (method == NULL) {
+        fprintf(stderr, "could not find method %s with signature %s in class %s for method argument %s\n",
+                name.c_str(),
+                signature.c_str(),
+                class_descriptor.c_str(),
+                method_name.data());
+        return EXIT_FAILURE;
+      }
+      compiler.CompileOne(method);
+    }
+  }
+
+  ImageWriter writer;
+  if (!writer.Write(image_filename, image_base)) {
+    fprintf(stderr, "could not write image %s\n", image_filename);
+    return EXIT_FAILURE;
+  }
+
+  return EXIT_SUCCESS;
+}
+
+} // namespace art
+
+int main(int argc, char** argv) {
+  return art::dex2oat(argc, argv);
+}
diff --git a/src/dex_cache.h b/src/dex_cache.h
index 804f680..22dae24 100644
--- a/src/dex_cache.h
+++ b/src/dex_cache.h
@@ -12,31 +12,32 @@
 
 class Class;
 class Field;
+class ImageWriter;
 class Method;
 class String;
 union JValue;
 
 class CodeAndDirectMethods : public IntArray {
  public:
-  Method* GetResolvedCode(uint32_t method_idx) const {
-    return reinterpret_cast<Method*>(Get(method_idx * kMax + kCode));
+  void* GetResolvedCode(uint32_t method_idx) const {
+    return reinterpret_cast<byte*>(Get(CodeIndex(method_idx)));
   }
-  void* GetResolvedMethod(uint32_t method_idx) const {
-    return reinterpret_cast<byte*>(Get(method_idx * kMax + kMethod));
+  Method* GetResolvedMethod(uint32_t method_idx) const {
+    return reinterpret_cast<Method*>(Get(MethodIndex(method_idx)));
   }
 
   void SetResolvedDirectMethodTrampoline(uint32_t method_idx) {
     UNIMPLEMENTED(WARNING) << "need to install a trampoline to resolve the method_idx at runtime";
-    Set(method_idx * kMax + kCode,   0xffffffff);
-    Set(method_idx * kMax + kMethod, method_idx);
+    Set(CodeIndex(method_idx),   0xffffffff);
+    Set(MethodIndex(method_idx), method_idx);
   }
 
   void SetResolvedDirectMethod(uint32_t method_idx, Method* method) {
     CHECK(method != NULL);
     CHECK(method->IsDirect());
     // CHECK(method->GetCode() != NULL);  // TODO enable when all code is compiling
-    Set(method_idx * kMax + kCode,   reinterpret_cast<int32_t>(method->GetCode()));
-    Set(method_idx * kMax + kMethod, reinterpret_cast<int32_t>(method));
+    Set(CodeIndex(method_idx),   reinterpret_cast<int32_t>(method->GetCode()));
+    Set(MethodIndex(method_idx), reinterpret_cast<int32_t>(method));
   }
 
   static size_t LengthAsArray(size_t elements) {
@@ -45,14 +46,12 @@
 
   // Offset of resolved method entry from start of code_and_direct_methods_
   static size_t MethodOffsetInBytes(uint32_t method_idx) {
-    return ((method_idx * kMax + kMethod) * sizeof(ElementType) +
-             Array::DataOffset().Int32Value());
+    return (MethodIndex(method_idx) * sizeof(ElementType) + Array::DataOffset().Int32Value());
   }
 
   // Offset of resolved method's code_ from start of code_and_direct_methods_
   static size_t CodeOffsetInBytes(uint32_t method_idx) {
-    return ((method_idx * kMax + kCode) * sizeof(ElementType) +
-             Array::DataOffset().Int32Value());
+    return (CodeIndex(method_idx) * sizeof(ElementType) + Array::DataOffset().Int32Value());
   }
 
  size_t NumCodeAndDirectMethods() const {
@@ -66,6 +65,17 @@
     kMax    = 2,
   };
 
+  static size_t CodeIndex(uint32_t method_idx) {
+    return method_idx * kMax + kCode;
+  }
+  static size_t MethodIndex(uint32_t method_idx) {
+    return method_idx * kMax + kMethod;
+  }
+
+  // grant friend status to ImageWriter fixup code that needs to know internal layout
+  friend class ImageWriter;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(CodeAndDirectMethods);
 };
 
 class DexCache : public ObjectArray<Object> {
@@ -197,6 +207,12 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(DexCache);
 };
 
+struct DexCacheHash {
+  size_t operator()(art::DexCache* const& obj) const {
+    return reinterpret_cast<size_t>(&obj);
+  }
+};
+
 }  // namespace art
 
 #endif  // ART_SRC_DEX_CACHE_H_
diff --git a/src/dex_file.cc b/src/dex_file.cc
index ad0b3b9..b982291 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -42,6 +42,19 @@
                         reinterpret_cast<const DexFile::ClassDef*>(NULL));
 }
 
+const DexFile* DexFile::Open(const std::string& filename) {
+  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);
+  } else {
+    return DexFile::OpenFile(filename);
+  }
+}
+
 DexFile::Closer::~Closer() {}
 
 DexFile::MmapCloser::MmapCloser(void* addr, size_t length) : addr_(addr), length_(length) {
@@ -89,7 +102,7 @@
    static LockedFd* CreateAndLock(std::string& name, mode_t mode) {
     int fd = open(name.c_str(), O_CREAT | O_RDWR, mode);
     if (fd == -1) {
-      PLOG(ERROR) << "Can't open file '" << name << "'";
+      PLOG(ERROR) << "Failed to open file '" << name << "'";
       return NULL;
     }
     fchmod(fd, mode);
@@ -101,7 +114,7 @@
         result = flock(fd, LOCK_EX);
     }
     if (result == -1 ) {
-      PLOG(ERROR) << "Can't lock file '" << name << "'";
+      PLOG(ERROR) << "Failed to lock file '" << name << "'";
       close(fd);
       return NULL;
     }
@@ -149,6 +162,7 @@
   size_t found = adjacent_dex_filename.find_last_of(".");
   if (found == std::string::npos) {
     LOG(WARNING) << "No . in filename" << filename;
+    return NULL;
   }
   adjacent_dex_filename.replace(adjacent_dex_filename.begin() + found,
                                 adjacent_dex_filename.end(),
@@ -168,7 +182,7 @@
   char resolved[PATH_MAX];
   char* absolute_path = realpath(filename.c_str(), resolved);
   if (absolute_path == NULL) {
-      LOG(WARNING) << "Could not create absolute path for " << filename
+      LOG(WARNING) << "Failed to create absolute path for " << filename
                    << " when looking for classes.dex";
       return NULL;
   }
@@ -180,7 +194,7 @@
 
   const char* data_root = getenv("ANDROID_DATA");
   if (data_root == NULL) {
-      data_root = "/data";
+    data_root = "/data";
   }
 
   std::string cache_path_tmp = StringPrintf("%s/art-cache/%s", data_root, cache_file.c_str());
@@ -188,12 +202,12 @@
 
   UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(filename));
   if (zip_archive.get() == NULL) {
-    LOG(WARNING) << "Could not open " << filename << " when looking for classes.dex";
+    LOG(WARNING) << "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) << "Could not find classes.dex within " << filename;
+    LOG(WARNING) << "Failed to find classes.dex within " << filename;
     return NULL;
   }
 
@@ -235,7 +249,7 @@
     struct stat fd_stat;
     int fd_stat_result = fstat(fd->GetFd(), &fd_stat);
     if (fd_stat_result == -1) {
-      PLOG(ERROR) << "Can't stat open file '" << cache_path_tmp << "'";
+      PLOG(ERROR) << "Failed to stat open file '" << cache_path_tmp << "'";
       return NULL;
     }
     struct stat file_stat;
@@ -287,7 +301,7 @@
     }
     int rename_result = rename(cache_path_tmp.c_str(), cache_path.c_str());
     if (rename_result == -1) {
-      PLOG(ERROR) << "Can't install dex cache file '" << cache_path << "'"
+      PLOG(ERROR) << "Failed to install dex cache file '" << cache_path << "'"
                   << " from '" << cache_path_tmp << "'";
       unlink(cache_path.c_str());
     }
diff --git a/src/dex_file.h b/src/dex_file.h
index 73e6954..98e79a7 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -316,6 +316,9 @@
   static ClassPathEntry FindInClassPath(const StringPiece& descriptor,
                                         const ClassPath& class_path);
 
+  // Opens .dex file, guessing the format based on file extension
+  static const DexFile* Open(const std::string& filename);
+
   // Opens a .dex file from the file system.
   static const DexFile* OpenFile(const std::string& filename);
 
diff --git a/src/exception_test.cc b/src/exception_test.cc
index 439e7ed..5283697 100644
--- a/src/exception_test.cc
+++ b/src/exception_test.cc
@@ -68,7 +68,7 @@
 
     dex_.reset(OpenDexFileBase64(kMyClassExceptionHandleDex, "kMyClassExceptionHandleDex"));
     ASSERT_TRUE(dex_.get() != NULL);
-    const PathClassLoader* class_loader = AllocPathClassLoader(dex_.get());
+    const ClassLoader* class_loader = AllocPathClassLoader(dex_.get());
     ASSERT_TRUE(class_loader != NULL);
     my_klass_ = class_linker_->FindClass("Ljava/lang/MyClass;", class_loader);
     ASSERT_TRUE(my_klass_ != NULL);
diff --git a/src/heap.cc b/src/heap.cc
index be3d154..86d19a8 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -37,7 +37,9 @@
 MemberOffset Heap::reference_pendingNext_offset_ = MemberOffset(0);
 MemberOffset Heap::finalizer_reference_zombie_offset_ = MemberOffset(0);
 
-bool Heap::Init(size_t initial_size, size_t maximum_size, const char* boot_image_file_name) {
+bool Heap::Init(size_t initial_size, size_t maximum_size,
+                const char* boot_image_file_name,
+                std::vector<const char*>& image_file_names) {
   Space* boot_space;
   byte* requested_base;
   if (boot_image_file_name == NULL) {
@@ -46,14 +48,28 @@
   } else {
     boot_space = Space::Create(boot_image_file_name);
     if (boot_space == NULL) {
+      LOG(WARNING) << "Failed to create space from " << boot_image_file_name;
       return false;
     }
     spaces_.push_back(boot_space);
     requested_base = boot_space->GetBase() + RoundUp(boot_space->Size(), kPageSize);
   }
 
+  std::vector<Space*> image_spaces;
+  for (size_t i = 0; i < image_file_names.size(); i++) {
+    Space* space = Space::Create(image_file_names[i]);
+    if (space == NULL) {
+      LOG(WARNING) << "Failed to create space from " << image_file_names[i];
+      return false;
+    }
+    image_spaces.push_back(space);
+    spaces_.push_back(space);
+    requested_base = space->GetBase() + RoundUp(space->Size(), kPageSize);
+  }
+
   Space* space = Space::Create(initial_size, maximum_size, requested_base);
   if (space == NULL) {
+    LOG(WARNING) << "Failed to create alloc space";
     return false;
   }
 
@@ -68,12 +84,14 @@
   // Allocate the initial live bitmap.
   UniquePtr<HeapBitmap> live_bitmap(HeapBitmap::Create(base, num_bytes));
   if (live_bitmap.get() == NULL) {
+    LOG(WARNING) << "Failed to create live bitmap";
     return false;
   }
 
   // Allocate the initial mark bitmap.
   UniquePtr<HeapBitmap> mark_bitmap(HeapBitmap::Create(base, num_bytes));
   if (mark_bitmap.get() == NULL) {
+    LOG(WARNING) << "Failed to create mark bitmap";
     return false;
   }
 
@@ -93,6 +111,9 @@
     boot_space_ = boot_space;
     RecordImageAllocations(boot_space);
   }
+  for (size_t i = 0; i < image_spaces.size(); i++) {
+    RecordImageAllocations(image_spaces[i]);
+  }
 
   return true;
 }
diff --git a/src/heap.h b/src/heap.h
index 30133b7..4922207 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -27,9 +27,12 @@
 
   typedef void (RootVisitor)(const Object* root, void* arg);
 
-  // Create a heap with the requested sizes. optional boot image may
+  // Create a heap with the requested sizes. The optional boot image may
   // be NULL, otherwise it is an image filename created by ImageWriter.
-  static bool Init(size_t starting_size, size_t maximum_size, const char* boot_image_file_name);
+  // image_file_names specifies application images to load.
+  static bool Init(size_t starting_size, size_t maximum_size,
+                   const char* boot_image_file_name,
+                   std::vector<const char*>& image_file_names);
 
   static void Destroy();
 
@@ -149,6 +152,7 @@
   static std::vector<Space*> spaces_;
 
   // Space loaded from an image
+  // TODO: remove after intern_addr is removed
   static Space* boot_space_;
 
   // default Space for allocations
diff --git a/src/image.cc b/src/image.cc
index a41bf5ba..5bf7b10 100644
--- a/src/image.cc
+++ b/src/image.cc
@@ -4,7 +4,7 @@
 
 namespace art {
 
-const byte ImageHeader::kImageMagic[] = { 'i', 'm', 'g', '\n' };
+const byte ImageHeader::kImageMagic[] = { 'o', 'a', 't', '\n' };
 const byte ImageHeader::kImageVersion[] = { '0', '0', '1', '\0' };
 
 }  // namespace art
diff --git a/src/image.h b/src/image.h
index 94824d7..00f72ba 100644
--- a/src/image.h
+++ b/src/image.h
@@ -50,6 +50,7 @@
   uint32_t base_addr_;
 
   // absolute address of an Object[] of Strings to InternTable::RegisterStrong.
+  // TODO: remove after interning all Strings in image
   uint32_t intern_addr_;
 };
 
diff --git a/src/image_test.cc b/src/image_test.cc
index db09021..f07588a 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -25,15 +25,10 @@
   }
   // TODO: Heap::CollectGarbage before writing
 
-  const std::vector<Space*>& spaces = Heap::GetSpaces();
-  // can't currently deal with writing a space that might have pointers between spaces
-  ASSERT_EQ(1U, spaces.size());
-  Space* space = spaces[0];
-
   ImageWriter writer;
   ScratchFile tmp;
-  const int image_base = 0x50000000;
-  bool success = writer.Write(space, tmp.GetFilename(), reinterpret_cast<byte*>(image_base));
+  const uintptr_t image_base = 0x50000000;
+  bool success = writer.Write(tmp.GetFilename(), image_base);
   ASSERT_TRUE(success);
 
   {
@@ -42,6 +37,10 @@
     ImageHeader image_header;
     file->ReadFully(&image_header, sizeof(image_header));
     ASSERT_TRUE(image_header.IsValid());
+
+    ASSERT_EQ(1U, Heap::GetSpaces().size());
+    Space* space = Heap::GetSpaces()[0];
+    ASSERT_TRUE(space != NULL);
     ASSERT_GE(sizeof(image_header) + space->Size(), static_cast<size_t>(file->Length()));
   }
 
@@ -69,7 +68,7 @@
   class_linker_ = runtime_->GetClassLinker();
 
   ASSERT_EQ(2U, Heap::GetSpaces().size());
-  Space* boot_space = Heap::GetSpaces()[0];
+  Space* boot_space = Heap::GetBootSpace();
   ASSERT_TRUE(boot_space != NULL);
 
   // enable to display maps to debug boot_base and boot_limit checking problems below
@@ -87,6 +86,7 @@
     EXPECT_TRUE(klass != NULL) << descriptor;
     EXPECT_LT(boot_base, reinterpret_cast<byte*>(klass)) << descriptor;
     EXPECT_LT(reinterpret_cast<byte*>(klass), boot_limit) << descriptor;
+    EXPECT_TRUE(klass->GetMonitor() == NULL);  // address should have been removed from monitor
   }
 }
 
diff --git a/src/image_writer.cc b/src/image_writer.cc
index e0e77c7..a49b261 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -23,9 +23,16 @@
 
 namespace art {
 
-bool ImageWriter::Write(Space* space, const char* filename, byte* image_base) {
-  image_base_ = image_base;
-  if (!Init(space)) {
+bool ImageWriter::Write(const char* filename, uintptr_t image_base) {
+  CHECK_NE(image_base, 0U);
+  image_base_ = reinterpret_cast<byte*>(image_base);
+
+  const std::vector<Space*>& spaces = Heap::GetSpaces();
+  // currently just write the last space, assuming it is the space that was being used for allocation
+  CHECK_GE(spaces.size(), 1U);
+  source_space_ = spaces[spaces.size()-1];
+
+  if (!Init()) {
     return false;
   }
   CalculateNewObjectOffsets();
@@ -38,8 +45,8 @@
   return file->WriteFully(image_->GetAddress(), image_top_);
 }
 
-bool ImageWriter::Init(Space* space) {
-  size_t size = space->Size();
+bool ImageWriter::Init() {
+  size_t size = source_space_->Size();
   int prot = PROT_READ | PROT_WRITE;
   size_t length = RoundUp(size, kPageSize);
   image_.reset(MemMap::Map(length, prot));
@@ -88,9 +95,23 @@
   DCHECK(obj != NULL);
   DCHECK(arg != NULL);
   ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg);
+  if (!image_writer->InSourceSpace(obj)) {
+    return;
+  }
   image_writer->SetImageOffset(obj, image_writer->image_top_);
   image_writer->image_top_ += RoundUp(obj->SizeOf(), 8);  // 64-bit alignment
   DCHECK_LT(image_writer->image_top_, image_writer->image_->GetLength());
+
+  // sniff out the DexCaches on this pass for use on the next pass
+  if (obj->IsClass()) {
+    Class* klass = obj->AsClass();
+    DexCache* dex_cache = klass->GetDexCache();
+    if (dex_cache != NULL) {
+      image_writer->dex_caches_.insert(dex_cache);
+    } else {
+      DCHECK(klass->IsArrayClass() || klass->IsPrimitive());
+    }
+  }
 }
 
 void ImageWriter::CalculateNewObjectOffsets() {
@@ -104,7 +125,7 @@
   // know where interned_array is going to end up
   image_top_ += RoundUp(sizeof(ImageHeader), 8); // 64-bit-alignment
 
-  heap_bitmap->Walk(CalculateNewObjectOffsetsCallback, this);
+  heap_bitmap->Walk(CalculateNewObjectOffsetsCallback, this);  // TODO: add Space-limited Walk
   DCHECK_LT(image_top_, image_->GetLength());
 
   // return to write header at start of image with future location of interned_array
@@ -120,7 +141,8 @@
   DCHECK(heap_bitmap != NULL);
   // TODO: heap validation can't handle this fix up pass
   Heap::DisableObjectValidation();
-  heap_bitmap->Walk(CopyAndFixupObjectsCallback, this);
+  heap_bitmap->Walk(CopyAndFixupObjectsCallback, this);  // TODO: add Space-limited Walk
+  FixupDexCaches();
 }
 
 void ImageWriter::CopyAndFixupObjectsCallback(Object* object, void *arg) {
@@ -128,7 +150,11 @@
   DCHECK(arg != NULL);
   const Object* obj = object;
   ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg);
+  if (!image_writer->InSourceSpace(object)) {
+    return;
+  }
 
+  // see GetLocalAddress for similar computation
   size_t offset = image_writer->GetImageOffset(obj);
   byte* dst = image_writer->image_->GetAddress() + offset;
   const byte* src = reinterpret_cast<const byte*>(obj);
@@ -136,6 +162,7 @@
   DCHECK_LT(offset + n, image_writer->image_->GetLength());
   memcpy(dst, src, n);
   Object* copy = reinterpret_cast<Object*>(dst);
+  ResetImageOffset(copy);
   image_writer->FixupObject(obj, copy);
 }
 
@@ -178,20 +205,36 @@
   FixupStaticFields(orig, copy);
 }
 
+const void* FixupCode(const ByteArray* copy_code_array, const void* orig_code) {
+  // TODO: change to DCHECK when all code compiling
+  if (copy_code_array == NULL) {
+    return NULL;
+  }
+  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 copy_code;
+}
+
 // TODO: remove this slow path
 void ImageWriter::FixupMethod(const Method* orig, Method* copy) {
   FixupInstanceFields(orig, copy);
   // TODO: remove need for this by adding "signature" to java.lang.reflect.Method
   copy->signature_ = down_cast<String*>(GetImageAddress(orig->signature_));
   DCHECK(copy->signature_ != NULL);
+  // TODO: convert shorty_ to heap allocated storage
   copy->dex_cache_strings_ = down_cast<ObjectArray<String>*>(GetImageAddress(orig->dex_cache_strings_));
   copy->dex_cache_resolved_types_ = down_cast<ObjectArray<Class>*>(GetImageAddress(orig->dex_cache_resolved_types_));
   copy->dex_cache_resolved_methods_ = down_cast<ObjectArray<Method>*>(GetImageAddress(orig->dex_cache_resolved_methods_));
   copy->dex_cache_resolved_fields_ = down_cast<ObjectArray<Field>*>(GetImageAddress(orig->dex_cache_resolved_fields_));
   copy->dex_cache_code_and_direct_methods_ = down_cast<CodeAndDirectMethods*>(GetImageAddress(orig->dex_cache_code_and_direct_methods_));
   copy->dex_cache_initialized_static_storage_ = down_cast<ObjectArray<StaticStorageBase>*>(GetImageAddress(orig->dex_cache_initialized_static_storage_));
-
-  // TODO: convert shorty_ to heap allocated storage
+  copy->code_array_ = down_cast<ByteArray*>(GetImageAddress(orig->code_array_));
+  copy->code_ = FixupCode(copy->code_array_, orig->code_);
+  copy->invoke_stub_array_ = down_cast<ByteArray*>(GetImageAddress(orig->invoke_stub_array_));
+  copy->invoke_stub_ = reinterpret_cast<Method::InvokeStub*>(FixupCode(copy->invoke_stub_array_, reinterpret_cast<void*>(orig->invoke_stub_)));
 }
 
 void ImageWriter::FixupField(const Field* orig, Field* copy) {
@@ -262,4 +305,34 @@
   }
 }
 
+void ImageWriter::FixupDexCaches() {
+  typedef Set::const_iterator It;  // TODO: C++0x auto
+  for (It it = dex_caches_.begin(), end = dex_caches_.end(); it != end; ++it) {
+    DexCache* orig = *it;
+    DexCache* copy = down_cast<DexCache*>(GetLocalAddress(orig));
+    FixupDexCache(orig, copy);
+  }
+}
+
+void ImageWriter::FixupDexCache(const DexCache* orig, DexCache* copy) {
+  CHECK(orig != NULL);
+  CHECK(copy != NULL);
+
+  CodeAndDirectMethods* orig_cadms = orig->GetCodeAndDirectMethods();
+  CodeAndDirectMethods* copy_cadms = down_cast<CodeAndDirectMethods*>(GetLocalAddress(orig_cadms));
+  for (size_t i = 0; i < orig->NumResolvedMethods(); i++) {
+    Method* orig_method = orig->GetResolvedMethod(i);
+    // if it was resolved in the original, resolve it in the copy
+    if (orig_method != NULL
+        && InSourceSpace(orig_method)
+        && orig_method == orig_cadms->GetResolvedMethod(i)) {
+      Method* copy_method = down_cast<Method*>(GetLocalAddress(orig_method));
+      copy_cadms->Set(CodeAndDirectMethods::CodeIndex(i),
+                      reinterpret_cast<int32_t>(copy_method->code_));
+      copy_cadms->Set(CodeAndDirectMethods::MethodIndex(i),
+                      reinterpret_cast<int32_t>(GetImageAddress(orig_method)));
+    }
+  }
+}
+
 }  // namespace art
diff --git a/src/image_writer.h b/src/image_writer.h
index b63effe..3352adb 100644
--- a/src/image_writer.h
+++ b/src/image_writer.h
@@ -8,6 +8,7 @@
 #include <cstddef>
 
 #include "UniquePtr.h"
+#include "dex_cache.h"
 #include "mem_map.h"
 #include "object.h"
 #include "os.h"
@@ -19,33 +20,53 @@
 class ImageWriter {
 
  public:
-  ImageWriter() : image_top_(0), image_base_(NULL) {};
-  bool Write(Space* space, const char* filename, byte* image_base);
+  ImageWriter() : source_space_(NULL), image_top_(0), image_base_(NULL) {};
+  bool Write(const char* filename, uintptr_t image_base);
   ~ImageWriter() {};
 
  private:
 
-  bool Init(Space* space);
+  bool Init();
 
   // we use the lock word to store the offset of the object in the image
-  void SetImageOffset(Object* object, size_t offset) {
+  static void SetImageOffset(Object* object, size_t offset) {
     DCHECK(object != NULL);
     DCHECK(object->GetMonitor() == NULL);  // should be no lock
     DCHECK_NE(0U, offset);
     object->SetMonitor(reinterpret_cast<Monitor*>(offset));
   }
-  size_t GetImageOffset(const Object* object) {
+  static size_t GetImageOffset(const Object* object) {
     DCHECK(object != NULL);
     size_t offset = reinterpret_cast<size_t>(object->GetMonitor());
     DCHECK_NE(0U, offset);
     return offset;
   }
+  static void ResetImageOffset(Object* object) {
+    DCHECK(object != NULL);
+    DCHECK(object->GetMonitor() != NULL);  // should be an offset
+    object->SetMonitor(reinterpret_cast<Monitor*>(0));
+  }
+
+  bool InSourceSpace(const Object* object) {
+    DCHECK(source_space_ != NULL);
+    const byte* o = reinterpret_cast<const byte*>(object);
+    return (o >= source_space_->GetBase() && o < source_space_->GetLimit());
+  }
   Object* GetImageAddress(const Object* object) {
     if (object == NULL) {
       return NULL;
     }
+    // if object outside the relocating source_space_, assume unchanged
+    if (!InSourceSpace(object)) {
+      return const_cast<Object*>(object);
+    }
     return reinterpret_cast<Object*>(image_base_ + GetImageOffset(object));
   }
+  Object* GetLocalAddress(const Object* object) {
+    size_t offset = GetImageOffset(object);
+    byte* dst = image_->GetAddress() + offset;
+    return reinterpret_cast<Object*>(dst);
+  }
 
   void CalculateNewObjectOffsets();
   static void CalculateNewObjectOffsetsCallback(Object* obj, void *arg);
@@ -61,6 +82,12 @@
   void FixupStaticFields(const Class* orig, Class* copy);
   void FixupFields(const Object* orig, Object* copy, uint32_t ref_offsets, bool is_static);
 
+  void FixupDexCaches();
+  void FixupDexCache(const DexCache* orig, DexCache* copy);
+
+  // Space we are writing objects from
+  const Space* source_space_;
+
   // memory mapped for generating the image
   UniquePtr<MemMap> image_;
 
@@ -69,6 +96,10 @@
 
   // Target base address for the output image
   byte* image_base_;
+
+  // DexCaches seen while scanning for fixing up CodeAndDirectMethods
+  typedef std::tr1::unordered_set<DexCache*, DexCacheHash> Set;
+  Set dex_caches_;
 };
 
 }  // namespace art
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index a75a365..bd1ccec 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -207,6 +207,7 @@
   // Pass everything as arguments
   const Method::InvokeStub* stub = method->GetInvokeStub();
   JValue result;
+
   if (method->HasCode() && stub != NULL) {
     (*stub)(method, receiver, self, args, &result);
   } else {
@@ -2566,11 +2567,11 @@
   Runtime* runtime = Runtime::Create(options, ignore_unrecognized);
   if (runtime == NULL) {
     return JNI_ERR;
-  } else {
-    *p_env = Thread::Current()->GetJniEnv();
-    *p_vm = runtime->GetJavaVM();
-    return JNI_OK;
   }
+  runtime->Start();
+  *p_env = Thread::Current()->GetJniEnv();
+  *p_vm = runtime->GetJavaVM();
+  return JNI_OK;
 }
 
 extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize, jsize* vm_count) {
diff --git a/src/main.cc b/src/oatexec.cc
similarity index 98%
rename from src/main.cc
rename to src/oatexec.cc
index 229135c..ca976af 100644
--- a/src/main.cc
+++ b/src/oatexec.cc
@@ -29,6 +29,7 @@
     fprintf(stderr, "Unable to find class Method\n");
     return false;
   }
+#if 0 // TODO: try restoring once iftable_ moved to managed heap
   jmethodID get_modifiers = env->GetMethodID(method.get(),
                                              "getModifiers",
                                              "()I");
@@ -36,6 +37,7 @@
     fprintf(stderr, "Unable to find reflect.Method.getModifiers\n");
     return false;
   }
+#endif
   static const int PUBLIC = 0x0001;   // java.lang.reflect.Modifiers.PUBLIC
 #if 0 // CallIntMethod not yet implemented
   int modifiers = env->CallIntMethod(reflected.get(), get_modifiers);
diff --git a/src/object.cc b/src/object.cc
index 5d99cb6..f92f746 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -1047,7 +1047,8 @@
 }
 
 bool String::Equals(const StringPiece& modified_utf8) const {
-  // TODO: do not assume C-string representation.
+  // TODO: do not assume C-string representation. For now DCHECK.
+  DCHECK_EQ(modified_utf8.data()[modified_utf8.size()], 0);
   return Equals(modified_utf8.data());
 }
 
diff --git a/src/object.h b/src/object.h
index fa09782..9b3bf88 100644
--- a/src/object.h
+++ b/src/object.h
@@ -2494,7 +2494,6 @@
 inline void Field::SetName(String* new_name) {
   SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Field, name_),
                  new_name, false);
-
 }
 
 inline uint32_t Field::GetAccessFlags() const {
@@ -2602,6 +2601,12 @@
 
 // C++ mirror of java.lang.Throwable
 class Throwable : public Object {
+ public:
+  void SetDetailMessage(String* new_detail_message) {
+    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_),
+                   new_detail_message, false);
+  }
+
  private:
   // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
   Throwable* cause_;
diff --git a/src/runtime.cc b/src/runtime.cc
index 2891e56..2c2c5e2 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -149,42 +149,35 @@
   }
 }
 
-const DexFile* Open(const std::string& filename) {
-  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);
-  } else {
-    return DexFile::OpenFile(filename);
-  }
-}
-
-void CreateBootClassPath(const char* boot_class_path_cstr,
-                         std::vector<const DexFile*>& boot_class_path_vector) {
-  CHECK(boot_class_path_cstr != NULL);
+void CreateClassPath(const char* class_path_cstr,
+                     std::vector<const DexFile*>& class_path_vector) {
+  CHECK(class_path_cstr != NULL);
   std::vector<std::string> parsed;
-  Split(boot_class_path_cstr, ':', parsed);
+  Split(class_path_cstr, ':', parsed);
   for (size_t i = 0; i < parsed.size(); ++i) {
-    const DexFile* dex_file = Open(parsed[i]);
+    const DexFile* dex_file = DexFile::Open(parsed[i]);
     if (dex_file != NULL) {
-      boot_class_path_vector.push_back(dex_file);
+      class_path_vector.push_back(dex_file);
     }
   }
 }
 
 Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, bool ignore_unrecognized) {
   UniquePtr<ParsedOptions> parsed(new ParsedOptions());
-  const char* boot_class_path = getenv("BOOTCLASSPATH");
+  const char* boot_class_path = NULL;
+  const char* class_path = NULL;
   parsed->boot_image_ = NULL;
 #ifdef NDEBUG
   // -Xcheck:jni is off by default for regular builds...
   parsed->check_jni_ = false;
 #else
   // ...but on by default in debug builds.
+#if 0 // TODO: disabled for oatexec until the shorty's used by check_jni are managed heap allocated.
+      // Instead we turn on -Xcheck_jni in common_test.
   parsed->check_jni_ = true;
+#else
+  parsed->check_jni_ = false;
+#endif
 #endif
   parsed->heap_initial_size_ = Heap::kInitialSize;
   parsed->heap_maximum_size_ = Heap::kMaximumSize;
@@ -207,12 +200,25 @@
           continue;
         }
         // TODO: usage
-        LOG(FATAL) << "Could not parse " << option;
+        LOG(FATAL) << "Failed to parse " << option;
         return NULL;
       }
       parsed->boot_class_path_ = *v;
+    } else if (option == "-classpath" || option == "-cp") {
+      // TODO: support -Djava.class.path
+      i++;
+      if (i == options.size()) {
+        // TODO: usage
+        LOG(FATAL) << "Missing required class path value for " << option;
+        return NULL;
+      }
+      const StringPiece& value = options[i].first;
+      class_path = value.data();
     } else if (option.starts_with("-Xbootimage:")) {
+      // TODO: remove when intern_addr_ is removed, just use -Ximage:
       parsed->boot_image_ = option.substr(strlen("-Xbootimage:")).data();
+    } else if (option.starts_with("-Ximage:")) {
+      parsed->images_.push_back(option.substr(strlen("-Ximage:")).data());
     } else if (option.starts_with("-Xcheck:jni")) {
       parsed->check_jni_ = true;
     } else if (option.starts_with("-Xms")) {
@@ -222,7 +228,7 @@
           continue;
         }
         // TODO: usage
-        LOG(FATAL) << "Could not parse " << option;
+        LOG(FATAL) << "Failed to parse " << option;
         return NULL;
       }
       parsed->heap_initial_size_ = size;
@@ -233,7 +239,7 @@
           continue;
         }
         // TODO: usage
-        LOG(FATAL) << "Could not parse " << option;
+        LOG(FATAL) << "Failed to parse " << option;
         return NULL;
       }
       parsed->heap_maximum_size_ = size;
@@ -244,7 +250,7 @@
           continue;
         }
         // TODO: usage
-        LOG(FATAL) << "Could not parse " << option;
+        LOG(FATAL) << "Failed to parse " << option;
         return NULL;
       }
       parsed->stack_size_ = size;
@@ -273,19 +279,33 @@
     }
   }
 
-  if (boot_class_path == NULL) {
-    boot_class_path = "";
+  // consider it an error if both bootclasspath and -Xbootclasspath: are supplied.
+  // TODO: remove bootclasspath which is only mostly just used by tests?
+  if (!parsed->boot_class_path_.empty() && boot_class_path != NULL) {
+    // TODO: usage
+    LOG(FATAL) << "bootclasspath and -Xbootclasspath: are mutually exclusive options.";
+    return NULL;
   }
-  if (parsed->boot_class_path_.size() == 0) {
-    CreateBootClassPath(boot_class_path, parsed->boot_class_path_);
+  if (parsed->boot_class_path_.empty()) {
+    if (boot_class_path == NULL) {
+      boot_class_path = getenv("BOOTCLASSPATH");
+      if (boot_class_path == NULL) {
+        boot_class_path = "";
+      }
+    }
+    CreateClassPath(boot_class_path, parsed->boot_class_path_);
   }
-  return parsed.release();
-}
 
-Runtime* Runtime::Create(const std::vector<const DexFile*>& boot_class_path) {
-  Runtime::Options options;
-  options.push_back(std::make_pair("bootclasspath", &boot_class_path));
-  return Runtime::Create(options, false);
+  if (class_path == NULL) {
+    class_path = getenv("CLASSPATH");
+    if (class_path == NULL) {
+      class_path = "";
+    }
+  }
+  CHECK_EQ(parsed->class_path_.size(), 0U);
+  CreateClassPath(class_path, parsed->class_path_);
+
+  return parsed.release();
 }
 
 Runtime* Runtime::Create(const Options& options, bool ignore_unrecognized) {
@@ -299,11 +319,12 @@
     return NULL;
   }
   instance_ = runtime.release();
+  return instance_;
+}
 
+void Runtime::Start() {
   instance_->InitLibraries();
   instance_->signal_catcher_ = new SignalCatcher;
-
-  return instance_;
 }
 
 bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) {
@@ -311,6 +332,7 @@
 
   UniquePtr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized));
   if (options.get() == NULL) {
+    LOG(WARNING) << "Failed to parse options";
     return false;
   }
   vfprintf_ = options->hook_vfprintf_;
@@ -324,7 +346,9 @@
 
   if (!Heap::Init(options->heap_initial_size_,
                   options->heap_maximum_size_,
-                  options->boot_image_)) {
+                  options->boot_image_,
+                  options->images_)) {
+    LOG(WARNING) << "Failed to create heap";
     return false;
   }
 
@@ -333,12 +357,16 @@
   java_vm_ = new JavaVMExt(this, options.get());
 
   if (!Thread::Startup()) {
+    LOG(WARNING) << "Failed to startup threads";
     return false;
   }
 
   thread_list_->Register(Thread::Attach(this));
 
-  class_linker_ = ClassLinker::Create(options->boot_class_path_, intern_table_, Heap::GetBootSpace());
+  class_linker_ = ClassLinker::Create(options->boot_class_path_,
+                                      options->class_path_,
+                                      intern_table_,
+                                      Heap::GetBootSpace());
 
   return true;
 }
diff --git a/src/runtime.h b/src/runtime.h
index 4e00ec1..8a6ffe3 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -40,7 +40,9 @@
     static ParsedOptions* Create(const Options& options, bool ignore_unrecognized);
 
     std::vector<const DexFile*> boot_class_path_;
+    std::vector<const DexFile*> class_path_;
     const char* boot_image_;
+    std::vector<const char*> images_;
     bool check_jni_;
     std::string jni_trace_;
     size_t heap_initial_size_;
@@ -62,7 +64,9 @@
 
   // Creates and initializes a new runtime.
   static Runtime* Create(const Options& options, bool ignore_unrecognized);
-  static Runtime* Create(const std::vector<const DexFile*>& boot_class_path);
+
+  // Starts a runtime, which may cause threads to be started and code to run.
+  void Start();
 
   static Runtime* Current() {
     return instance_;
@@ -115,7 +119,8 @@
  private:
   static void PlatformAbort(const char*, int);
 
-  Runtime() : stack_size_(0), thread_list_(NULL), intern_table_(NULL), class_linker_(NULL) {}
+  Runtime() : stack_size_(0), thread_list_(NULL), intern_table_(NULL), class_linker_(NULL),
+    signal_catcher_(NULL) {}
 
   void BlockSignals();
 
diff --git a/src/runtime_test.cc b/src/runtime_test.cc
index 65792c9..66c181b 100644
--- a/src/runtime_test.cc
+++ b/src/runtime_test.cc
@@ -14,13 +14,22 @@
   void* test_abort = reinterpret_cast<void*>(0xb);
   void* test_exit = reinterpret_cast<void*>(0xc);
   void* null = reinterpret_cast<void*>(NULL);
-  std::vector<const DexFile*> boot_class_path;
-  boot_class_path.push_back(java_lang_dex_file_.get());
+
+  std::string lib_core = GetLibCoreDexFileName();
+
+  std::string boot_class_path;
+  boot_class_path += "-Xbootclasspath:";
+  boot_class_path += lib_core;
 
   Runtime::Options options;
-  options.push_back(std::make_pair("-Xbootclasspath:class_path_foo:class_path_bar", null));
-  options.push_back(std::make_pair("bootclasspath", &boot_class_path));
+  options.push_back(std::make_pair(boot_class_path.c_str(), null));
+  options.push_back(std::make_pair("-classpath", null));
+  options.push_back(std::make_pair(lib_core.c_str(), null));
+  options.push_back(std::make_pair("-cp", null));
+  options.push_back(std::make_pair(lib_core.c_str(), null));
   options.push_back(std::make_pair("-Xbootimage:boot_image", null));
+  options.push_back(std::make_pair("-Ximage:image_1", null));
+  options.push_back(std::make_pair("-Ximage:image_2", null));
   options.push_back(std::make_pair("-Xcheck:jni", null));
   options.push_back(std::make_pair("-Xms2048", null));
   options.push_back(std::make_pair("-Xmx4k", null));
@@ -34,8 +43,12 @@
   UniquePtr<Runtime::ParsedOptions> parsed(Runtime::ParsedOptions::Create(options, false));
   ASSERT_TRUE(parsed.get() != NULL);
 
-  EXPECT_EQ(1U, parsed->boot_class_path_.size());  // bootclasspath overrides -Xbootclasspath
+  EXPECT_EQ(1U, parsed->boot_class_path_.size());
+  EXPECT_EQ(1U, parsed->class_path_.size());
   EXPECT_STREQ("boot_image", parsed->boot_image_);
+  EXPECT_EQ(2U, parsed->images_.size());
+  EXPECT_STREQ("image_1", parsed->images_[0]);
+  EXPECT_STREQ("image_2", parsed->images_[1]);
   EXPECT_EQ(true, parsed->check_jni_);
   EXPECT_EQ(2048U, parsed->heap_initial_size_);
   EXPECT_EQ(4 * KB, parsed->heap_maximum_size_);
diff --git a/src/space.cc b/src/space.cc
index 36eb1f6..36e3cf2 100644
--- a/src/space.cc
+++ b/src/space.cc
@@ -59,18 +59,25 @@
 
 bool Space::Init(size_t initial_size, size_t maximum_size, byte* requested_base) {
   if (!(initial_size <= maximum_size)) {
+    LOG(WARNING) << "Failed to create space with initial size > maximum size ("
+                 << initial_size << ">" << maximum_size << ")";
     return false;
   }
   size_t length = RoundUp(maximum_size, kPageSize);
   int prot = PROT_READ | PROT_WRITE;
   UniquePtr<MemMap> mem_map(MemMap::Map(requested_base, length, prot));
   if (mem_map.get() == NULL) {
+    LOG(WARNING) << "Failed to allocate " << length << " bytes for space";
     return false;
   }
   Init(mem_map.release());
   maximum_size_ = maximum_size;
   mspace_ = CreateMallocSpace(base_, initial_size, maximum_size);
-  return (mspace_ != NULL);
+  if (mspace_ == NULL) {
+    LOG(WARNING) << "Failed to create mspace for space";
+    return false;
+  }
+  return true;
 }
 
 void Space::Init(MemMap* mem_map) {
@@ -83,17 +90,24 @@
 bool Space::Init(const char* image_file_name) {
   UniquePtr<File> file(OS::OpenFile(image_file_name, false));
   if (file.get() == NULL) {
+    LOG(WARNING) << "Failed to open " << image_file_name;
     return false;
   }
   ImageHeader image_header;
   bool success = file->ReadFully(&image_header, sizeof(image_header));
   if (!success || !image_header.IsValid()) {
+    LOG(WARNING) << "Invalid image header " << image_file_name;
     return false;
   }
   UniquePtr<MemMap> map(MemMap::Map(image_header.GetBaseAddr(),
-      file->Length(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED,
-      file->Fd(), 0));
+                                    file->Length(),
+                                    // TODO: selectively PROT_EXEC when image contains a code space
+                                    PROT_READ | PROT_WRITE | PROT_EXEC,
+                                    MAP_PRIVATE | MAP_FIXED,
+                                    file->Fd(),
+                                    0));
   if (map.get() == NULL) {
+    LOG(WARNING) << "Failed to map " << image_file_name;
     return false;
   }
   CHECK_EQ(image_header.GetBaseAddr(), map->GetAddress());