Separate oat from image

Change-Id: If2abdb99826ead14e3465d90ba2acffd89709389
diff --git a/Android.mk b/Android.mk
index f141d51..bd37967 100644
--- a/Android.mk
+++ b/Android.mk
@@ -57,8 +57,8 @@
   $(foreach file,$(sort $(ART_HOST_TEST_EXECUTABLES)),$(1) $(file) &&) true
 endef
 
-ART_HOST_TEST_DEPENDENCIES   := $(ART_HOST_TEST_EXECUTABLES)   $(ANDROID_HOST_OUT)/framework/core-hostdex.jar   $(ART_TEST_OAT_FILES)
-ART_TARGET_TEST_DEPENDENCIES := $(ART_TARGET_TEST_EXECUTABLES) $(ANDROID_PRODUCT_OUT)/system/framework/core.jar $(ART_TEST_OAT_FILES)
+ART_HOST_TEST_DEPENDENCIES   := $(ART_HOST_EXECUTABLES)   $(ART_HOST_TEST_EXECUTABLES)   $(ANDROID_HOST_OUT)/framework/core-hostdex.jar   $(ART_TEST_OAT_FILES)
+ART_TARGET_TEST_DEPENDENCIES := $(ART_TARGET_EXECUTABLES) $(ART_TARGET_TEST_EXECUTABLES) $(ANDROID_PRODUCT_OUT)/system/framework/core.jar $(ART_TEST_OAT_FILES)
 
 ART_TARGET_TEST_DEPENDENCIES += $(TARGET_OUT_EXECUTABLES)/oat_process $(TARGET_OUT_EXECUTABLES)/oat_processd
 
@@ -88,7 +88,7 @@
 
 # "mm test-art-target" to build and run all target tests
 .PHONY: test-art-target
-test-art-target: test-art-target-gtest test-art-target-oat
+test-art-target: test-art-target-gtest test-art-target-oat test-art-target-run-test
 	@echo test-art-target PASSED
 
 .PHONY: test-art-target-sync
@@ -108,6 +108,15 @@
 test-art-target-oat: $(ART_TEST_OAT_TARGETS)
 	@echo test-art-target-oat PASSED
 
+.PHONY: test-art-target-run-test
+test-art-target-run-test: test-art-target-run-test-002
+	@echo test-art-target-run-test PASSED
+
+.PHONY: test-art-target-run-test-002
+test-art-target-run-test-002:
+	art/test/run-test 002
+	@echo test-art-target-run-test-002 PASSED
+
 ########################################################################
 # oat_process test targets
 
@@ -125,7 +134,7 @@
 test-art-target-oat-process-am: $(TARGET_OUT_JAVA_LIBRARIES)/am.oat test-art-target-sync
 	adb remount
 	adb sync
-	adb shell sh -c "export CLASSPATH=/system/framework/am.jar && oat_processd /system/bin/app_process -Xbootimage:/system/framework/boot.oat -Ximage:/system/framework/am.oat /system/bin com.android.commands.am.Am start http://android.com && touch /sdcard/test-art-target-process-am"
+	adb shell sh -c "export CLASSPATH=/system/framework/am.jar && oat_processd /system/bin/app_process -Xbootimage:/system/framework/boot.art -Ximage:/system/framework/am.oat /system/bin com.android.commands.am.Am start http://android.com && touch /sdcard/test-art-target-process-am"
 	$(hide) (adb pull /sdcard/test-art-target-process-am /tmp/ && echo test-art-target-process-am PASSED) || echo test-art-target-process-am FAILED
 	$(hide) rm /tmp/test-art-target-process-am
 
@@ -149,7 +158,7 @@
 	  sleep 30; \
 	fi
 	adb shell kill `adb shell ps | fgrep com.android.calculator2 | sed -e 's/[^ ]* *\([0-9]*\).*/\1/'`
-	adb shell sh -c "export CLASSPATH=/system/framework/am.jar && oat_processd /system/bin/app_process -Xbootimage:/system/framework/boot.oat -Ximage:/system/framework/am.oat /system/bin com.android.commands.am.Am start -a android.intent.action.MAIN -n com.android.calculator2/.Calculator && touch /sdcard/test-art-target-process-Calculator"
+	adb shell sh -c "export CLASSPATH=/system/framework/am.jar && oat_processd /system/bin/app_process -Xbootimage:/system/framework/boot.art -Ximage:/system/framework/am.oat /system/bin com.android.commands.am.Am start -a android.intent.action.MAIN -n com.android.calculator2/.Calculator && touch /sdcard/test-art-target-process-Calculator"
 	$(hide) (adb pull /sdcard/test-art-target-process-Calculator /tmp/ && echo test-art-target-process-Calculator PASSED) || echo test-art-target-process-Calculator FAILED
 	$(hide) rm /tmp/test-art-target-process-Calculator
 
@@ -161,17 +170,17 @@
 
 .PHONY: dump-oat-core
 dump-oat-core: $(TARGET_CORE_OAT) $(OATDUMP)
-	$(OATDUMP) $(addprefix --dex-file=,$(TARGET_CORE_DEX)) --image=$< --strip-prefix=$(PRODUCT_OUT) --output=/tmp/core.oatdump.txt
+	$(OATDUMP) $(addprefix --dex-file=,$(TARGET_CORE_DEX)) --oat=$(TARGET_CORE_OAT) --image=$(TARGET_CORE_IMG) --strip-prefix=$(PRODUCT_OUT) --output=/tmp/core.oatdump.txt
 	@echo Output in /tmp/core.oatdump.txt
 
 .PHONY: dump-oat-boot
 dump-oat-boot: $(TARGET_BOOT_OAT) $(OATDUMP)
-	$(OATDUMP) $(addprefix --dex-file=,$(TARGET_BOOT_DEX)) --image=$< --strip-prefix=$(PRODUCT_OUT) --output=/tmp/boot.oatdump.txt
+	$(OATDUMP) $(addprefix --dex-file=,$(TARGET_BOOT_DEX)) --oat=$(TARGET_BOOT_OAT) --image=$(TARGET_BOOT_IMG) --strip-prefix=$(PRODUCT_OUT) --output=/tmp/boot.oatdump.txt
 	@echo Output in /tmp/boot.oatdump.txt
 
 .PHONY: dump-oat-Calculator
 dump-oat-Calculator: $(TARGET_OUT_APPS)/Calculator.oat $(TARGET_BOOT_OAT) $(OATDUMP)
-	$(OATDUMP) --dex-file=$(TARGET_OUT_APPS)/Calculator.apk --image=$< $(addprefix --boot-dex-file=,$(TARGET_BOOT_DEX)) --boot=$(TARGET_BOOT_OAT) --strip-prefix=$(PRODUCT_OUT) --output=/tmp/Calculator.oatdump.txt
+	$(OATDUMP) --dex-file=$(TARGET_OUT_APPS)/Calculator.apk --oat=$< --image=$(patsubst %.oat,%.art,$<) $(addprefix --boot-dex-file=,$(TARGET_BOOT_DEX)) --boot-oat=$(TARGET_BOOT_OAT) --boot-image=$(TARGET_BOOT_IMG) --strip-prefix=$(PRODUCT_OUT) --output=/tmp/Calculator.oatdump.txt
 	@echo Output in /tmp/Calculator.oatdump.txt
 
 
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 1b65971..481fbc0 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -129,6 +129,9 @@
 	src/monitor.cc \
 	src/mspace.c \
 	src/mutex.cc \
+	src/oat.cc \
+	src/oat_file.cc \
+	src/oat_writer.cc \
 	src/object.cc \
 	src/object_bitmap.cc \
 	src/offsets.cc \
@@ -187,6 +190,7 @@
 	src/jni_compiler_test.cc \
 	src/managed_register_arm_test.cc \
 	src/managed_register_x86_test.cc \
+	src/oat_test.cc \
 	src/object_test.cc \
 	src/reference_table_test.cc \
 	src/runtime_test.cc \
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 2ea132d..52d5621 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -24,9 +24,9 @@
 # TODO: for now, override with debug version for better error reporting
 OATDUMP := $(OATDUMPD)
 
-# start of oat reserved address space
-OAT_HOST_BASE_ADDRESS   := 0x60000000
-OAT_TARGET_BASE_ADDRESS := 0x60000000
+# start of image reserved address space
+IMG_HOST_BASE_ADDRESS   := 0x60000000
+IMG_TARGET_BASE_ADDRESS := 0x60000000
 
 ########################################################################
 # A smaller libcore only oat file
@@ -39,23 +39,27 @@
 HOST_CORE_OAT := $(HOST_OUT_JAVA_LIBRARIES)/core.oat
 TARGET_CORE_OAT := $(TARGET_OUT_JAVA_LIBRARIES)/core.oat
 
+HOST_CORE_IMG := $(HOST_OUT_JAVA_LIBRARIES)/core.art
+TARGET_CORE_IMG := $(TARGET_OUT_JAVA_LIBRARIES)/core.art
+
 # TODO: change DEX2OATD to order-only prerequisite when output is stable
 $(HOST_CORE_OAT): $(HOST_CORE_DEX) $(DEX2OAT)
 	@echo "host dex2oat: $@ ($<)"
-	$(hide) $(DEX2OAT) -Xms16m -Xmx16m $(addprefix --dex-file=,$(filter-out $(DEX2OAT),$^)) --image=$@ --base=$(OAT_HOST_BASE_ADDRESS)
+	$(hide) $(DEX2OAT) -Xms16m -Xmx16m $(addprefix --dex-file=,$(filter-out $(DEX2OAT),$^)) --oat=$@ --image=$(HOST_CORE_IMG) --base=$(IMG_HOST_BASE_ADDRESS)
 
 # TODO: change DEX2OATD to order-only prerequisite when output is stable
 $(TARGET_CORE_OAT): $(TARGET_CORE_DEX) $(DEX2OAT)
 	@echo "target dex2oat: $@ ($<)"
-	$(hide) $(DEX2OAT) -Xms32m -Xmx32m $(addprefix --dex-file=,$(filter-out $(DEX2OAT),$^)) --image=$@ --base=$(OAT_TARGET_BASE_ADDRESS) --strip-prefix=$(PRODUCT_OUT)
+	$(hide) $(DEX2OAT) -Xms32m -Xmx32m $(addprefix --dex-file=,$(filter-out $(DEX2OAT),$^)) --oat=$@ --image=$(TARGET_CORE_IMG) --base=$(IMG_TARGET_BASE_ADDRESS) --strip-prefix=$(PRODUCT_OUT)
 
 ########################################################################
 # The full system boot classpath
 TARGET_BOOT_JARS := $(subst :, ,$(DEXPREOPT_BOOT_JARS))
 TARGET_BOOT_DEX := $(foreach jar,$(TARGET_BOOT_JARS),$(TARGET_OUT_JAVA_LIBRARIES)/$(jar).jar)
 TARGET_BOOT_OAT := $(TARGET_OUT_JAVA_LIBRARIES)/boot.oat
+TARGET_BOOT_IMG := $(TARGET_OUT_JAVA_LIBRARIES)/boot.art
 
 # TODO: change DEX2OATD to order-only prerequisite when output is stable
 $(TARGET_BOOT_OAT): $(TARGET_BOOT_DEX) $(DEX2OAT)
 	@echo "target dex2oat: $@ ($<)"
-	$(hide) $(DEX2OAT) -Xms256m -Xmx256m $(addprefix --dex-file=,$(filter-out $(DEX2OAT),$^)) --image=$@ --base=$(OAT_TARGET_BASE_ADDRESS) --strip-prefix=$(PRODUCT_OUT)
+	$(hide) $(DEX2OAT) -Xms256m -Xmx256m $(addprefix --dex-file=,$(filter-out $(DEX2OAT),$^)) --oat=$@ --image=$(TARGET_BOOT_IMG) --base=$(IMG_TARGET_BASE_ADDRESS) --strip-prefix=$(PRODUCT_OUT)
diff --git a/build/Android.oattest.mk b/build/Android.oattest.mk
index fe26a9b..ce615b2 100644
--- a/build/Android.oattest.mk
+++ b/build/Android.oattest.mk
@@ -40,7 +40,7 @@
 # TODO: change DEX2OATD (and perhaps $(2) boot oat) to order-only prerequisite when output is stable
 $(patsubst %.apk,%.oat,$(patsubst %.jar,%.oat,$(1))): $(1) $(2) $(DEX2OAT)
 	@echo "target dex2oat: $$@ ($$<)"
-	$(hide) $(DEX2OAT) -Xms16m -Xmx16m $(addprefix --boot-dex-file=,$(3)) --boot=$(2) $(addprefix --dex-file=,$$<) --image=$$@ --strip-prefix=$(PRODUCT_OUT)
+	$(hide) $(DEX2OAT) -Xms16m -Xmx16m $(addprefix --boot-dex-file=,$(3)) --boot-oat=$(2) --boot-image=$(patsubst %.oat,%.art,$(2)) $(addprefix --dex-file=,$$<) --oat=$$@ --image=$$(patsubst %.oat,%.art,$$@) --strip-prefix=$(PRODUCT_OUT)
 endef
 
 ########################################################################
@@ -64,7 +64,7 @@
 test-art-target-oat-$(1): test-art-target-sync
 	adb shell touch /sdcard/test-art-target-oat-$(1)
 	adb shell rm /sdcard/test-art-target-oat-$(1)
-	adb shell sh -c "oatexecd -Xbootclasspath:/system/framework/core.jar -Xbootimage:/system/framework/core.oat -classpath /system/framework/art-test-dex-$(1).jar -Ximage:/system/framework/art-test-dex-$(1).oat $(1) $(2) && touch /sdcard/test-art-target-oat-$(1)"
+	adb shell sh -c "oatexecd -Xbootclasspath:/system/framework/core.jar -Xbootoat:/system/framework/core.oat -Xbootimage:/system/framework/core.art -classpath /system/framework/art-test-dex-$(1).jar -Xoat:/system/framework/art-test-dex-$(1).oat -Ximage:/system/framework/art-test-dex-$(1).art $(1) $(2) && touch /sdcard/test-art-target-oat-$(1)"
 	$(hide) (adb pull /sdcard/test-art-target-oat-$(1) /tmp/ && echo test-art-target-oat-$(1) PASSED) || (echo test-art-target-oat-$(1) FAILED && exit 1)
 	$(hide) rm /tmp/test-art-target-oat-$(1)
 
diff --git a/src/common_test.h b/src/common_test.h
index bbf2e2f..8491dbb 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -106,19 +106,21 @@
     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)));
-    options.push_back(std::make_pair("-Xms16m", reinterpret_cast<void*>(NULL)));
-    options.push_back(std::make_pair("-Xmx16m", reinterpret_cast<void*>(NULL)));
+    options.push_back(std::make_pair("-Xms64m", reinterpret_cast<void*>(NULL)));
+    options.push_back(std::make_pair("-Xmx64m", reinterpret_cast<void*>(NULL)));
     runtime_.reset(Runtime::Create(options, false));
     ASSERT_TRUE(runtime_.get() != NULL);
     class_linker_ = runtime_->GetClassLinker();
 
 #if defined(__i386__)
     runtime_->SetJniStubArray(JniCompiler::CreateJniStub(kX86));
+    runtime_->SetAbstractMethodErrorStubArray(Compiler::CreateAbstractMethodErrorStub(kX86));
     runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(kX86));
     compiler_.reset(new Compiler(kX86));
 #elif defined(__arm__)
     runtime_->SetJniStubArray(JniCompiler::CreateJniStub(kThumb2));
     runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(kThumb2));
+    runtime_->SetAbstractMethodErrorStubArray(Compiler::CreateAbstractMethodErrorStub(kThumb2));
     compiler_.reset(new Compiler(kThumb2));
 #endif
 
diff --git a/src/compiler.cc b/src/compiler.cc
index 34263da..af5cdd6 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -24,14 +24,22 @@
   ByteArray* CreateAbstractMethodErrorStub();
 }
 
+ByteArray* Compiler::CreateAbstractMethodErrorStub(InstructionSet instruction_set) {
+  switch (instruction_set) {
+    case kArm:
+    case kThumb2:
+      return arm::CreateAbstractMethodErrorStub();
+    case kX86:
+      return x86::CreateAbstractMethodErrorStub();
+    default:
+      LOG(FATAL) << "Unknown InstructionSet " << (int) instruction_set;
+      return NULL;
+  }
+}
+
 Compiler::Compiler(InstructionSet insns) : instruction_set_(insns), jni_compiler_(insns),
     verbose_(false) {
   CHECK(!Runtime::Current()->IsStarted());
-  if (insns == kArm || insns == kThumb2) {
-    abstract_method_error_stub_ = arm::CreateAbstractMethodErrorStub();
-  } else if (insns == kX86) {
-    abstract_method_error_stub_ = x86::CreateAbstractMethodErrorStub();
-  }
 }
 
 void Compiler::CompileAll(const ClassLoader* class_loader) {
@@ -273,17 +281,17 @@
       method->UnregisterNative();
     }
   } else if (method->IsAbstract()) {
-    DCHECK(abstract_method_error_stub_ != NULL);
+    ByteArray* abstract_method_error_stub = Runtime::Current()->GetAbstractMethodErrorStubArray();
     if (instruction_set_ == kX86) {
-      method->SetCode(abstract_method_error_stub_, kX86);
+      method->SetCodeArray(abstract_method_error_stub, kX86);
     } else {
       CHECK(instruction_set_ == kArm || instruction_set_ == kThumb2);
-      method->SetCode(abstract_method_error_stub_, kArm);
+      method->SetCodeArray(abstract_method_error_stub, kArm);
     }
   } else {
     oatCompileMethod(*this, method, kThumb2);
   }
-  CHECK(method->GetCode() != NULL);
+  CHECK(method->GetCode() != NULL) << PrettyMethod(method);
 
   if (instruction_set_ == kX86) {
     art::x86::X86CreateInvokeStub(method);
diff --git a/src/compiler.h b/src/compiler.h
index 563bbde..f9a5c29 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -30,6 +30,10 @@
     return verbose_;
   }
 
+  // Stub to throw AbstractMethodError
+  // TODO: remove from Compiler
+  static ByteArray* CreateAbstractMethodErrorStub(InstructionSet instruction_set);
+
  private:
   // Attempt to resolve all type, methods, fields, and strings
   // referenced from code in the dex file following PathClassLoader
@@ -55,7 +59,6 @@
 
   InstructionSet instruction_set_;
   JniCompiler jni_compiler_;
-  ByteArray* abstract_method_error_stub_;
 
   bool verbose_;
 
diff --git a/src/compiler/Frontend.cc b/src/compiler/Frontend.cc
index eef6888..cb83143 100644
--- a/src/compiler/Frontend.cc
+++ b/src/compiler/Frontend.cc
@@ -904,7 +904,9 @@
     memcpy(vmap_table->GetData(),
            reinterpret_cast<const int16_t*>(&cUnit.coreVmapTable[0]),
            vmap_table->GetLength() * sizeof(cUnit.coreVmapTable[0]));
-    method->SetCode(managed_code, art::kThumb2, mapping_table, vmap_table);
+    method->SetCodeArray(managed_code, art::kThumb2);
+    method->SetMappingTable(mapping_table);
+    method->SetVMapTable(vmap_table);
     method->SetFrameSizeInBytes(cUnit.frameSize);
     method->SetReturnPcOffsetInBytes(cUnit.frameSize - sizeof(intptr_t));
     method->SetCoreSpillMask(cUnit.coreSpillMask);
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
index 6c967ed..78d3628 100644
--- a/src/dex2oat.cc
+++ b/src/dex2oat.cc
@@ -10,6 +10,7 @@
 #include "class_loader.h"
 #include "compiler.h"
 #include "image_writer.h"
+#include "oat_writer.h"
 #include "runtime.h"
 #include "stringpiece.h"
 
@@ -25,7 +26,12 @@
           "      Example: --dex-file=/system/framework/core.jar\n"
           "\n");
   fprintf(stderr,
-          "  --image=<file>: specifies the required output image filename.\n"
+          "  --image=<file.art>: specifies the required output image filename.\n"
+          "      Example: --image=/system/framework/boot.art\n"
+          "\n");
+  // TODO: remove this by inferring from --image
+  fprintf(stderr,
+          "  --oat=<file.oat>: specifies the required oat filename.\n"
           "      Example: --image=/system/framework/boot.oat\n"
           "\n");
   fprintf(stderr,
@@ -33,10 +39,15 @@
           "      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"
+          "  --boot-image=<file.art>: provide the image file for the boot class path.\n"
+          "      Example: --boot-image=/system/framework/boot.art\n"
           "\n");
-  // TODO: remove this by making boot image contain boot DexFile information?
+  // TODO: remove this by inferring from --boot-image
+  fprintf(stderr,
+          "  --boot-oat=<file.oat>: provide the oat file for the boot class path.\n"
+          "      Example: --boot-oat=/system/framework/boot.oat\n"
+          "\n");
+  // TODO: remove this by inderring from --boot-image or --boot-oat
   fprintf(stderr,
           "  --boot-dex-file=<dex-file>: specifies a .dex file that is part of the boot\n"
           "      image specified with --boot. \n"
@@ -76,8 +87,10 @@
 
   std::vector<const char*> dex_filenames;
   std::vector<const char*> method_names;
+  std::string oat_filename;
   const char* image_filename = NULL;
   std::string boot_image_option;
+  std::string boot_oat_option;
   std::vector<const char*> boot_dex_filenames;
   uintptr_t image_base = 0;
   std::string strip_location_prefix;
@@ -90,6 +103,8 @@
       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("--oat=")) {
+      oat_filename = option.substr(strlen("--oat=")).data();
     } else if (option.starts_with("--image=")) {
       image_filename = option.substr(strlen("--image=")).data();
     } else if (option.starts_with("--base=")) {
@@ -100,11 +115,16 @@
         fprintf(stderr, "Failed to 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();
+    } else if (option.starts_with("--boot-image=")) {
+      const char* boot_image_filename = option.substr(strlen("--boot-image=")).data();
       boot_image_option.clear();
       boot_image_option += "-Xbootimage:";
       boot_image_option += boot_image_filename;
+    } else if (option.starts_with("--boot-oat=")) {
+      const char* boot_oat_filename = option.substr(strlen("--boot-oat=")).data();
+      boot_oat_option.clear();
+      boot_oat_option += "-Xbootoat:";
+      boot_oat_option += boot_oat_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=")) {
@@ -119,6 +139,11 @@
     }
   }
 
+  if (oat_filename == NULL) {
+   fprintf(stderr, "--oat file name not specified\n");
+   return EXIT_FAILURE;
+  }
+
   if (image_filename == NULL) {
    fprintf(stderr, "--image file name not specified\n");
    return EXIT_FAILURE;
@@ -129,6 +154,11 @@
    return EXIT_FAILURE;
   }
 
+  if (boot_image_option.empty() != boot_oat_option.empty()) {
+   fprintf(stderr, "--boot-image and --boat-oat must be specified together or not at all\n");
+   return EXIT_FAILURE;
+  }
+
   if (boot_image_option.empty()) {
     if (image_base == 0) {
       fprintf(stderr, "non-zero --base not specified\n");
@@ -136,7 +166,7 @@
     }
   } else {
     if (boot_dex_filenames.empty()) {
-      fprintf(stderr, "no --boot-dex-file values specified with --boot\n");
+      fprintf(stderr, "no --boot-dex-file values specified with --boot-image\n");
       return EXIT_FAILURE;
     }
   }
@@ -153,6 +183,7 @@
   } 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)));
+    options.push_back(std::make_pair(boot_oat_option.c_str(), reinterpret_cast<void*>(NULL)));
   }
   if (Xms != NULL) {
     options.push_back(std::make_pair(Xms, reinterpret_cast<void*>(NULL)));
@@ -167,11 +198,12 @@
   }
   ClassLinker* class_linker = runtime->GetClassLinker();
 
-  // If we have an existing boot image, position new space after it
+  // If we have an existing boot image, position new space after its oat file
   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);
+    byte* oat_limit_addr = boot_space->GetImageHeader().GetOatLimitAddr();
+    image_base = RoundUp(reinterpret_cast<uintptr_t>(oat_limit_addr), kPageSize);
   }
 
   // ClassLoader creation needs to come after Runtime::Create
@@ -185,11 +217,13 @@
     class_loader = PathClassLoader::Alloc(dex_files);
   }
 
-  // if we loaded an existing image, we will reuse its stub array.
+  // if we loaded an existing image, we will reuse values from the image roots.
   if (!runtime->HasJniStubArray()) {
     runtime->SetJniStubArray(JniCompiler::CreateJniStub(kThumb2));
   }
-  // similarly for the callee save method
+  if (!runtime->HasAbstractMethodErrorStubArray()) {
+    runtime->SetAbstractMethodErrorStubArray(Compiler::CreateAbstractMethodErrorStub(kThumb2));
+  }
   if (!runtime->HasCalleeSaveMethod()) {
     runtime->SetCalleeSaveMethod(runtime->CreateCalleeSaveMethod(kThumb2));
   }
@@ -239,9 +273,14 @@
     }
   }
 
-  ImageWriter writer;
-  if (!writer.Write(image_filename, image_base)) {
-    fprintf(stderr, "could not write image %s\n", image_filename);
+  if (!OatWriter::Create(oat_filename, class_loader)) {
+    fprintf(stderr, "Failed to create oat file %s\n", oat_filename.c_str());
+    return EXIT_FAILURE;
+  }
+
+  ImageWriter image_writer;
+  if (!image_writer.Write(image_filename, image_base, oat_filename, strip_location_prefix)) {
+    fprintf(stderr, "Failed to create image file %s\n", image_filename);
     return EXIT_FAILURE;
   }
 
diff --git a/src/dex_file.cc b/src/dex_file.cc
index c532134..d437a3f 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -475,17 +475,25 @@
   for (size_t i = 0; i < NumClassDefs(); ++i) {
     const ClassDef& class_def = GetClassDef(i);
     const char* descriptor = GetClassDescriptor(class_def);
-    index_[descriptor] = &class_def;
+    index_[descriptor] = i;
   }
 }
 
-const DexFile::ClassDef* DexFile::FindClassDef(const StringPiece& descriptor) const {
+bool DexFile::FindClassDefIndex(const StringPiece& descriptor, uint32_t& idx) const {
   Index::const_iterator it = index_.find(descriptor);
   if (it == index_.end()) {
-    return NULL;
-  } else {
-    return it->second;
+    return false;
   }
+  idx = it->second;
+  return true;
+}
+
+const DexFile::ClassDef* DexFile::FindClassDef(const StringPiece& descriptor) const {
+  uint32_t idx;
+  if (FindClassDefIndex(descriptor, idx)) {
+    return &GetClassDef(idx);
+  }
+  return NULL;
 }
 
 // Materializes the method descriptor for a method prototype.  Method
diff --git a/src/dex_file.h b/src/dex_file.h
index 4ad701f..e313d74 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -359,6 +359,9 @@
     return *header_;
   }
 
+  // Looks up a class definition index by its class descriptor.
+  bool FindClassDefIndex(const StringPiece& descriptor, uint32_t& idx) const;
+
   // Looks up a class definition by its class descriptor.
   const ClassDef* FindClassDef(const StringPiece& descriptor) const;
 
@@ -909,8 +912,8 @@
   // Returns true if the header magic is of the expected value.
   bool IsMagicValid();
 
-  // The index of descriptors to class definitions.
-  typedef std::map<const StringPiece, const DexFile::ClassDef*> Index;
+  // The index of descriptors to class definition indexes.
+  typedef std::map<const StringPiece, uint32_t> Index;
   Index index_;
 
   // The base address of the memory mapping.
diff --git a/src/dex_file_test.cc b/src/dex_file_test.cc
index 4271d0c..3a9b6f4 100644
--- a/src/dex_file_test.cc
+++ b/src/dex_file_test.cc
@@ -2,8 +2,6 @@
 
 #include "dex_file.h"
 
-#include <stdio.h>
-
 #include "UniquePtr.h"
 #include "common_test.h"
 
diff --git a/src/exception_test.cc b/src/exception_test.cc
index ee6c1a0d..2e9cf3f 100644
--- a/src/exception_test.cc
+++ b/src/exception_test.cc
@@ -75,19 +75,21 @@
     ByteArray* fake_code = ByteArray::Alloc(12);
     ASSERT_TRUE(fake_code != NULL);
     IntArray* fake_mapping_data = IntArray::Alloc(2);
-    ASSERT_TRUE(fake_mapping_data!= NULL);
+    ASSERT_TRUE(fake_mapping_data != NULL);
     fake_mapping_data->Set(0, 3);  // offset 3
     fake_mapping_data->Set(1, 3);  // maps to dex offset 3
     method_f_ = my_klass_->FindVirtualMethod("f", "()I");
     ASSERT_TRUE(method_f_ != NULL);
     method_f_->SetFrameSizeInBytes(kStackAlignment);
     method_f_->SetReturnPcOffsetInBytes(kStackAlignment-kPointerSize);
-    method_f_->SetCode(fake_code, kThumb2, fake_mapping_data);
+    method_f_->SetCodeArray(fake_code, kThumb2);
+    method_f_->SetMappingTable(fake_mapping_data);
     method_g_ = my_klass_->FindVirtualMethod("g", "(I)V");
     ASSERT_TRUE(method_g_ != NULL);
     method_g_->SetFrameSizeInBytes(kStackAlignment);
     method_g_->SetReturnPcOffsetInBytes(kStackAlignment-kPointerSize);
-    method_g_->SetCode(fake_code, kThumb2, fake_mapping_data);
+    method_g_->SetCodeArray(fake_code, kThumb2);
+    method_g_->SetMappingTable(fake_mapping_data);
   }
 
   UniquePtr<const DexFile> dex_;
diff --git a/src/globals.h b/src/globals.h
index 2fb0225..e5fead6 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -38,6 +38,9 @@
 // Required object alignment
 const int kObjectAlignment = 8;
 
+// Required ARM instruction alignment
+const int kArmAlignment = 4;
+
 // System page size.  Normally you're expected to get this from
 // sysconf(_SC_PAGESIZE) or some system-specific define (usually
 // PAGESIZE or PAGE_SIZE).  If we use a simple compile-time constant
diff --git a/src/heap.cc b/src/heap.cc
index 015f638..2051051 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -74,7 +74,9 @@
       LOG(FATAL) << "Failed to create space from " << boot_image_file_name;
     }
     spaces_.push_back(boot_space);
-    requested_base = boot_space->GetBase() + RoundUp(boot_space->Size(), kPageSize);
+    byte* oat_limit_addr = boot_space->GetImageHeader().GetOatLimitAddr();
+    requested_base = reinterpret_cast<byte*>(RoundUp(reinterpret_cast<uintptr_t>(oat_limit_addr),
+                                                     kPageSize));
   }
 
   std::vector<Space*> image_spaces;
@@ -85,7 +87,9 @@
     }
     image_spaces.push_back(space);
     spaces_.push_back(space);
-    requested_base = space->GetBase() + RoundUp(space->Size(), kPageSize);
+    byte* oat_limit_addr = space->GetImageHeader().GetOatLimitAddr();
+    requested_base = reinterpret_cast<byte*>(RoundUp(reinterpret_cast<uintptr_t>(oat_limit_addr),
+                                                     kPageSize));
   }
 
   Space* space = Space::Create(initial_size, maximum_size, requested_base);
diff --git a/src/image.cc b/src/image.cc
index 5bf7b10..c610c8a 100644
--- a/src/image.cc
+++ b/src/image.cc
@@ -4,7 +4,7 @@
 
 namespace art {
 
-const byte ImageHeader::kImageMagic[] = { 'o', 'a', 't', '\n' };
+const byte ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
 const byte ImageHeader::kImageVersion[] = { '0', '0', '1', '\0' };
 
 }  // namespace art
diff --git a/src/image.h b/src/image.h
index fdf6443..3d2f5f4 100644
--- a/src/image.h
+++ b/src/image.h
@@ -11,12 +11,25 @@
 namespace art {
 
 // header of image files written by ImageWriter, read and validated by Space.
-class ImageHeader {
+class PACKED ImageHeader {
  public:
   ImageHeader() {}
 
-  ImageHeader(uint32_t base_addr, uint32_t image_roots)
-      : base_addr_(base_addr), image_roots_(image_roots) {
+  ImageHeader(uint32_t image_base_addr,
+              uint32_t image_roots,
+              uint32_t oat_checksum,
+              uint32_t oat_base_addr,
+              uint32_t oat_limit_addr)
+      : image_base_addr_(image_base_addr),
+        oat_checksum_(oat_checksum),
+        oat_base_addr_(oat_base_addr),
+        oat_limit_addr_(oat_limit_addr),
+        image_roots_(image_roots) {
+    CHECK_EQ(image_base_addr, RoundUp(image_base_addr, kPageSize));
+    CHECK_EQ(oat_base_addr, RoundUp(oat_base_addr, kPageSize));
+    CHECK_LT(image_base_addr, image_roots);
+    CHECK_LT(image_roots, oat_base_addr);
+    CHECK_LT(oat_base_addr, oat_limit_addr);
     memcpy(magic_, kImageMagic, sizeof(kImageMagic));
     memcpy(version_, kImageVersion, sizeof(kImageVersion));
   }
@@ -36,13 +49,27 @@
     return reinterpret_cast<const char*>(magic_);
   }
 
-  byte* GetBaseAddr() const {
-    return reinterpret_cast<byte*>(base_addr_);
+  byte* GetImageBaseAddr() const {
+    return reinterpret_cast<byte*>(image_base_addr_);
+  }
+
+  uint32_t GetOatChecksum() const {
+    return oat_checksum_;
+  }
+
+  byte* GetOatBaseAddr() const {
+    return reinterpret_cast<byte*>(oat_base_addr_);
+  }
+
+  byte* GetOatLimitAddr() const {
+    return reinterpret_cast<byte*>(oat_limit_addr_);
   }
 
   enum ImageRoot {
     kJniStubArray,
+    kAbstractMethodErrorStubArray,
     kCalleeSaveMethod,
+    kOatLocation,
     kImageRootsMax,
   };
 
@@ -58,7 +85,16 @@
   byte version_[4];
 
   // required base address for mapping the image.
-  uint32_t base_addr_;
+  uint32_t image_base_addr_;
+
+  // checksum of the oat file we link to for load time sanity check
+  uint32_t oat_checksum_;
+
+  // required oat address expected by image Method::GetCode() pointers.
+  uint32_t oat_base_addr_;
+
+  // end of oat address range for this image file, used for positioning a following image
+  uint32_t oat_limit_addr_;
 
   // absolute address of an Object[] of objects needed to reinitialize from an image
   uint32_t image_roots_;
diff --git a/src/image_test.cc b/src/image_test.cc
index 7ecdf3c..2fd5c34 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -7,6 +7,7 @@
 #include "file.h"
 #include "image.h"
 #include "image_writer.h"
+#include "oat_writer.h"
 #include "signal_catcher.h"
 #include "space.h"
 #include "utils.h"
@@ -16,22 +17,19 @@
 class ImageTest : public CommonTest {};
 
 TEST_F(ImageTest, WriteRead) {
-  // TODO: remove the touching of classes, call Compiler instead
-  for (size_t i = 0; i < java_lang_dex_file_->NumClassDefs(); i++) {
-    const DexFile::ClassDef& class_def = java_lang_dex_file_->GetClassDef(i);
-    const char* descriptor = java_lang_dex_file_->GetClassDescriptor(class_def);
-    Class* klass = class_linker_->FindSystemClass(descriptor);
-    ASSERT_TRUE(klass != NULL) << descriptor;
-  }
+  ScratchFile tmp_oat;
+  bool success_oat = OatWriter::Create(tmp_oat.GetFilename(), NULL);
+  ASSERT_TRUE(success_oat);
 
   ImageWriter writer;
-  ScratchFile tmp;
+  ScratchFile tmp_image;
   const uintptr_t image_base = 0x50000000;
-  bool success = writer.Write(tmp.GetFilename(), image_base);
-  ASSERT_TRUE(success);
+  bool success_image = writer.Write(tmp_image.GetFilename(), image_base,
+                                    std::string(tmp_oat.GetFilename()), "");
+  ASSERT_TRUE(success_image);
 
   {
-    UniquePtr<File> file(OS::OpenFile(tmp.GetFilename(), false));
+    UniquePtr<File> file(OS::OpenFile(tmp_image.GetFilename(), false));
     ASSERT_TRUE(file.get() != NULL);
     ImageHeader image_header;
     file->ReadFully(&image_header, sizeof(image_header));
@@ -58,8 +56,11 @@
 
   Runtime::Options options;
   options.push_back(std::make_pair("bootclasspath", &boot_class_path));
+  std::string boot_oat("-Xbootoat:");
+  boot_oat.append(tmp_oat.GetFilename());
+  options.push_back(std::make_pair(boot_oat.c_str(), reinterpret_cast<void*>(NULL)));
   std::string boot_image("-Xbootimage:");
-  boot_image.append(tmp.GetFilename());
+  boot_image.append(tmp_image.GetFilename());
   options.push_back(std::make_pair(boot_image.c_str(), reinterpret_cast<void*>(NULL)));
 
   runtime_.reset(Runtime::Create(options, false));
diff --git a/src/image_writer.cc b/src/image_writer.cc
index 23bc6cf..37eac2e 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -23,7 +23,8 @@
 
 namespace art {
 
-bool ImageWriter::Write(const char* filename, uintptr_t image_base) {
+bool ImageWriter::Write(const char* image_filename, uintptr_t image_base,
+                        const std::string& oat_filename, const std::string& strip_location_prefix) {
   CHECK_NE(image_base, 0U);
   image_base_ = reinterpret_cast<byte*>(image_base);
 
@@ -32,6 +33,12 @@
   CHECK_GE(spaces.size(), 1U);
   source_space_ = spaces[spaces.size()-1];
 
+  oat_file_.reset(OatFile::Open(oat_filename, strip_location_prefix, NULL));
+  if (oat_file_.get() == NULL) {
+    LOG(ERROR) << "Failed to open oat file " << oat_filename;
+    return false;
+  }
+
   if (!Init()) {
     return false;
   }
@@ -39,11 +46,17 @@
   CalculateNewObjectOffsets();
   CopyAndFixupObjects();
 
-  UniquePtr<File> file(OS::OpenFile(filename, true));
+  UniquePtr<File> file(OS::OpenFile(image_filename, true));
   if (file.get() == NULL) {
+    LOG(ERROR) << "Failed to open image file " << image_filename;
     return false;
   }
-  return file->WriteFully(image_->GetAddress(), image_top_);
+  bool success = file->WriteFully(image_->GetAddress(), image_top_);
+  if (!success) {
+    PLOG(ERROR) << "Failed to write image file " << image_filename;
+    return false;
+  }
+  return true;
 }
 
 bool ImageWriter::Init() {
@@ -52,6 +65,7 @@
   size_t length = RoundUp(size, kPageSize);
   image_.reset(MemMap::Map(length, prot));
   if (image_.get() == NULL) {
+    LOG(ERROR) << "Failed to allocate memory for image file generation";
     return false;
   }
   return true;
@@ -94,20 +108,29 @@
     if (dex_cache != NULL) {
       image_writer->dex_caches_.insert(dex_cache);
     } else {
-      DCHECK(klass->IsArrayClass() || klass->IsPrimitive());
+      DCHECK(klass->IsArrayClass() || klass->IsPrimitive()) << PrettyClass(klass);
     }
   }
 }
 
-ObjectArray<Object>* CreateImageRoots() {
+ObjectArray<Object>* ImageWriter::CreateImageRoots() const {
   // 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());
-  image_roots->Set(ImageHeader::kCalleeSaveMethod, runtime->GetCalleeSaveMethod());
+  image_roots->Set(ImageHeader::kJniStubArray,
+                   runtime->GetJniStubArray());
+  image_roots->Set(ImageHeader::kAbstractMethodErrorStubArray,
+                   runtime->GetAbstractMethodErrorStubArray());
+  image_roots->Set(ImageHeader::kCalleeSaveMethod,
+                   runtime->GetCalleeSaveMethod());
+  image_roots->Set(ImageHeader::kOatLocation,
+                   String::AllocFromModifiedUtf8(oat_file_->GetLocation().c_str()));
+  for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
+    CHECK(image_roots->Get(i) != NULL);
+  }
   return image_roots;
 }
 
@@ -125,12 +148,17 @@
   heap_bitmap->Walk(CalculateNewObjectOffsetsCallback, this);  // TODO: add Space-limited Walk
   DCHECK_LT(image_top_, image_->GetLength());
 
+  // Note that image_top_ is left at end of used space
+  oat_base_ = image_base_ +  RoundUp(image_top_, kPageSize);
+  byte* oat_limit = oat_base_ +  oat_file_->GetSize();
+
   // 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)));
+                           reinterpret_cast<uint32_t>(GetImageAddress(image_roots)),
+                           oat_file_->GetOatHeader().GetChecksum(),
+                           reinterpret_cast<uint32_t>(oat_base_),
+                           reinterpret_cast<uint32_t>(oat_limit));
   memcpy(image_->GetAddress(), &image_header, sizeof(image_header));
-
-  // Note that top_ is left at end of used space
 }
 
 void ImageWriter::CopyAndFixupObjects() {
@@ -199,14 +227,30 @@
 
 void ImageWriter::FixupMethod(const Method* orig, Method* copy) {
   FixupInstanceFields(orig, copy);
-  copy->code_ = FixupCode(copy->code_array_, orig->code_);
+
+  // OatWriter clears the code_array_ after writing the code.
+  // It replaces the code_ with an offset value we now adjust to be a pointer.
+  DCHECK(copy->code_array_ == NULL)
+          << PrettyMethod(orig)
+          << " orig_code_array_=" << orig->GetCodeArray() << " orig_code_=" << orig->GetCode()
+          << " copy_code_array_=" << copy->code_array_ << " orig_code_=" << copy->code_
+          << " jni_stub=" << Runtime::Current()->GetJniStubArray()
+          << " ame_stub=" << Runtime::Current()->GetAbstractMethodErrorStubArray();
   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();
+    copy->code_ = oat_base_ + orig->GetOatCodeOffset();
   } else {
-    DCHECK(copy->native_method_ == NULL);
+    DCHECK(copy->native_method_ == NULL) << copy->native_method_;
+    if (orig->IsAbstract()) {
+        ByteArray* orig_ame_stub_array_ = Runtime::Current()->GetAbstractMethodErrorStubArray();
+        ByteArray* copy_ame_stub_array_ = down_cast<ByteArray*>(GetImageAddress(orig_ame_stub_array_));
+        copy->code_ = copy_ame_stub_array_->GetData();
+    } else {
+        copy->code_ = oat_base_ + orig->GetOatCodeOffset();
+    }
   }
 }
 
diff --git a/src/image_writer.h b/src/image_writer.h
index c1480ff..8c4b308 100644
--- a/src/image_writer.h
+++ b/src/image_writer.h
@@ -10,6 +10,7 @@
 #include "UniquePtr.h"
 #include "dex_cache.h"
 #include "mem_map.h"
+#include "oat_file.h"
 #include "object.h"
 #include "os.h"
 #include "space.h"
@@ -21,7 +22,8 @@
 
  public:
   ImageWriter() : source_space_(NULL), image_top_(0), image_base_(NULL) {};
-  bool Write(const char* filename, uintptr_t image_base);
+  bool Write(const char* image_filename, uintptr_t image_base,
+             const std::string& oat_filename, const std::string& strip_location_prefix);
   ~ImageWriter() {};
 
  private:
@@ -82,6 +84,7 @@
   }
 
   void CalculateNewObjectOffsets();
+  ObjectArray<Object>* CreateImageRoots() const;
   static void CalculateNewObjectOffsetsCallback(Object* obj, void* arg);
 
   void CopyAndFixupObjects();
@@ -97,6 +100,9 @@
   void FixupDexCaches();
   void FixupDexCache(const DexCache* orig, DexCache* copy);
 
+  // oat file with code for this image
+  UniquePtr<OatFile> oat_file_;
+
   // Space we are writing objects from
   const Space* source_space_;
 
@@ -106,9 +112,12 @@
   // Offset to the free space in image_
   size_t image_top_;
 
-  // Target base address for the output image
+  // Target image base address for the output image
   byte* image_base_;
 
+  // Target oat base address for the pointers from the output image to its oat file
+  byte* oat_base_;
+
   // DexCaches seen while scanning for fixing up CodeAndDirectMethods
   typedef std::tr1::unordered_set<DexCache*, DexCacheHash> Set;
   Set dex_caches_;
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index 6c09f58..c8cfc66 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -430,7 +430,7 @@
   CHECK(managed_code != NULL);
   MemoryRegion code(managed_code->GetData(), managed_code->GetLength());
   __ FinalizeInstructions(code);
-  native_method->SetCode(managed_code, instruction_set_);
+  native_method->SetCodeArray(managed_code, instruction_set_);
   native_method->SetFrameSizeInBytes(frame_size);
   native_method->SetReturnPcOffsetInBytes(jni_conv->ReturnPcOffset());
   native_method->SetCoreSpillMask(jni_conv->CoreSpillMask());
diff --git a/src/mem_map.cc b/src/mem_map.cc
index e44a894..2bd7bd0 100644
--- a/src/mem_map.cc
+++ b/src/mem_map.cc
@@ -87,9 +87,9 @@
     std::string end_str = maps.substr(i+1+8, 8);
     uint32_t start = ParseHex(start_str);
     uint32_t end = ParseHex(end_str);
-    CHECK(!(base >= start && base < end)
-          && !(limit >= start && limit < end)
-          && !(base <= start && limit > end))
+    CHECK(!(base >= start && base < end)       // start of new within old
+          && !(limit > start && limit < end)  // end of new within old
+          && !(base <= start && limit > end))  // start/end of new includes all of old
         << StringPrintf("Requested region %08x-%08x overlaps with existing map %08x-%08x\n",
                         base, limit, start, end)
         << maps;
diff --git a/src/oat.cc b/src/oat.cc
new file mode 100644
index 0000000..191f26f
--- /dev/null
+++ b/src/oat.cc
@@ -0,0 +1,68 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "oat.h"
+
+#include <zlib.h>
+
+namespace art {
+
+const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '0', '1', '\0' };
+
+OatHeader::OatHeader(const std::vector<const DexFile*>* dex_files) {
+  memcpy(magic_, kOatMagic, sizeof(kOatMagic));
+  memcpy(version_, kOatVersion, sizeof(kOatVersion));
+  adler32_checksum_ = adler32(0L, Z_NULL, 0);
+  dex_file_count_ = dex_files->size();
+  UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_));
+  executable_offset_ = 0;
+}
+
+bool OatHeader::IsValid() const {
+  if (memcmp(magic_, kOatMagic, sizeof(kOatMagic) != 0)) {
+    return false;
+  }
+  if (memcmp(version_, kOatVersion, sizeof(kOatVersion) != 0)) {
+    return false;
+  }
+  return true;
+}
+
+const char* OatHeader::GetMagic() const {
+  CHECK(IsValid());
+  return reinterpret_cast<const char*>(magic_);
+}
+
+uint32_t OatHeader::GetDexFileCount() const {
+  DCHECK(IsValid());
+  return dex_file_count_;
+}
+
+uint32_t OatHeader::GetChecksum() const {
+  CHECK(IsValid());
+  return adler32_checksum_;
+}
+
+void OatHeader::UpdateChecksum(const void* data, size_t length) {
+  DCHECK(IsValid());
+  const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data);
+  adler32_checksum_ = adler32(adler32_checksum_, bytes, length);
+}
+
+uint32_t OatHeader::GetExecutableOffset() const {
+  DCHECK(IsValid());
+  DCHECK(IsAligned(executable_offset_, kPageSize));
+  CHECK_GT(executable_offset_, sizeof(OatHeader));
+  return executable_offset_;
+}
+
+void OatHeader::SetExecutableOffset(uint32_t executable_offset) {
+  DCHECK(IsAligned(executable_offset, kPageSize));
+  CHECK_GT(executable_offset, sizeof(OatHeader));
+  DCHECK(IsValid());
+  DCHECK_EQ(executable_offset_, 0U);
+  executable_offset_ = executable_offset;
+  UpdateChecksum(&executable_offset_, sizeof(executable_offset));
+}
+
+}  // namespace art
diff --git a/src/oat.h b/src/oat.h
new file mode 100644
index 0000000..ca3e9e0
--- /dev/null
+++ b/src/oat.h
@@ -0,0 +1,41 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_OAT_H_
+#define ART_SRC_OAT_H_
+
+#include <vector>
+
+#include "dex_file.h"
+#include "macros.h"
+
+namespace art {
+
+class PACKED OatHeader {
+ public:
+  OatHeader() {}
+  OatHeader(const std::vector<const DexFile*>* dex_files);
+
+  bool IsValid() const;
+  const char* GetMagic() const;
+  uint32_t GetChecksum() const;
+  void UpdateChecksum(const void* data, size_t length);
+  uint32_t GetDexFileCount() const;
+  uint32_t GetExecutableOffset() const;
+  void SetExecutableOffset(uint32_t executable_offset);
+
+ private:
+  static const uint8_t kOatMagic[4];
+  static const uint8_t kOatVersion[4];
+
+  uint8_t magic_[4];
+  uint8_t version_[4];
+  uint32_t adler32_checksum_;
+  uint32_t dex_file_count_;
+  uint32_t executable_offset_;
+
+  DISALLOW_COPY_AND_ASSIGN(OatHeader);
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_OAT_H_
diff --git a/src/oat_file.cc b/src/oat_file.cc
new file mode 100644
index 0000000..6266494
--- /dev/null
+++ b/src/oat_file.cc
@@ -0,0 +1,167 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "oat_file.h"
+
+#include <sys/mman.h>
+
+#include "file.h"
+#include "os.h"
+#include "stl_util.h"
+
+namespace art {
+
+OatFile* OatFile::Open(const std::string& filename,
+                       const std::string& strip_location_prefix,
+                       byte* requested_base) {
+  StringPiece location = filename;
+  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());
+
+  UniquePtr<OatFile> oat_file(new OatFile(location.ToString()));
+  bool success = oat_file->Read(filename, requested_base);
+  if (!success) {
+    return NULL;
+  }
+  return oat_file.release();
+}
+
+OatFile::OatFile(const std::string& filename) : location_(filename) {}
+
+OatFile::~OatFile() {
+  STLDeleteValues(&oat_dex_files_);
+}
+
+bool OatFile::Read(const std::string& filename, byte* requested_base) {
+  UniquePtr<File> file(OS::OpenFile(filename.c_str(), false));
+  if (file.get() == NULL) {
+    return false;
+  }
+
+  OatHeader oat_header;
+  bool success = file->ReadFully(&oat_header, sizeof(oat_header));
+  if (!success || !oat_header.IsValid()) {
+    LOG(WARNING) << "Invalid oat header " << filename;
+    return false;
+  }
+
+  UniquePtr<MemMap> map(MemMap::Map(requested_base,
+                                    file->Length(),
+                                    PROT_READ,
+                                    MAP_PRIVATE | ((requested_base != NULL) ? MAP_FIXED : 0),
+                                    file->Fd(),
+                                    0));
+  if (map.get() == NULL) {
+    LOG(WARNING) << "Failed to map oat file " << filename;
+    return false;
+  }
+  CHECK(requested_base == 0 || requested_base == map->GetAddress()) << map->GetAddress();
+  DCHECK_EQ(0, memcmp(&oat_header, map->GetAddress(), sizeof(OatHeader)));
+
+  off_t code_offset = oat_header.GetExecutableOffset();
+  if (code_offset < file->Length()) {
+    byte* code_address = map->GetAddress() + code_offset;
+    size_t code_length = file->Length() - code_offset;
+    if (mprotect(code_address, code_length, PROT_READ | PROT_EXEC) != 0) {
+      PLOG(ERROR) << "Failed to make oat code executable.";
+      return false;
+    }
+  } else {
+    // its possible to have no code if all the methods were abstract, native, etc
+    DCHECK_EQ(code_offset, RoundUp(file->Length(), kPageSize));
+  }
+
+  const byte* oat = map->GetAddress();
+  oat += sizeof(OatHeader);
+  CHECK_LT(oat, map->GetLimit());
+  for (size_t i = 0; i < oat_header.GetDexFileCount(); i++) {
+    size_t dex_file_location_size = *reinterpret_cast<const uint32_t*>(oat);
+    oat += sizeof(dex_file_location_size);
+    CHECK_LT(oat, map->GetLimit());
+
+    const char* dex_file_location_data = reinterpret_cast<const char*>(oat);
+    oat += dex_file_location_size;
+    CHECK_LT(oat, map->GetLimit());
+
+    std::string dex_file_location(dex_file_location_data, dex_file_location_size);
+
+    uint32_t dex_file_checksum = *reinterpret_cast<const uint32_t*>(oat);
+    oat += sizeof(dex_file_checksum);
+    CHECK_LT(oat, map->GetLimit());
+
+    uint32_t classes_offset = *reinterpret_cast<const uint32_t*>(oat);
+    CHECK_GT(classes_offset, 0U);
+    CHECK_LT(classes_offset, static_cast<uint32_t>(file->Length()));
+    oat += sizeof(classes_offset);
+    CHECK_LT(oat, map->GetLimit());
+
+    uint32_t* classes_pointer = reinterpret_cast<uint32_t*>(map->GetAddress() + classes_offset);
+
+    oat_dex_files_[dex_file_location] = new OatDexFile(this,
+                                                       dex_file_location,
+                                                       dex_file_checksum,
+                                                       classes_pointer);
+  }
+
+  mem_map_.reset(map.release());
+  return true;
+}
+
+const OatHeader& OatFile::GetOatHeader() const {
+  return *reinterpret_cast<const OatHeader*>(GetBase());
+}
+
+const byte* OatFile::GetBase() const {
+  CHECK(mem_map_->GetAddress() != NULL);
+  return mem_map_->GetAddress();
+}
+
+const byte* OatFile::GetLimit() const {
+  CHECK(mem_map_->GetLimit() != NULL);
+  return mem_map_->GetLimit();
+}
+
+const OatFile::OatDexFile& OatFile::GetOatDexFile(const DexFile& dex_file) {
+  Table::const_iterator it = oat_dex_files_.find(dex_file.GetLocation());
+  if (it == oat_dex_files_.end()) {
+    LOG(FATAL) << "Failed to find OatDexFile for DexFile " << dex_file.GetLocation();
+  }
+  return *it->second;
+}
+
+OatFile::OatDexFile::OatDexFile(const OatFile* oat_file,
+                                std::string dex_file_location,
+                                uint32_t dex_file_checksum,
+                                uint32_t* classes_pointer)
+    : oat_file_(oat_file),
+      dex_file_location_(dex_file_location),
+      dex_file_checksum_(dex_file_checksum),
+      classes_pointer_(classes_pointer) {}
+
+OatFile::OatDexFile::~OatDexFile() {}
+
+const OatFile::OatClass OatFile::OatDexFile::GetOatClass(uint32_t class_def_index) const {
+  uint32_t methods_offset = classes_pointer_[class_def_index];
+  const byte* methods_pointer = oat_file_->GetBase() + methods_offset;
+  CHECK_LT(methods_pointer, oat_file_->GetLimit());
+  return OatClass(oat_file_, reinterpret_cast<const uint32_t*>(methods_pointer));
+}
+
+OatFile::OatClass::OatClass(const OatFile* oat_file, const uint32_t* methods_pointer)
+    : oat_file_(oat_file), methods_pointer_(methods_pointer) {}
+
+OatFile::OatClass::~OatClass() {}
+
+const void* OatFile::OatClass::GetMethodCode(uint32_t method_index) const {
+  uint32_t code_offset = methods_pointer_[method_index];
+  if (code_offset == 0) {
+    return NULL;
+  }
+  const void* code_pointer = reinterpret_cast<const void*>(oat_file_->GetBase() + code_offset);
+  CHECK_LT(code_pointer, oat_file_->GetLimit());
+  return code_pointer;
+}
+
+}  // namespace art
diff --git a/src/oat_file.h b/src/oat_file.h
new file mode 100644
index 0000000..2528a6c
--- /dev/null
+++ b/src/oat_file.h
@@ -0,0 +1,101 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_OAT_FILE_H_
+#define ART_SRC_OAT_FILE_H_
+
+#include <vector>
+
+#include "dex_file.h"
+#include "mem_map.h"
+#include "oat.h"
+
+namespace art {
+
+class OatFile {
+ public:
+
+  // Open an oat file. Returns NULL on failure.  Requested base can
+  // optionally be used to request where the file should be loaded.
+  static OatFile* Open(const std::string& filename,
+                       const std::string& strip_location_prefix,
+                       byte* requested_base);
+
+  ~OatFile();
+
+  const std::string& GetLocation() const {
+    return location_;
+  }
+
+  const OatHeader& GetOatHeader() const;
+
+  class OatDexFile;
+
+  class OatClass {
+   public:
+    // get the code for the method based on its index into the class
+    // defintion. direct methods come first, followed by virtual
+    // methods. note that runtime created methods such as miranda
+    // methods are not included.
+    const void* GetMethodCode(uint32_t method_index) const;
+    ~OatClass();
+
+   private:
+    OatClass(const OatFile* oat_file, const uint32_t* methods_pointer);
+
+    const OatFile* oat_file_;
+    const uint32_t* methods_pointer_;
+
+    friend class OatDexFile;
+  };
+
+  class OatDexFile {
+   public:
+    const OatClass GetOatClass(uint32_t class_def_index) const;
+    ~OatDexFile();
+   private:
+    OatDexFile(const OatFile* oat_file,
+               std::string dex_file_location,
+               uint32_t dex_file_checksum,
+               uint32_t* classes_pointer);
+
+    const OatFile* oat_file_;
+    std::string dex_file_location_;
+    uint32_t dex_file_checksum_;
+    const uint32_t* classes_pointer_;
+
+    friend class OatFile;
+    DISALLOW_COPY_AND_ASSIGN(OatDexFile);
+  };
+
+  const OatDexFile& GetOatDexFile(const DexFile& dex_file);
+
+  size_t GetSize() const {
+    return GetLimit() - GetBase();
+  }
+
+ private:
+  OatFile(const std::string& filename);
+  bool Read(const std::string& filename, byte* requested_base);
+
+  const byte* GetBase() const;
+  const byte* GetLimit() const;
+
+  // The oat file name.
+  //
+  // The image will embed this to link its associated oat file.
+  const std::string location_;
+
+  // backing memory map for oat file
+  UniquePtr<MemMap> mem_map_;
+
+  typedef std::map<std::string, const OatDexFile*> Table;
+  Table oat_dex_files_;
+
+  friend class OatClass;
+  friend class OatDexFile;
+  DISALLOW_COPY_AND_ASSIGN(OatFile);
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_OAT_WRITER_H_
diff --git a/src/oat_test.cc b/src/oat_test.cc
new file mode 100644
index 0000000..b1453b3
--- /dev/null
+++ b/src/oat_test.cc
@@ -0,0 +1,79 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "oat_file.h"
+#include "oat_writer.h"
+
+#include "common_test.h"
+
+namespace art {
+
+class OatTest : public CommonTest {};
+
+TEST_F(OatTest, WriteRead) {
+  const bool compile = false;  // DISABLED_ due to the time to compile libcore
+
+  const ClassLoader* class_loader = NULL;
+  if (compile) {
+    compiler_->CompileAll(class_loader);
+  }
+
+  ScratchFile tmp;
+  bool success = OatWriter::Create(tmp.GetFilename(), class_loader);
+  ASSERT_TRUE(success);
+
+  if (compile) {  // OatWriter strips the code, regenerate to compare
+    compiler_->CompileAll(class_loader);
+  }
+  UniquePtr<OatFile> oat_file(OatFile::Open(std::string(tmp.GetFilename()), "", NULL));
+  ASSERT_TRUE(oat_file.get() != NULL);
+  const OatHeader& oat_header = oat_file->GetOatHeader();
+  ASSERT_EQ(1U, oat_header.GetDexFileCount());
+
+  const Runtime* runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  ByteArray* jni_stub_array = runtime->GetJniStubArray();
+  ByteArray* ame_stub_array = runtime->GetAbstractMethodErrorStubArray();
+
+  const DexFile& dex_file = *java_lang_dex_file_.get();
+  const OatFile::OatDexFile& oat_dex_file = oat_file->GetOatDexFile(dex_file);
+  for (size_t i = 0; i < dex_file.NumClassDefs(); i++) {
+    const DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
+    const byte* class_data = dex_file.GetClassData(class_def);
+    DexFile::ClassDataHeader header = dex_file.ReadClassDataHeader(&class_data);
+    size_t num_virtual_methods = header.virtual_methods_size_;
+    const char* descriptor = dex_file.GetClassDescriptor(class_def);
+
+    const OatFile::OatClass oat_class = oat_dex_file.GetOatClass(i);
+
+    Class* klass = class_linker->FindClass(descriptor, class_loader);
+
+    size_t method_index = 0;
+    for (size_t i = 0; i < klass->NumDirectMethods(); i++, method_index++) {
+      Method* method = klass->GetDirectMethod(i);
+      const void* oat_code = oat_class.GetMethodCode(method_index);
+      uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(oat_code), 2);
+      oat_code = reinterpret_cast<const void*>(oat_code_aligned);
+      const ByteArray* code_array = method->GetCodeArray();
+      if (code_array == NULL || code_array == jni_stub_array || code_array == ame_stub_array) {
+        ASSERT_TRUE(oat_code == NULL);
+      } else {
+        ASSERT_EQ(0, memcmp(oat_code, code_array->GetData(), code_array->GetLength()));
+      }
+    }
+    for (size_t i = 0; i < num_virtual_methods; i++, method_index++) {
+      Method* method = klass->GetVirtualMethod(i);
+      const void* oat_code = oat_class.GetMethodCode(method_index);
+      uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(oat_code), 2);
+      oat_code = reinterpret_cast<const void*>(oat_code_aligned);
+      const ByteArray* code_array = method->GetCodeArray();
+      if (code_array == NULL || code_array == jni_stub_array || code_array == ame_stub_array) {
+        ASSERT_TRUE(oat_code == NULL);
+      } else {
+        ASSERT_TRUE(oat_code != NULL);
+        ASSERT_EQ(0, memcmp(oat_code, code_array->GetData(), code_array->GetLength()));
+      }
+    }
+  }
+}
+
+}  // namespace art
diff --git a/src/oat_writer.cc b/src/oat_writer.cc
new file mode 100644
index 0000000..b8232d9
--- /dev/null
+++ b/src/oat_writer.cc
@@ -0,0 +1,438 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "oat_writer.h"
+
+#include "class_linker.h"
+#include "class_loader.h"
+#include "file.h"
+#include "os.h"
+#include "stl_util.h"
+
+namespace art {
+
+bool OatWriter::Create(const std::string& filename, const ClassLoader* class_loader) {
+  const std::vector<const DexFile*>& dex_files = ClassLoader::GetClassPath(class_loader);
+  OatWriter oat_writer(dex_files, class_loader);
+  return oat_writer.Write(filename);
+}
+
+OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, const ClassLoader* class_loader) {
+  class_loader_ = class_loader;
+  dex_files_ = &dex_files;
+
+  size_t offset = InitOatHeader();
+  offset = InitOatDexFiles(offset);
+  offset = InitOatClasses(offset);
+  offset = InitOatMethods(offset);
+  offset = InitOatCode(offset);
+  offset = InitOatCodeDexFiles(offset);
+
+  CHECK_EQ(dex_files_->size(), oat_dex_files_.size());
+  CHECK_EQ(dex_files_->size(), oat_classes_.size());
+}
+
+size_t OatWriter::InitOatHeader() {
+  // create the OatHeader
+  oat_header_ = new OatHeader(dex_files_);
+  size_t offset = sizeof(*oat_header_);
+  return offset;
+}
+
+size_t OatWriter::InitOatDexFiles(size_t offset) {
+  // create the OatDexFiles
+  for (size_t i = 0; i != dex_files_->size(); ++i) {
+    const DexFile* dex_file = (*dex_files_)[i];
+    CHECK(dex_file != NULL);
+    OatDexFile* oat_dex_file = new OatDexFile(*dex_file);
+    oat_dex_files_.push_back(oat_dex_file);
+    offset += oat_dex_file->SizeOf();
+  }
+  return offset;
+}
+
+size_t OatWriter::InitOatClasses(size_t offset) {
+  // create the OatClasses
+  // calculate the offsets within OatDexFiles to OatClasses
+  for (size_t i = 0; i != dex_files_->size(); ++i) {
+    // set offset in OatDexFile to OatClasses
+    oat_dex_files_[i]->classes_offset_ = offset;
+    oat_dex_files_[i]->UpdateChecksum(*oat_header_);
+
+    const DexFile* dex_file = (*dex_files_)[i];
+    OatClasses* oat_classes = new OatClasses(*dex_file);
+    oat_classes_.push_back(oat_classes);
+    offset += oat_classes->SizeOf();
+  }
+  return offset;
+}
+
+size_t OatWriter::InitOatMethods(size_t offset) {
+  // create the OatMethods
+  // calculate the offsets within OatClasses to OatMethods
+  size_t class_index = 0;
+  for (size_t i = 0; i != dex_files_->size(); ++i) {
+    const DexFile* dex_file = (*dex_files_)[i];
+    for (size_t class_def_index = 0;
+         class_def_index < dex_file->NumClassDefs();
+         class_def_index++, class_index++) {
+      oat_classes_[i]->methods_offsets_[class_def_index] = offset;
+      const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+      const byte* class_data = dex_file->GetClassData(class_def);
+      DexFile::ClassDataHeader header = dex_file->ReadClassDataHeader(&class_data);
+      size_t num_direct_methods = header.direct_methods_size_;
+      size_t num_virtual_methods = header.virtual_methods_size_;
+      uint32_t num_methods = num_direct_methods + num_virtual_methods;
+      OatMethods* oat_methods = new OatMethods(num_methods);
+      oat_methods_.push_back(oat_methods);
+      offset += oat_methods->SizeOf();
+    }
+    oat_classes_[i]->UpdateChecksum(*oat_header_);
+  }
+  return offset;
+}
+
+size_t OatWriter::InitOatCode(size_t offset) {
+  // calculate the offsets within OatHeader to executable code
+  size_t old_offset = offset;
+  // required to be on a new page boundary
+  offset = RoundUp(offset, kPageSize);
+  oat_header_->SetExecutableOffset(offset);
+  executable_offset_padding_length_ = offset - old_offset;
+  return offset;
+}
+
+size_t OatWriter::InitOatCodeDexFiles(size_t offset) {
+  // calculate the offsets within OatMethods
+  size_t oat_class_index = 0;
+  for (size_t i = 0; i != dex_files_->size(); ++i) {
+    const DexFile* dex_file = (*dex_files_)[i];
+    CHECK(dex_file != NULL);
+    offset = InitOatCodeDexFile(offset, oat_class_index, *dex_file);
+  }
+  return offset;
+}
+
+size_t OatWriter::InitOatCodeDexFile(size_t offset,
+                                     size_t& oat_class_index,
+                                     const DexFile& dex_file) {
+  for (size_t class_def_index = 0;
+       class_def_index < dex_file.NumClassDefs();
+       class_def_index++, oat_class_index++) {
+    const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+    offset = InitOatCodeClassDef(offset, oat_class_index, dex_file, class_def);
+    oat_methods_[oat_class_index]->UpdateChecksum(*oat_header_);
+  }
+  return offset;
+}
+
+size_t OatWriter::InitOatCodeClassDef(size_t offset,
+                                      size_t oat_class_index,
+                                      const DexFile& dex_file,
+                                      const DexFile::ClassDef& class_def) {
+  const byte* class_data = dex_file.GetClassData(class_def);
+  DexFile::ClassDataHeader header = dex_file.ReadClassDataHeader(&class_data);
+  size_t num_virtual_methods = header.virtual_methods_size_;
+  const char* descriptor = dex_file.GetClassDescriptor(class_def);
+
+  // TODO: remove code ByteArrays from Class/Method (and therefore ClassLoader)
+  // TODO: don't write code for shared stubs
+  Class* klass = Runtime::Current()->GetClassLinker()->FindClass(descriptor, class_loader_);
+  CHECK(klass != NULL) << descriptor;
+  CHECK_EQ(klass->GetClassLoader(), class_loader_);
+  CHECK_EQ(oat_methods_[oat_class_index]->method_offsets_.size(),
+           klass->NumDirectMethods() + num_virtual_methods);
+  // Note that we leave the offset to the code in Method::code_
+  size_t class_def_method_index = 0;
+  for (size_t i = 0; i < klass->NumDirectMethods(); i++, class_def_method_index++) {
+    Method* method = klass->GetDirectMethod(i);
+    CHECK(method != NULL) << descriptor << " direct " << i;
+    offset = InitOatCodeMethod(offset, oat_class_index, class_def_method_index, method);
+  }
+  // note that num_virtual_methods != klass->NumVirtualMethods() because of miranda methods
+  for (size_t i = 0; i < num_virtual_methods; i++, class_def_method_index++) {
+    Method* method = klass->GetVirtualMethod(i);
+    CHECK(method != NULL) << descriptor << " virtual " << i;
+    offset = InitOatCodeMethod(offset, oat_class_index, class_def_method_index, method);
+  }
+  return offset;
+}
+
+size_t OatWriter::InitOatCodeMethod(size_t offset,
+                                    size_t oat_class_index,
+                                    size_t class_def_method_index,
+                                    Method* method) {
+  Runtime* runtime = Runtime::Current();
+  ByteArray* jni_stub_array = runtime->GetJniStubArray();
+  ByteArray* ame_stub_array = runtime->GetAbstractMethodErrorStubArray();
+
+  const ByteArray* code_array = method->GetCodeArray();
+  if (code_array == NULL || code_array == jni_stub_array || code_array == ame_stub_array) {
+    oat_methods_[oat_class_index]->method_offsets_[class_def_method_index] = 0;
+    method->SetOatCodeOffset(0);
+  } else {
+    offset = RoundUp(offset, kArmAlignment);
+    uint32_t thumb_offset = (reinterpret_cast<const int8_t*>(method->GetCode())
+                             - code_array->GetData());
+    uint32_t code_offset = offset + thumb_offset;
+    oat_methods_[oat_class_index]->method_offsets_[class_def_method_index] = code_offset;
+    method->SetOatCodeOffset(code_offset);
+    offset += code_array->GetLength();
+    oat_header_->UpdateChecksum(code_array->GetData(), code_array->GetLength());
+  }
+  return offset;
+}
+
+bool OatWriter::Write(const std::string& filename) {
+
+  UniquePtr<File> file(OS::OpenFile(filename.c_str(), true));
+  if (file.get() == NULL) {
+    return false;
+  }
+
+  if (!file->WriteFully(oat_header_, sizeof(*oat_header_))) {
+    PLOG(ERROR) << "Failed to write oat header to " << filename;
+    return false;
+  }
+
+  if (!WriteTables(file.get())) {
+    LOG(ERROR) << "Failed to write oat tables to " << filename;
+    return false;
+  }
+
+  size_t code_offset = WriteCode(file.get());
+  if (code_offset == 0) {
+    LOG(ERROR) << "Failed to write oat code to " << filename;
+    return false;
+  }
+
+  code_offset = WriteCodeDexFiles(file.get(), code_offset);
+  if (code_offset == 0) {
+    LOG(ERROR) << "Failed to write oat code for dex files to " << filename;
+    return false;
+  }
+
+  return true;
+}
+
+bool OatWriter::WriteTables(File* file) {
+  for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
+    if (!oat_dex_files_[i]->Write(file)) {
+      PLOG(ERROR) << "Failed to write oat dex information";
+      return false;
+    }
+  }
+  for (size_t i = 0; i != oat_classes_.size(); ++i) {
+    if (!oat_classes_[i]->Write(file)) {
+      PLOG(ERROR) << "Failed to write oat classes information";
+      return false;
+    }
+  }
+  for (size_t i = 0; i != oat_methods_.size(); ++i) {
+    if (!oat_methods_[i]->Write(file)) {
+      PLOG(ERROR) << "Failed to write oat methods information";
+      return false;
+    }
+  }
+  return true;
+}
+
+size_t OatWriter::WriteCode(File* file) {
+  uint32_t code_offset = oat_header_->GetExecutableOffset();
+  off_t new_offset = lseek(file->Fd(), executable_offset_padding_length_, SEEK_CUR);
+  if (static_cast<uint32_t>(new_offset) != code_offset) {
+    PLOG(ERROR) << "Failed to seek to oat code section";
+    return 0;
+  }
+  return code_offset;
+}
+
+size_t OatWriter::WriteCodeDexFiles(File* file, size_t code_offset) {
+  for (size_t i = 0; i != oat_classes_.size(); ++i) {
+    const DexFile* dex_file = (*dex_files_)[i];
+    CHECK(dex_file != NULL);
+    code_offset = WriteCodeDexFile(file, code_offset, *dex_file);
+    if (code_offset == 0) {
+      return 0;
+    }
+  }
+  return code_offset;
+}
+
+size_t OatWriter::WriteCodeDexFile(File* file,
+                                   size_t code_offset,
+                                   const DexFile& dex_file) {
+  for (size_t class_def_index = 0;
+       class_def_index < dex_file.NumClassDefs();
+       class_def_index++) {
+    const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+    code_offset = WriteCodeClassDef(file, code_offset, dex_file, class_def);
+    if (code_offset == 0) {
+      return 0;
+    }
+  }
+  return code_offset;
+}
+
+size_t OatWriter::WriteCodeClassDef(File* file,
+                                    size_t code_offset,
+                                    const DexFile& dex_file,
+                                    const DexFile::ClassDef& class_def) {
+  const Runtime* runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  ByteArray* ame_stub_array = runtime->GetAbstractMethodErrorStubArray();
+
+  const byte* class_data = dex_file.GetClassData(class_def);
+  DexFile::ClassDataHeader header = dex_file.ReadClassDataHeader(&class_data);
+  size_t num_virtual_methods = header.virtual_methods_size_;
+  const char* descriptor = dex_file.GetClassDescriptor(class_def);
+  Class* klass = class_linker->FindClass(descriptor, class_loader_);
+
+  // TODO: deduplicate code arrays
+  // Note that we clear the code array here, image_writer will use GetCodeOffset to find it
+  for (size_t i = 0; i < klass->NumDirectMethods(); i++) {
+    Method* method = klass->GetDirectMethod(i);
+    code_offset = WriteCodeMethod(file, code_offset, method);
+    if (code_offset == 0) {
+      return 0;
+    }
+  }
+  // note that num_virtual_methods != klass->NumVirtualMethods() because of miranda methods
+  for (size_t i = 0; i < num_virtual_methods; i++) {
+    Method* method = klass->GetVirtualMethod(i);
+    code_offset = WriteCodeMethod(file, code_offset, method);
+    if (code_offset == 0) {
+      return 0;
+    }
+  }
+  for (size_t i = num_virtual_methods; i < klass->NumVirtualMethods(); i++) {
+    Method* method = klass->GetVirtualMethod(i);
+    const ByteArray* code_array = method->GetCodeArray();
+    CHECK(code_array == NULL  // if compiler not run
+          || code_array == ame_stub_array)  // otherwise
+            << PrettyMethod(method) << " " << code_array;
+    method->SetCodeArray(NULL, kNone);
+  }
+  return code_offset;
+}
+
+size_t OatWriter::WriteCodeMethod(File* file,
+                                  size_t code_offset,
+                                  Method* method) {
+  const Runtime* runtime = Runtime::Current();
+  ByteArray* jni_stub_array = runtime->GetJniStubArray();
+  ByteArray* ame_stub_array = runtime->GetAbstractMethodErrorStubArray();
+
+  const ByteArray* code_array = method->GetCodeArray();
+  if (code_array != NULL && code_array != jni_stub_array && code_array != ame_stub_array) {
+    uint32_t aligned_code_offset = RoundUp(code_offset, kArmAlignment);
+    uint32_t aligned_code_delta = aligned_code_offset - code_offset;
+    if (aligned_code_delta != 0) {
+      off_t new_offset = lseek(file->Fd(), aligned_code_delta, SEEK_CUR);
+      if (static_cast<uint32_t>(new_offset) != aligned_code_offset) {
+        PLOG(ERROR) << "Failed to seek to align oat code";
+        return false;
+      }
+      code_offset += aligned_code_delta;
+    }
+    if (!file->WriteFully(code_array->GetData(), code_array->GetLength())) {
+      PLOG(ERROR) << "Failed to write method code for " << PrettyMethod(method);
+      return false;
+    }
+    code_offset += code_array->GetLength();
+  }
+  // preserve code offset around code clearing
+  uint32_t offset = method->GetOatCodeOffset();
+  method->SetCodeArray(NULL, kNone);
+  method->SetOatCodeOffset(offset);
+  return code_offset;
+}
+
+OatWriter::~OatWriter() {
+  delete oat_header_;
+  STLDeleteElements(&oat_dex_files_);
+  STLDeleteElements(&oat_classes_);
+  STLDeleteElements(&oat_methods_);
+}
+
+OatWriter::OatDexFile::OatDexFile(const DexFile& dex_file) {
+  const std::string& location = dex_file.GetLocation();
+  dex_file_location_size_ = location.size();
+  dex_file_location_data_ = reinterpret_cast<const uint8_t*>(location.data());
+  dex_file_checksum_ = dex_file.GetHeader().checksum_;
+}
+
+size_t OatWriter::OatDexFile::SizeOf() const {
+  return sizeof(dex_file_location_size_)
+          + dex_file_location_size_
+          + sizeof(dex_file_checksum_)
+          + sizeof(classes_offset_);
+}
+
+void OatWriter::OatDexFile::UpdateChecksum(OatHeader& oat_header) const {
+  oat_header.UpdateChecksum(&dex_file_location_size_, sizeof(dex_file_location_size_));
+  oat_header.UpdateChecksum(dex_file_location_data_, dex_file_location_size_);
+  oat_header.UpdateChecksum(&dex_file_checksum_, sizeof(dex_file_checksum_));
+  oat_header.UpdateChecksum(&classes_offset_, sizeof(classes_offset_));
+}
+
+bool OatWriter::OatDexFile::Write(File* file) const {
+  if (!file->WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) {
+    PLOG(ERROR) << "Failed to write dex file location length";
+    return false;
+  }
+  if (!file->WriteFully(dex_file_location_data_, dex_file_location_size_)) {
+    PLOG(ERROR) << "Failed to write dex file location data";
+    return false;
+  }
+  if (!file->WriteFully(&dex_file_checksum_, sizeof(dex_file_checksum_))) {
+    PLOG(ERROR) << "Failed to write dex file checksum";
+    return false;
+  }
+  if (!file->WriteFully(&classes_offset_, sizeof(classes_offset_))) {
+    PLOG(ERROR) << "Failed to write classes offset";
+    return false;
+  }
+  return true;
+}
+
+OatWriter::OatClasses::OatClasses(const DexFile& dex_file) {
+  methods_offsets_.resize(dex_file.NumClassDefs());
+}
+
+size_t OatWriter::OatClasses::SizeOf() const {
+  return (sizeof(methods_offsets_[0]) * methods_offsets_.size());
+}
+
+void OatWriter::OatClasses::UpdateChecksum(OatHeader& oat_header) const {
+  oat_header.UpdateChecksum(&methods_offsets_[0], SizeOf());
+}
+
+bool OatWriter::OatClasses::Write(File* file) const {
+  if (!file->WriteFully(&methods_offsets_[0], SizeOf())) {
+    PLOG(ERROR) << "Failed to methods offsets";
+    return false;
+  }
+  return true;
+}
+
+OatWriter::OatMethods::OatMethods(uint32_t methods_count) {
+  method_offsets_.resize(methods_count);
+}
+
+size_t OatWriter::OatMethods::SizeOf() const {
+  return (sizeof(method_offsets_[0]) * method_offsets_.size());
+}
+
+void OatWriter::OatMethods::UpdateChecksum(OatHeader& oat_header) const {
+  oat_header.UpdateChecksum(&method_offsets_[0], SizeOf());
+}
+
+bool OatWriter::OatMethods::Write(File* file) const {
+  if (!file->WriteFully(&method_offsets_[0], SizeOf())) {
+    PLOG(ERROR) << "Failed to method offsets";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace art
diff --git a/src/oat_writer.h b/src/oat_writer.h
new file mode 100644
index 0000000..7cef892
--- /dev/null
+++ b/src/oat_writer.h
@@ -0,0 +1,155 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_OAT_WRITER_H_
+#define ART_SRC_OAT_WRITER_H_
+
+#include <stdint.h>
+
+#include <cstddef>
+
+#include "UniquePtr.h"
+#include "dex_cache.h"
+#include "mem_map.h"
+#include "oat.h"
+#include "object.h"
+#include "os.h"
+#include "space.h"
+
+namespace art {
+
+// OatHeader         fixed length with count of D OatDexFiles
+//
+// OatDexFile[0]     each fixed length with offset to variable sized OatClasses
+// OatDexFile[1]
+// ...
+// OatDexFile[D]
+//
+// OatClasses[0]     one variable sized OatClasses for each OatDexFile
+// OatClasses[1]     contains DexFile::NumClassDefs offsets to OatMethods for each ClassDef
+// ...
+// OatClasses[D]
+//
+// OatMethods[0]     one variable sized OatMethods for each of C DexFile::ClassDefs
+// OatMethods[1]     contains offsets to code
+// ...
+// OatMethods[C]
+//
+// padding           if necessary so that the follow code will be page aligned
+//
+// Method::GetCode() one variable sized code blob for each Method::GetCode() value
+// Method::GetCode()
+// Method::GetCode()
+// Method::GetCode()
+// Method::GetCode()
+// Method::GetCode()
+// ...
+// Method::GetCode()
+//
+class OatWriter {
+ public:
+  // Write an oat file. Returns true on success, false on failure.
+  static bool Create(const std::string& filename, const ClassLoader* class_loader);
+
+ private:
+
+  OatWriter(const std::vector<const DexFile*>& dex_files, const ClassLoader* class_loader);
+  ~OatWriter();
+
+  size_t InitOatHeader();
+  size_t InitOatDexFiles(size_t offset);
+  size_t InitOatClasses(size_t offset);
+  size_t InitOatMethods(size_t offset);
+  size_t InitOatCode(size_t offset);
+  size_t InitOatCodeDexFiles(size_t offset);
+  size_t InitOatCodeDexFile(size_t offset,
+                            size_t& oat_class_index,
+                            const DexFile& dex_file);
+  size_t InitOatCodeClassDef(size_t offset,
+                             size_t oat_class_index,
+                             const DexFile& dex_file,
+                             const DexFile::ClassDef& class_def);
+  size_t InitOatCodeMethod(size_t offset,
+                           size_t oat_class_index,
+                           size_t class_def_method_index,
+                           Method* method);
+
+  bool Write(const std::string& filename);
+  bool WriteTables(File* file);
+  size_t WriteCode(File* file);
+  size_t WriteCodeDexFiles(File* file,
+                           size_t offset);
+  size_t WriteCodeDexFile(File* file,
+                          size_t offset,
+                          const DexFile& dex_file);
+  size_t WriteCodeClassDef(File* file,
+                           size_t offset,
+                           const DexFile& dex_file,
+                           const DexFile::ClassDef& class_def);
+  size_t WriteCodeMethod(File* file,
+                         size_t offset,
+                         Method* method);
+
+  class OatDexFile {
+   public:
+    OatDexFile(const DexFile& dex_file);
+    size_t SizeOf() const;
+    void UpdateChecksum(OatHeader& oat_header) const;
+    bool Write(File* file) const;
+
+    // data to write
+    uint32_t dex_file_location_size_;
+    const uint8_t* dex_file_location_data_;
+    uint32_t dex_file_checksum_;
+    uint32_t classes_offset_;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(OatDexFile);
+  };
+
+  class OatClasses {
+   public:
+    OatClasses(const DexFile& dex_file);
+    size_t SizeOf() const;
+    void UpdateChecksum(OatHeader& oat_header) const;
+    bool Write(File* file) const;
+
+    // data to write
+    std::vector<uint32_t> methods_offsets_;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(OatClasses);
+  };
+
+  class OatMethods {
+   public:
+    OatMethods(uint32_t methods_count);
+    size_t SizeOf() const;
+    void UpdateChecksum(OatHeader& oat_header) const;
+    bool Write(File* file) const;
+
+    // data to write
+    std::vector<uint32_t> method_offsets_;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(OatMethods);
+  };
+
+  // TODO: remove the ClassLoader when the code storage moves out of Method
+  const ClassLoader* class_loader_;
+
+  // note OatFile does not take ownership of the DexFiles
+  const std::vector<const DexFile*>* dex_files_;
+
+  // data to write
+  OatHeader* oat_header_;
+  std::vector<OatDexFile*> oat_dex_files_;
+  std::vector<OatClasses*> oat_classes_;
+  std::vector<OatMethods*> oat_methods_;
+  uint32_t executable_offset_padding_length_;
+
+  DISALLOW_COPY_AND_ASSIGN(OatWriter);
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_OAT_WRITER_H_
diff --git a/src/oatdump.cc b/src/oatdump.cc
index fe3cf61..bec8d10 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -22,24 +22,34 @@
 static void usage() {
   fprintf(stderr,
           "Usage: oatdump [options] ...\n"
-          "    Example: oatdump --dex-file=$ANDROID_PRODUCT_OUT/system/framework/core.jar --image=$ANDROID_PRODUCT_OUT/system/framework/boot.oat --strip-prefix=$ANDROID_PRODUCT_OUT\n"
-          "    Example: adb shell oatdump --dex-file=/system/framework/core.jar --image=/system/framework/boot.oat\n"
+          "    Example: oatdump --dex-file=$ANDROID_PRODUCT_OUT/system/framework/core.jar --oat=$ANDROID_PRODUCT_OUT/system/framework/boot.oat --image=$ANDROID_PRODUCT_OUT/system/framework/boot.art --strip-prefix=$ANDROID_PRODUCT_OUT\n"
+          "    Example: adb shell oatdump --dex-file=/system/framework/core.jar --oat=/system/framework/boot.oat --image=/system/framework/boot.art\n"
           "\n");
-  // TODO: remove this by making image contain boot DexFile information?
+  // TODO: remove this by inferring from --image or --oat
   fprintf(stderr,
           "  --dex-file=<dex-file>: specifies a .dex file location. At least one .dex\n"
           "      file must be specified. \n"
           "      Example: --dex-file=/system/framework/core.jar\n"
           "\n");
   fprintf(stderr,
-          "  --image=<file>: specifies the required input image filename.\n"
+          "  --image=<file.art>: specifies the required input image filename.\n"
+          "      Example: --image=/system/framework/boot.art\n"
+          "\n");
+  // TODO: remove this by inferring from --image
+  fprintf(stderr,
+          "  --oat=<file.oat>: specifies the required input oat filename.\n"
           "      Example: --image=/system/framework/boot.oat\n"
           "\n");
   fprintf(stderr,
-          "  --boot=<oat-file>: provide the oat file for the boot class path.\n"
-          "      Example: --boot=/system/framework/boot.oat\n"
+          "  --boot-image=<file.art>: provide the image file for the boot class path.\n"
+          "      Example: --boot-image=/system/framework/boot.art\n"
           "\n");
-  // TODO: remove this by making boot image contain boot DexFile information?
+  // TODO: remove this by inferring from --boot-image
+  fprintf(stderr,
+          "  --boot-oat=<file.oat>: provide the oat file for the boot class path.\n"
+          "      Example: --boot-oat=/system/framework/boot.oat\n"
+          "\n");
+  // TODO: remove this by inderring from --boot-image or --boot-oat
   fprintf(stderr,
           "  --boot-dex-file=<dex-file>: specifies a .dex file that is part of the boot\n"
           "       image specified with --boot. \n"
@@ -59,7 +69,9 @@
 
 const char* image_roots_descriptions_[] = {
   "kJniStubArray",
-  "kCalleeSaveMethod"
+  "kAbstractMethodErrorStubArray",
+  "kCalleeSaveMethod",
+  "kOatLocation",
 };
 
 class OatDump {
@@ -72,8 +84,11 @@
     os << "MAGIC:\n";
     os << image_header.GetMagic() << "\n\n";
 
-    os << "BASE:\n";
-    os << reinterpret_cast<void*>(image_header.GetBaseAddr()) << "\n\n";
+    os << "IMAGE BASE:\n";
+    os << reinterpret_cast<void*>(image_header.GetImageBaseAddr()) << "\n\n";
+
+    os << "OAT BASE:\n";
+    os << reinterpret_cast<void*>(image_header.GetOatBaseAddr()) << "\n\n";
 
     os << "ROOTS:\n";
     CHECK_EQ(arraysize(image_roots_descriptions_), size_t(ImageHeader::kImageRootsMax));
@@ -94,7 +109,10 @@
     os << "STATS:\n" << std::flush;
     UniquePtr<File> file(OS::OpenFile(image_filename.c_str(), false));
     state.stats_.file_bytes = file->Length();
-    state.stats_.header_bytes = sizeof(ImageHeader);
+    size_t header_bytes = sizeof(ImageHeader);
+    state.stats_.header_bytes = header_bytes;
+    size_t alignment_bytes = RoundUp(header_bytes, kObjectAlignment) - header_bytes;
+    state.stats_.alignment_bytes += alignment_bytes;
     state.stats_.Dump(os);
 
     os << std::flush;
@@ -167,6 +185,8 @@
           } else {
             state->stats_.managed_code_bytes += code_bytes;
           }
+        } else {
+          code_base = reinterpret_cast<const int8_t*>(method->GetCode());
         }
         StringAppendF(&summary, "\tCODE     %p-%p\n", code_base, code_limit);
 
@@ -351,7 +371,9 @@
 
   std::vector<const char*> dex_filenames;
   const char* image_filename = NULL;
+  const char* oat_filename = NULL;
   const char* boot_image_filename = NULL;
+  const char* boot_oat_filename = NULL;
   std::vector<const char*> boot_dex_filenames;
   std::string strip_location_prefix;
   std::ostream* os = &std::cout;
@@ -363,8 +385,12 @@
       dex_filenames.push_back(option.substr(strlen("--dex-file=")).data());
     } else if (option.starts_with("--image=")) {
       image_filename = option.substr(strlen("--image=")).data();
-    } else if (option.starts_with("--boot=")) {
-      boot_image_filename = option.substr(strlen("--boot=")).data();
+    } else if (option.starts_with("--oat=")) {
+      oat_filename = option.substr(strlen("--oat=")).data();
+    } else if (option.starts_with("--boot-image=")) {
+      boot_image_filename = option.substr(strlen("--boot-image=")).data();
+    } else if (option.starts_with("--boot-oat=")) {
+      boot_oat_filename = option.substr(strlen("--boot-oat=")).data();
     } 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=")) {
@@ -388,11 +414,21 @@
    return EXIT_FAILURE;
   }
 
+  if (oat_filename == NULL) {
+   fprintf(stderr, "--oat file name not specified\n");
+   return EXIT_FAILURE;
+  }
+
   if (dex_filenames.empty()) {
    fprintf(stderr, "no --dex-file values specified\n");
    return EXIT_FAILURE;
   }
 
+  if ((boot_image_filename != NULL) != (boot_oat_filename != NULL)) {
+   fprintf(stderr, "--boot-image and --boat-oat must be specified together or not at all\n");
+   return EXIT_FAILURE;
+  }
+
   if (boot_image_filename != NULL && boot_dex_filenames.empty()) {
     fprintf(stderr, "no --boot-dex-file values specified with --boot\n");
     return EXIT_FAILURE;
@@ -406,22 +442,31 @@
 
   Runtime::Options options;
   std::string image_option;
+  std::string oat_option;
   std::string boot_image_option;
+  std::string boot_oat_option;
   if (boot_image_filename == NULL) {
     // if we don't have multiple images, pass the main one as the boot to match dex2oat
     boot_image_filename = image_filename;
+    boot_oat_filename = oat_filename;
     boot_dex_files = dex_files;
     dex_files.clear();
   } else {
     image_option += "-Ximage:";
     image_option += image_filename;
+    oat_option += "-Xoat:";
+    oat_option += oat_filename;
     options.push_back(std::make_pair("classpath", &dex_files));
     options.push_back(std::make_pair(image_option.c_str(), reinterpret_cast<void*>(NULL)));
+    options.push_back(std::make_pair(oat_option.c_str(), reinterpret_cast<void*>(NULL)));
   }
   boot_image_option += "-Xbootimage:";
   boot_image_option += boot_image_filename;
+  boot_oat_option += "-Xbootoat:";
+  boot_oat_option += boot_oat_filename;
   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)));
+  options.push_back(std::make_pair(boot_oat_option.c_str(), reinterpret_cast<void*>(NULL)));
 
   UniquePtr<Runtime> runtime(Runtime::Create(options, false));
   if (runtime.get() == NULL) {
diff --git a/src/object.cc b/src/object.cc
index 4a6e649..8fc4108 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -623,7 +623,9 @@
   }
   size_t mapping_table_length = mapping_table->GetLength();
   uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(GetCode());
-  CHECK_LT(sought_offset, static_cast<uint32_t>(GetCodeArray()->GetLength()));
+  if (GetCodeArray() != NULL) {
+    CHECK_LT(sought_offset, static_cast<uint32_t>(GetCodeArray()->GetLength()));
+  }
   uint32_t best_offset = 0;
   uint32_t best_dex_offset = 0;
   for (size_t i = 0; i < mapping_table_length; i += 2) {
@@ -653,7 +655,9 @@
     uint32_t map_offset = mapping_table->Get(i);
     uint32_t map_dex_offset = mapping_table->Get(i + 1);
     if (map_dex_offset == dex_pc) {
-      DCHECK_LT(map_offset, static_cast<uint32_t>(GetCodeArray()->GetLength()));
+      if (GetCodeArray() != NULL) {
+        DCHECK_LT(map_offset, static_cast<uint32_t>(GetCodeArray()->GetLength()));
+      }
       return reinterpret_cast<uintptr_t>(GetCode()) + map_offset;
     }
   }
@@ -686,25 +690,27 @@
   return DexFile::kDexNoIndex;
 }
 
-void Method::SetCode(ByteArray* code_array, InstructionSet instruction_set,
-                     IntArray* mapping_table, ShortArray* vmap_table) {
+void Method::SetCodeArray(ByteArray* code_array, InstructionSet instruction_set) {
+// TODO: restore this check or warning when compile time code storage is moved out of Method
 //  CHECK(GetCode() == NULL || IsNative()) << PrettyMethod(this);
-  if (GetCode() != NULL && !IsNative()) {
-    LOG(WARNING) << "Calling SetCode more than once for " << PrettyMethod(this);
-  }
+//  if (GetCode() != NULL && !IsNative()) {
+//    LOG(WARNING) << "Calling SetCode more than once for " << PrettyMethod(this);
+//  }
   SetFieldPtr<ByteArray*>(OFFSET_OF_OBJECT_MEMBER(Method, code_array_), code_array, false);
-  SetFieldPtr<IntArray*>(OFFSET_OF_OBJECT_MEMBER(Method, mapping_table_),
-       mapping_table, false);
-  SetFieldPtr<ShortArray*>(OFFSET_OF_OBJECT_MEMBER(Method, vmap_table_),
-       vmap_table, false);
-  int8_t* code = code_array->GetData();
-  uintptr_t address = reinterpret_cast<uintptr_t>(code);
-  if (instruction_set == kThumb2) {
-    // Set the low-order bit so a BLX will switch to Thumb mode
-    address |= 0x1;
+
+  void* code;
+  if (code_array != NULL) {
+    code = code_array->GetData();
+    if (instruction_set == kThumb2) {
+      uintptr_t address = reinterpret_cast<uintptr_t>(code);
+      // Set the low-order bit so a BLX will switch to Thumb mode
+      address |= 0x1;
+      code = reinterpret_cast<void*>(address);
+    }
+  } else {
+    code = NULL;
   }
-  SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, code_),
-                           reinterpret_cast<const void*>(address), false);
+  SetCode(code);
 }
 
 bool Method::IsWithinCode(uintptr_t pc) const {
@@ -717,6 +723,9 @@
 #if defined(__arm__)
     pc &= ~0x1;  // clear any possible thumb instruction mode bit
 #endif
+    if (GetCodeArray() == NULL) {
+      return true;
+    }
     uint32_t rel_offset = pc - reinterpret_cast<uintptr_t>(GetCodeArray()->GetData());
     // Strictly the following test should be a less-than, however, if the last
     // instruction is a call to an exception throw we may see return addresses
diff --git a/src/object.h b/src/object.h
index 51d7b84..7634807 100644
--- a/src/object.h
+++ b/src/object.h
@@ -817,8 +817,21 @@
     return GetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, code_), false);
   }
 
-  void SetCode(ByteArray* code_array, InstructionSet instruction_set,
-               IntArray* mapping_table = NULL, ShortArray* vmap_table = NULL);
+  void SetCode(void* code) {
+    SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, code_), code, false);
+  }
+
+  uint32_t GetOatCodeOffset() const {
+    CHECK(!Runtime::Current()->IsStarted());
+    return reinterpret_cast<uint32_t>(GetCode());
+  }
+
+  void SetOatCodeOffset(uint32_t code_offset) {
+    CHECK(!Runtime::Current()->IsStarted());
+    SetCode(reinterpret_cast<void*>(code_offset));
+  }
+
+  void SetCodeArray(ByteArray* code_array, InstructionSet instruction_set);
 
   static MemberOffset GetCodeOffset() {
     return OFFSET_OF_OBJECT_MEMBER(Method, code_);
@@ -828,14 +841,21 @@
   bool IsWithinCode(uintptr_t pc) const;
 
   IntArray* GetMappingTable() const {
-    return GetFieldObject<IntArray*>(
-        OFFSET_OF_OBJECT_MEMBER(Method, mapping_table_), false);
+    return GetFieldObject<IntArray*>(OFFSET_OF_OBJECT_MEMBER(Method, mapping_table_), false);
+  }
+
+  void SetMappingTable(IntArray* mapping_table) {
+    SetFieldPtr<IntArray*>(OFFSET_OF_OBJECT_MEMBER(Method, mapping_table_), mapping_table, false);
   }
 
   ShortArray* GetVMapTable() const {
     return GetFieldObject<ShortArray*>(OFFSET_OF_OBJECT_MEMBER(Method, vmap_table_), false);
   }
 
+  void SetVMapTable(ShortArray* vmap_table) {
+    SetFieldPtr<ShortArray*>(OFFSET_OF_OBJECT_MEMBER(Method, vmap_table_), vmap_table, false);
+  }
+
   size_t GetFrameSizeInBytes() const {
     DCHECK(sizeof(size_t) == sizeof(uint32_t));
     size_t result = GetField32(
@@ -1300,8 +1320,7 @@
   uint32_t GetAccessFlags() const;
 
   void SetAccessFlags(uint32_t new_access_flags) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), new_access_flags,
-               false);
+    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), new_access_flags, false);
   }
 
   // Returns true if the class is an interface.
@@ -1362,8 +1381,7 @@
 
   void SetPrimitiveType(PrimitiveType new_type) {
     CHECK(sizeof(PrimitiveType) == sizeof(int32_t));
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), new_type,
-               false);
+    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), new_type, false);
   }
 
   // Returns true if the class is a primitive type.
@@ -1421,8 +1439,7 @@
   void SetComponentType(Class* new_component_type) {
     DCHECK(GetComponentType() == NULL);
     DCHECK(new_component_type != NULL);
-    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, component_type_),
-                   new_component_type, false);
+    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, component_type_), new_component_type, false);
   }
 
   size_t GetComponentSize() const {
@@ -1483,15 +1500,13 @@
   void SetObjectSize(size_t new_object_size) {
     DCHECK(!IsVariableSize());
     CHECK(sizeof(size_t) == sizeof(int32_t));
-    return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, object_size_),
-                      new_object_size, false);
+    return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), new_object_size, false);
   }
 
   // Returns true if this class is in the same packages as that class.
   bool IsInSamePackage(const Class* that) const;
 
-  static bool IsInSamePackage(const String* descriptor1,
-                              const String* descriptor2);
+  static bool IsInSamePackage(const String* descriptor1, const String* descriptor2);
 
   // Returns true if this class can access that class.
   bool CanAccess(const Class* that) const {
@@ -1572,8 +1587,7 @@
         OFFSET_OF_OBJECT_MEMBER(Class, super_class_), false);
     DCHECK(old_super_class == NULL || old_super_class == new_super_class);
     DCHECK(new_super_class != NULL);
-    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, super_class_),
-        new_super_class, false);
+    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, super_class_), new_super_class, false);
   }
 
   bool HasSuperClass() const {
@@ -1587,8 +1601,7 @@
   }
 
   void SetSuperClassTypeIdx(int32_t new_super_class_idx) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, super_class_type_idx_),
-               new_super_class_idx, false);
+    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, super_class_type_idx_), new_super_class_idx, false);
   }
 
   const ClassLoader* GetClassLoader() const;
@@ -1761,8 +1774,7 @@
   void SetInterfaces(ObjectArray<Class>* new_interfaces) {
     DCHECK(NULL == GetFieldObject<Object*>(
         OFFSET_OF_OBJECT_MEMBER(Class, interfaces_), false));
-    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, interfaces_),
-                   new_interfaces, false);
+    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, interfaces_), new_interfaces, false);
   }
 
   void SetInterface(uint32_t i, Class* f) {  // TODO: uint16_t
@@ -1799,15 +1811,13 @@
   // Get instance fields
   ObjectArray<Field>* GetIFields() const {
     DCHECK(IsLoaded() || IsErroneous());
-    return GetFieldObject<ObjectArray<Field>*>(
-        OFFSET_OF_OBJECT_MEMBER(Class, ifields_), false);
+    return GetFieldObject<ObjectArray<Field>*>(OFFSET_OF_OBJECT_MEMBER(Class, ifields_), false);
   }
 
   void SetIFields(ObjectArray<Field>* new_ifields) {
     DCHECK(NULL == GetFieldObject<ObjectArray<Field>*>(
         OFFSET_OF_OBJECT_MEMBER(Class, ifields_), false));
-    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, ifields_),
-                   new_ifields, false);
+    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, ifields_), new_ifields, false);
   }
 
   size_t NumInstanceFields() const {
@@ -1829,27 +1839,23 @@
   size_t NumReferenceInstanceFields() const {
     DCHECK(IsResolved() || IsErroneous());
     DCHECK(sizeof(size_t) == sizeof(int32_t));
-    return GetField32(
-        OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), false);
+    return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), false);
   }
 
   size_t NumReferenceInstanceFieldsDuringLinking() const {
     DCHECK(IsLoaded() || IsErroneous());
     DCHECK(sizeof(size_t) == sizeof(int32_t));
-    return GetField32(
-        OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), false);
+    return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), false);
   }
 
   void SetNumReferenceInstanceFields(size_t new_num) {
     DCHECK(sizeof(size_t) == sizeof(int32_t));
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_),
-               new_num, false);
+    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), new_num, false);
   }
 
   uint32_t GetReferenceInstanceOffsets() const {
     DCHECK(IsResolved() || IsErroneous());
-    return GetField32(
-        OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_), false);
+    return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_), false);
   }
 
   void SetReferenceInstanceOffsets(uint32_t new_reference_offsets);
@@ -1863,34 +1869,29 @@
   size_t NumReferenceStaticFields() const {
     DCHECK(IsResolved() || IsErroneous());
     DCHECK(sizeof(size_t) == sizeof(int32_t));
-    return GetField32(
-        OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), false);
+    return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), false);
   }
 
   size_t NumReferenceStaticFieldsDuringLinking() const {
     DCHECK(IsLoaded() || IsErroneous());
     DCHECK(sizeof(size_t) == sizeof(int32_t));
-    return GetField32(
-        OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), false);
+    return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), false);
   }
 
   void SetNumReferenceStaticFields(size_t new_num) {
     DCHECK(sizeof(size_t) == sizeof(int32_t));
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_),
-               new_num, false);
+    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), new_num, false);
   }
 
   ObjectArray<Field>* GetSFields() const {
     DCHECK(IsLoaded() || IsErroneous());
-    return GetFieldObject<ObjectArray<Field>*>(
-        OFFSET_OF_OBJECT_MEMBER(Class, sfields_), false);
+    return GetFieldObject<ObjectArray<Field>*>(OFFSET_OF_OBJECT_MEMBER(Class, sfields_), false);
   }
 
   void SetSFields(ObjectArray<Field>* new_sfields) {
     DCHECK(NULL == GetFieldObject<ObjectArray<Field>*>(
         OFFSET_OF_OBJECT_MEMBER(Class, sfields_), false));
-    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, sfields_),
-                   new_sfields, false);
+    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, sfields_), new_sfields, false);
   }
 
   size_t NumStaticFields() const {
@@ -1908,8 +1909,7 @@
   }
 
   uint32_t GetReferenceStaticOffsets() const {
-    return GetField32(
-        OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_), false);
+    return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_), false);
   }
 
   void SetReferenceStaticOffsets(uint32_t new_reference_offsets);
@@ -1930,19 +1930,16 @@
   }
 
   void SetClinitThreadId(pid_t new_clinit_thread_id) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_),
-               new_clinit_thread_id, false);
+    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), new_clinit_thread_id, false);
   }
 
   Class* GetVerifyErrorClass() const {
     // DCHECK(IsErroneous());
-    return GetFieldObject<Class*>(
-        OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), false);
+    return GetFieldObject<Class*>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), false);
   }
 
   void SetVerifyErrorClass(Class* klass) {
-    klass->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_),
-                          klass, false);
+    klass->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false);
   }
 
   String* GetSourceFile() const;
diff --git a/src/runtime.cc b/src/runtime.cc
index 0dc5229..6cde082 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -10,9 +10,13 @@
 #include "UniquePtr.h"
 #include "class_linker.h"
 #include "heap.h"
+#include "image.h"
 #include "intern_table.h"
 #include "jni_internal.h"
+#include "oat_file.h"
 #include "signal_catcher.h"
+#include "space.h"
+#include "stl_util.h"
 #include "thread.h"
 #include "thread_list.h"
 
@@ -32,6 +36,7 @@
       signal_catcher_(NULL),
       java_vm_(NULL),
       jni_stub_array_(NULL),
+      abstract_method_error_stub_array_(NULL),
       callee_save_method_(NULL),
       started_(false),
       vfprintf_(NULL),
@@ -48,6 +53,7 @@
   // Make sure all other non-daemon threads have terminated, and all daemon threads are suspended.
   delete thread_list_;
 
+  STLDeleteElements(&oat_files_);
   delete class_linker_;
   Heap::Destroy();
   delete intern_table_;
@@ -186,6 +192,7 @@
 Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, bool ignore_unrecognized) {
   UniquePtr<ParsedOptions> parsed(new ParsedOptions());
   parsed->boot_image_ = NULL;
+  parsed->boot_oat_ = NULL;
 #ifdef NDEBUG
   // -Xcheck:jni is off by default for regular builds...
   parsed->check_jni_ = false;
@@ -244,10 +251,15 @@
       const StringPiece& value = options[i].first;
       parsed->class_path_string_ = value.data();
     } else if (option.starts_with("-Xbootimage:")) {
-      // TODO: remove when intern_addr_ is removed, just use -Ximage:
+      // TODO: remove and just use -Ximage:
       parsed->boot_image_ = option.substr(strlen("-Xbootimage:")).data();
+    } else if (option.starts_with("-Xbootoat:")) {
+      // TODO: remove and just use -Xoat:
+      parsed->boot_oat_ = option.substr(strlen("-Xbootoat:")).data();
     } else if (option.starts_with("-Ximage:")) {
       parsed->images_.push_back(option.substr(strlen("-Ximage:")).data());
+    } else if (option.starts_with("-Xoat:")) {
+      parsed->oats_.push_back(option.substr(strlen("-Xoat:")).data());
     } else if (option.starts_with("-Xcheck:jni")) {
       parsed->check_jni_ = true;
     } else if (option.starts_with("-Xms")) {
@@ -402,7 +414,7 @@
 
   UniquePtr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized));
   if (options.get() == NULL) {
-    LOG(WARNING) << "Failed to parse options";
+    LOG(ERROR) << "Failed to parse options";
     return false;
   }
   verbose_startup_ = options->IsVerbose("startup");
@@ -424,7 +436,7 @@
   intern_table_ = new InternTable;
 
   Heap::Init(options->heap_initial_size_, options->heap_maximum_size_,
-      options->boot_image_, options->images_);
+             options->boot_image_, options->images_);
 
   BlockSignals();
 
@@ -439,7 +451,19 @@
   class_linker_ = ClassLinker::Create(options->boot_class_path_,
                                       options->class_path_,
                                       intern_table_,
-                                      Heap::GetBootSpace());
+                                      Heap::GetBootSpace() != NULL);
+  if (Heap::GetBootSpace() != NULL) {
+    if (!OpenOat(Heap::GetBootSpace(), options->boot_oat_)) {
+      LOG(ERROR) << "Failed to open boot oat " << options->boot_oat_;
+      return false;
+    }
+  }
+  for (size_t i = 0; i < options->oats_.size(); i++) {
+    if (!OpenOat(Heap::GetSpaces()[i+1], options->oats_[i])) {
+      LOG(ERROR) << "Failed to open oat " << options->oats_[i];
+      return false;
+    }
+  }
 
   if (IsVerboseStartup()) {
     LOG(INFO) << "Runtime::Init exiting";
@@ -447,6 +471,49 @@
   return true;
 }
 
+bool Runtime::OpenOat(const Space* space, const char* oat) {
+  if (IsVerboseStartup()) {
+    LOG(INFO) << "Runtime::OpenOat entering " << oat;
+  }
+  // TODO: check in ParsedOptions?
+  if (space == NULL) {
+    LOG(ERROR) << "oat specified without image";
+    return false;
+  }
+  if (oat == NULL) {
+    LOG(ERROR) << "image specified without oat";
+    return false;
+  }
+  const ImageHeader& image_header = space->GetImageHeader();
+  String* oat_location = image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString();
+  std::string oat_filename = oat_location->ToModifiedUtf8();
+  if (!StringPiece(oat).ends_with(oat_filename)) {
+    LOG(ERROR) << "oat file name " << oat
+               << " does not match value found in image: " << oat_filename;
+    return false;
+  }
+  // TODO: we should be able to just use oat_filename instead of oat
+  // if we passed the prefix argument to find it on the host during cross compilation.
+  OatFile* oat_file = OatFile::Open(std::string(oat), "", image_header.GetOatBaseAddr());
+  if (oat_file == NULL) {
+    LOG(ERROR) << "Failed to open oat file " << oat_filename << " referenced from image";
+    return false;
+  }
+  uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();
+  uint32_t image_oat_checksum = image_header.GetOatChecksum();
+  if (oat_checksum != image_oat_checksum) {
+    LOG(ERROR) << "Failed to match oat filechecksum " << std::hex << oat_checksum
+               << " to expected oat checksum " << std::hex << oat_checksum
+               << " in image";
+    return false;
+  }
+  oat_files_.push_back(oat_file);
+  if (IsVerboseStartup()) {
+    LOG(INFO) << "Runtime::OpenOat exiting";
+  }
+  return true;
+}
+
 void Runtime::InitNativeMethods() {
   if (IsVerboseStartup()) {
     LOG(INFO) << "Runtime::InitNativeMethods entering";
@@ -598,13 +665,58 @@
   thread_list_->Unregister();
 }
 
+void Runtime::VisitRoots(Heap::RootVisitor* visitor, void* arg) const {
+  class_linker_->VisitRoots(visitor, arg);
+  intern_table_->VisitRoots(visitor, arg);
+  java_vm_->VisitRoots(visitor, arg);
+  thread_list_->VisitRoots(visitor, arg);
+  visitor(jni_stub_array_, arg);
+  visitor(abstract_method_error_stub_array_, arg);
+  visitor(callee_save_method_, arg);
+
+  //(*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg);
+  //(*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg);
+  //(*visitor)(&gDvm.noClassDefFoundErrorObj, 0, ROOT_VM_INTERNAL, arg);
+  UNIMPLEMENTED(WARNING) << "some roots not marked";
+}
+
+bool Runtime::HasJniStubArray() const {
+  return jni_stub_array_ != NULL;
+}
+
+ByteArray* Runtime::GetJniStubArray() const {
+  CHECK(jni_stub_array_ != NULL);
+  return jni_stub_array_;
+}
+
+void Runtime::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;
+}
+
+bool Runtime::HasAbstractMethodErrorStubArray() const {
+  return abstract_method_error_stub_array_ != NULL;
+}
+
+ByteArray* Runtime::GetAbstractMethodErrorStubArray() const {
+  CHECK(abstract_method_error_stub_array_ != NULL);
+  return abstract_method_error_stub_array_;
+}
+
+void Runtime::SetAbstractMethodErrorStubArray(ByteArray* abstract_method_error_stub_array) {
+  CHECK(abstract_method_error_stub_array != NULL);
+  CHECK(abstract_method_error_stub_array_ == NULL || abstract_method_error_stub_array_ == abstract_method_error_stub_array);
+  abstract_method_error_stub_array_ = abstract_method_error_stub_array;
+}
+
 Method* Runtime::CreateCalleeSaveMethod(InstructionSet insns) {
   Class* method_class = Method::GetMethodClass();
   Method* method = down_cast<Method*>(method_class->AllocObject());
   method->SetDeclaringClass(method_class);
   method->SetName(intern_table_->InternStrong("$$$callee_save_method$$$"));
   method->SetSignature(intern_table_->InternStrong("()V"));
-  method->SetCode(NULL, insns, NULL);
+  method->SetCodeArray(NULL, insns);
   if ((insns == kThumb2) || (insns == kArm)) {
     size_t frame_size = (12 /* gprs */ + 32 /* fprs */ + 4 /* data */) * kPointerSize;
     method->SetFrameSizeInBytes(frame_size);
@@ -667,18 +779,19 @@
   return method;
 }
 
-void Runtime::VisitRoots(Heap::RootVisitor* visitor, void* arg) const {
-  class_linker_->VisitRoots(visitor, arg);
-  intern_table_->VisitRoots(visitor, arg);
-  java_vm_->VisitRoots(visitor, arg);
-  thread_list_->VisitRoots(visitor, arg);
-  visitor(jni_stub_array_, arg);
-  visitor(callee_save_method_, arg);
-
-  //(*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg);
-  //(*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg);
-  //(*visitor)(&gDvm.noClassDefFoundErrorObj, 0, ROOT_VM_INTERNAL, arg);
-  UNIMPLEMENTED(WARNING) << "some roots not marked";
+bool Runtime::HasCalleeSaveMethod() const {
+  return callee_save_method_ != NULL;
 }
 
+// Returns a special method that describes all callee saves being spilled to the stack.
+Method* Runtime::GetCalleeSaveMethod() const {
+  CHECK(callee_save_method_ != NULL);
+  return callee_save_method_;
+}
+
+void Runtime::SetCalleeSaveMethod(Method* method) {
+  callee_save_method_ = method;
+}
+
+
 }  // namespace art
diff --git a/src/runtime.h b/src/runtime.h
index ca0496c..bae5643 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -30,6 +30,7 @@
 class InternTable;
 class JavaVMExt;
 class Method;
+class OatFile;
 class SignalCatcher;
 class String;
 class ThreadList;
@@ -49,7 +50,9 @@
     std::string class_path_string_;
     std::vector<const DexFile*> class_path_;
     const char* boot_image_;
+    const char* boot_oat_;
     std::vector<const char*> images_;
+    std::vector<const char*> oats_;
     bool check_jni_;
     std::string jni_trace_;
     size_t heap_initial_size_;
@@ -145,35 +148,19 @@
 
   void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
 
-  bool HasJniStubArray() const {
-    return jni_stub_array_ != NULL;
-  }
+  bool HasJniStubArray() const;
+  ByteArray* GetJniStubArray() const;
+  void SetJniStubArray(ByteArray* jni_stub_array);
 
-  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;
-  }
-
-  Method* CreateCalleeSaveMethod(InstructionSet insns);
-
-  bool HasCalleeSaveMethod() const {
-    return callee_save_method_ != NULL;
-  }
+  bool HasAbstractMethodErrorStubArray() const;
+  ByteArray* GetAbstractMethodErrorStubArray() const;
+  void SetAbstractMethodErrorStubArray(ByteArray* abstract_method_error_stub_array);
 
   // Returns a special method that describes all callee saves being spilled to the stack.
-  Method* GetCalleeSaveMethod() const {
-    return callee_save_method_;
-  }
-
-  void SetCalleeSaveMethod(Method* method) {
-    callee_save_method_ = method;
-  }
+  Method* CreateCalleeSaveMethod(InstructionSet insns);
+  bool HasCalleeSaveMethod() const;
+  Method* GetCalleeSaveMethod() const;
+  void SetCalleeSaveMethod(Method* method);
 
   int32_t GetStat(int kind);
 
@@ -195,6 +182,7 @@
   void BlockSignals();
 
   bool Init(const Options& options, bool ignore_unrecognized);
+  bool OpenOat(const Space* space, const char* boot_oat_);
   void InitNativeMethods();
   void RegisterRuntimeNativeMethods(JNIEnv*);
   void StartDaemonThreads();
@@ -214,12 +202,16 @@
 
   ClassLinker* class_linker_;
 
+  std::vector<const OatFile*> oat_files_;
+
   SignalCatcher* signal_catcher_;
 
   JavaVMExt* java_vm_;
 
   ByteArray* jni_stub_array_;
 
+  ByteArray* abstract_method_error_stub_array_;
+
   Method* callee_save_method_;
 
   bool started_;
diff --git a/src/space.cc b/src/space.cc
index 9efe599..09eb0c9 100644
--- a/src/space.cc
+++ b/src/space.cc
@@ -114,7 +114,7 @@
     LOG(WARNING) << "Invalid image header " << image_file_name;
     return false;
   }
-  UniquePtr<MemMap> map(MemMap::Map(image_header.GetBaseAddr(),
+  UniquePtr<MemMap> map(MemMap::Map(image_header.GetImageBaseAddr(),
                                     file->Length(),
                                     // TODO: selectively PROT_EXEC when image contains a code space
                                     PROT_READ | PROT_WRITE | PROT_EXEC,
@@ -125,13 +125,16 @@
     LOG(WARNING) << "Failed to map " << image_file_name;
     return false;
   }
-  CHECK_EQ(image_header.GetBaseAddr(), map->GetAddress());
+  CHECK_EQ(image_header.GetImageBaseAddr(), map->GetAddress());
   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));
 
+  Object* ame_stub_array = image_header.GetImageRoot(ImageHeader::kAbstractMethodErrorStubArray);
+  Runtime::Current()->SetAbstractMethodErrorStubArray(down_cast<ByteArray*>(ame_stub_array));
+
   Object* callee_save_method = image_header.GetImageRoot(ImageHeader::kCalleeSaveMethod);
   Runtime::Current()->SetCalleeSaveMethod(down_cast<Method*>(callee_save_method));
 
diff --git a/src/stub_arm.cc b/src/stub_arm.cc
index a35bfe2..75d60f5 100644
--- a/src/stub_arm.cc
+++ b/src/stub_arm.cc
@@ -36,6 +36,7 @@
   size_t cs = assembler->CodeSize();
   ByteArray* abstract_stub = ByteArray::Alloc(cs);
   CHECK(abstract_stub != NULL);
+  CHECK(abstract_stub->GetClass()->GetDescriptor());
   MemoryRegion code(abstract_stub->GetData(), abstract_stub->GetLength());
   assembler->FinalizeInstructions(code);
 
diff --git a/src/utils.cc b/src/utils.cc
index b47ee2c..b5063ffeb 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -48,6 +48,9 @@
 }
 
 std::string PrettyDescriptor(const String* java_descriptor) {
+  if (java_descriptor == NULL) {
+    return "null";
+  }
   std::string descriptor(java_descriptor->ToModifiedUtf8());
 
   // Count the number of '['s to get the dimensionality.
diff --git a/test/003-omnibus-opcodes/build b/test/003-omnibus-opcodes/build
index 5a8a1a0..bbd392f5 100644
--- a/test/003-omnibus-opcodes/build
+++ b/test/003-omnibus-opcodes/build
@@ -27,7 +27,9 @@
 
 dex2oatd -Xms16m -Xmx16m \
     --boot-dex-file=${ANDROID_PRODUCT_OUT}/system/framework/core.jar \
-    --boot=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \
+    --boot-oat=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \
+    --boot-image=${ANDROID_PRODUCT_OUT}/system/framework/core.art \
     --dex-file=${ANDROID_PRODUCT_OUT}/system/framework/test.jar \
-    --image=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \
+    --oat=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \
+    --image=${ANDROID_PRODUCT_OUT}/system/framework/test.art \
     --strip-prefix=${ANDROID_PRODUCT_OUT}
diff --git a/test/023-many-interfaces/build b/test/023-many-interfaces/build
index a2b7eea..118e178 100644
--- a/test/023-many-interfaces/build
+++ b/test/023-many-interfaces/build
@@ -29,7 +29,9 @@
 
 dex2oatd -Xms16m -Xmx16m \
     --boot-dex-file=${ANDROID_PRODUCT_OUT}/system/framework/core.jar \
-    --boot=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \
+    --boot-oat=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \
+    --boot-image=${ANDROID_PRODUCT_OUT}/system/framework/core.art \
     --dex-file=${ANDROID_PRODUCT_OUT}/system/framework/test.jar \
-    --image=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \
+    --oat=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \
+    --image=${ANDROID_PRODUCT_OUT}/system/framework/test.art \
     --strip-prefix=${ANDROID_PRODUCT_OUT}
diff --git a/test/056-const-string-jumbo/build b/test/056-const-string-jumbo/build
index 98f27b8..c39779b 100644
--- a/test/056-const-string-jumbo/build
+++ b/test/056-const-string-jumbo/build
@@ -48,7 +48,9 @@
 
 dex2oatd -Xms16m -Xmx16m \
     --boot-dex-file=${ANDROID_PRODUCT_OUT}/system/framework/core.jar \
-    --boot=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \
+    --boot-oat=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \
+    --boot-image=${ANDROID_PRODUCT_OUT}/system/framework/core.art \
     --dex-file=${ANDROID_PRODUCT_OUT}/system/framework/test.jar \
-    --image=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \
+    --oat=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \
+    --image=${ANDROID_PRODUCT_OUT}/system/framework/test.art \
     --strip-prefix=${ANDROID_PRODUCT_OUT}
diff --git a/test/085-old-style-inner-class/build b/test/085-old-style-inner-class/build
index 6f41a0d..5a1b04c 100644
--- a/test/085-old-style-inner-class/build
+++ b/test/085-old-style-inner-class/build
@@ -30,7 +30,9 @@
 
 dex2oatd -Xms16m -Xmx16m \
     --boot-dex-file=${ANDROID_PRODUCT_OUT}/system/framework/core.jar \
-    --boot=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \
+    --boot-oat=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \
+    --boot-image=${ANDROID_PRODUCT_OUT}/system/framework/core.art \
     --dex-file=${ANDROID_PRODUCT_OUT}/system/framework/test.jar \
-    --image=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \
+    --oat=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \
+    --image=${ANDROID_PRODUCT_OUT}/system/framework/test.art \
     --strip-prefix=${ANDROID_PRODUCT_OUT}
diff --git a/test/etc/default-build b/test/etc/default-build
index dcc372a..e31e0ea 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -30,9 +30,11 @@
 
 dex2oatd -Xms16m -Xmx16m \
     --boot-dex-file=${ANDROID_PRODUCT_OUT}/system/framework/core.jar \
-    --boot=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \
+    --boot-oat=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \
+    --boot-image=${ANDROID_PRODUCT_OUT}/system/framework/core.art \
     --dex-file=${ANDROID_PRODUCT_OUT}/system/framework/test.jar \
-    --image=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \
+    --oat=${ANDROID_PRODUCT_OUT}/system/framework/test.oat \
+    --image=${ANDROID_PRODUCT_OUT}/system/framework/test.art \
     --strip-prefix=${ANDROID_PRODUCT_OUT}
 
 if [ -r src-ex ]; then
@@ -50,8 +52,10 @@
 
     dex2oatd -Xms16m -Xmx16m \
         --boot-dex-file=${ANDROID_PRODUCT_OUT}/system/framework/core.jar \
-        --boot=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \
+        --boot-oat=${ANDROID_PRODUCT_OUT}/system/framework/core.oat \
+        --boot-image=${ANDROID_PRODUCT_OUT}/system/framework/core.art \
         --dex-file=${ANDROID_PRODUCT_OUT}/system/framework/test-ex.jar \
-        --image=${ANDROID_PRODUCT_OUT}/system/framework/test-ex.oat \
+        --oat=${ANDROID_PRODUCT_OUT}/system/framework/test-ex.oat \
+        --image=${ANDROID_PRODUCT_OUT}/system/framework/test-ex.art \
         --strip-prefix=${ANDROID_PRODUCT_OUT}
 fi
diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar
index 8b490cd..fd96b6b 100755
--- a/test/etc/push-and-run-test-jar
+++ b/test/etc/push-and-run-test-jar
@@ -108,13 +108,17 @@
 if [ "$QUIET" = "n" ]; then
     adb push ${ANDROID_PRODUCT_OUT}/system/framework/test.jar /system/framework
     adb push ${ANDROID_PRODUCT_OUT}/system/framework/test.oat /system/framework
+    adb push ${ANDROID_PRODUCT_OUT}/system/framework/test.art /system/framework
     adb push ${ANDROID_PRODUCT_OUT}/system/framework/test-ex.jar /system/framework
     adb push ${ANDROID_PRODUCT_OUT}/system/framework/test-ex.oat /system/framework
+    adb push ${ANDROID_PRODUCT_OUT}/system/framework/test-ex.art /system/framework
 else
     adb push ${ANDROID_PRODUCT_OUT}/system/framework/test.jar /system/framework >/dev/null 2>&1
     adb push ${ANDROID_PRODUCT_OUT}/system/framework/test.oat /system/framework >/dev/null 2>&1
+    adb push ${ANDROID_PRODUCT_OUT}/system/framework/test.art /system/framework >/dev/null 2>&1
     adb push ${ANDROID_PRODUCT_OUT}/system/framework/test-ex.jar /system/framework >/dev/null 2>&1
     adb push ${ANDROID_PRODUCT_OUT}/system/framework/test-ex.oat /system/framework >/dev/null 2>&1
+    adb push ${ANDROID_PRODUCT_OUT}/system/framework/test-ex.art /system/framework >/dev/null 2>&1
 fi
 
 if [ "$DEBUG" = "y" ]; then
@@ -130,10 +134,13 @@
 if [ "$ZYGOTE" = "y" ]; then
     adb shell cd /data \; dvz -classpath test.jar Main "$@"
 else
-    cmdline="cd /data; oatexecd -Xbootclasspath:/system/framework/core.jar \
-      -Xbootimage:/system/framework/core.oat \
+    cmdline="cd /data; oatexecd \
+      -Xbootclasspath:/system/framework/core.jar \
+      -Xbootoat:/system/framework/core.oat \
+      -Xbootimage:/system/framework/core.art \
       -classpath /system/framework/test.jar \
-      -Ximage:/system/framework/test.oat Main"
+      -Xoat:/system/framework/test.oat \
+      -Ximage:/system/framework/test.art Main"
     #cmdline="cd /data; dalvikvm $DEX_VERIFY $DEX_OPTIMIZE $DEX_DEBUG \
     #    $GC_OPTS -cp test.jar -Xint:${INTERP} -ea Main"
     if [ "$DEV_MODE" = "y" ]; then