Work on heap and space initialization to support image loading

Change-Id: Icab25efa4dee17e4b6c6e97e38f63f5ab8a8a005
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 6fa0293..0f1196a 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -47,6 +47,7 @@
 	src/file.cc \
 	src/file_linux.cc \
 	src/heap.cc \
+	src/image.cc \
 	src/image_writer.cc \
 	src/indirect_reference_table.cc \
 	src/intern_table.cc \
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 3437a5a..a7d10f4 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -257,7 +257,7 @@
   return down_cast<Method*>(GetClassRoot(kJavaLangReflectMethod)->NewInstance());
 }
 
-// TODO remove once we can use java.lang.Class.getSystemClassLoader
+// TODO: remove once we can use java.lang.Class.getSystemClassLoader
 PathClassLoader* ClassLinker::AllocPathClassLoader(std::vector<const DexFile*> dex_files) {
   PathClassLoader* cl = down_cast<PathClassLoader*>(GetClassRoot(kDalvikSystemPathClassLoader)->NewInstance());
   cl->SetClassPath(dex_files);
@@ -266,7 +266,7 @@
 
 Class* ClassLinker::FindClass(const StringPiece& descriptor,
                               ClassLoader* class_loader) {
-  // TODO remove this contrived parent class loader check when we have a real ClassLoader.
+  // TODO: remove this contrived parent class loader check when we have a real ClassLoader.
   if (class_loader != NULL) {
     Class* klass = FindClass(descriptor, NULL);
     if (klass != NULL) {
@@ -323,7 +323,7 @@
       ObjectLock lock(klass);
       klass->clinit_thread_id_ = self->GetId();
       // Add the newly loaded class to the loaded classes table.
-      bool success = InsertClass(klass); // TODO just return collision
+      bool success = InsertClass(klass);  // TODO: just return collision
       if (!success) {
         // We may fail to insert if we raced with another thread.
         klass->clinit_thread_id_ = 0;
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 7e93e0d..21c576e 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -394,59 +394,59 @@
   scoped_ptr<DexFile> dex(OpenDexFileBase64(kStatics));
   PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
   Class* statics = class_linker_->FindClass("LStatics;", class_loader);
-  // class_linker_->InitializeClass(statics);  // TODO uncomment this
+  // class_linker_->InitializeClass(statics);  // TODO: uncomment this
 
   EXPECT_EQ(10U, statics->NumStaticFields());
 
   Field* s0 = statics->GetStaticField(0);
   EXPECT_EQ("Ljava/lang/reflect/Field;", s0->GetClass()->descriptor_);
   EXPECT_EQ('Z', s0->GetType());
-//  EXPECT_EQ(true, s0->GetBoolean());  // TODO uncomment this
+//  EXPECT_EQ(true, s0->GetBoolean());  // TODO: uncomment this
   s0->SetBoolean(false);
 
   Field* s1 = statics->GetStaticField(1);
   EXPECT_EQ('B', s1->GetType());
-//  EXPECT_EQ(5, s1->GetByte());  // TODO uncomment this
+//  EXPECT_EQ(5, s1->GetByte());  // TODO: uncomment this
   s1->SetByte(6);
 
   Field* s2 = statics->GetStaticField(2);
   EXPECT_EQ('C', s2->GetType());
-//  EXPECT_EQ('a', s2->GetChar());  // TODO uncomment this
+//  EXPECT_EQ('a', s2->GetChar());  // TODO: uncomment this
   s2->SetChar('b');
 
   Field* s3 = statics->GetStaticField(3);
   EXPECT_EQ('S', s3->GetType());
-//  EXPECT_EQ(65000, s3->GetShort());  // TODO uncomment this
+//  EXPECT_EQ(65000, s3->GetShort());  // TODO: uncomment this
   s3->SetShort(65001);
 
   Field* s4 = statics->GetStaticField(4);
   EXPECT_EQ('I', s4->GetType());
-//  EXPECT_EQ(2000000000, s4->GetInt());  // TODO uncomment this
+//  EXPECT_EQ(2000000000, s4->GetInt());  // TODO: uncomment this
   s4->SetInt(2000000001);
 
   Field* s5 = statics->GetStaticField(5);
   EXPECT_EQ('J', s5->GetType());
-//  EXPECT_EQ(0x1234567890abcdefLL, s5->GetLong());  // TODO uncomment this
+//  EXPECT_EQ(0x1234567890abcdefLL, s5->GetLong());  // TODO: uncomment this
   s5->SetLong(0x34567890abcdef12LL);
 
   Field* s6 = statics->GetStaticField(6);
   EXPECT_EQ('F', s6->GetType());
-//  EXPECT_EQ(0.5, s6->GetFloat());  // TODO uncomment this
+//  EXPECT_EQ(0.5, s6->GetFloat());  // TODO: uncomment this
   s6->SetFloat(0.75);
 
   Field* s7 = statics->GetStaticField(7);
   EXPECT_EQ('D', s7->GetType());
-//  EXPECT_EQ(16777217, s7->GetDouble());  // TODO uncomment this
+//  EXPECT_EQ(16777217, s7->GetDouble());  // TODO: uncomment this
   s7->SetDouble(16777219);
 
   Field* s8 = statics->GetStaticField(8);
   EXPECT_EQ('L', s8->GetType());
-//  EXPECT_TRUE(down_cast<String*>(s8->GetObject())->Equals("android"));  // TODO uncomment this
+//  EXPECT_TRUE(down_cast<String*>(s8->GetObject())->Equals("android"));  // TODO: uncomment this
   s8->SetObject(String::AllocFromAscii("robot"));
 
   Field* s9 = statics->GetStaticField(9);
   EXPECT_EQ('[', s9->GetType());
-//  EXPECT_EQ(NULL, s9->GetObject());  // TODO uncomment this
+//  EXPECT_EQ(NULL, s9->GetObject());  // TODO: uncomment this
   s9->SetObject(NULL);
 
   EXPECT_EQ(false,                s0->GetBoolean());
diff --git a/src/dex_file.cc b/src/dex_file.cc
index 22e4430..b9ae7e3 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -35,7 +35,7 @@
       return ClassPathEntry(dex_file, dex_class_def);
     }
   }
-  // TODO remove reinterpret_cast when issue with -std=gnu++0x host issue resolved
+  // TODO: remove reinterpret_cast when issue with -std=gnu++0x host issue resolved
   return ClassPathEntry(reinterpret_cast<const DexFile*>(NULL),
                         reinterpret_cast<const DexFile::ClassDef*>(NULL));
 }
@@ -257,7 +257,7 @@
       return NULL;
     }
 
-    // TODO restat and check length against zip_entry->GetUncompressedLength()?
+    // TODO: restat and check length against zip_entry->GetUncompressedLength()?
 
     // Compute checksum and compare to zip. If things look okay, rename from tmp.
     off_t lseek_result = lseek(fd->GetFd(), 0, SEEK_SET);
diff --git a/src/file.h b/src/file.h
index 680872e..3353602 100644
--- a/src/file.h
+++ b/src/file.h
@@ -33,6 +33,8 @@
   // Returns a negative value if position cannot be determined.
   virtual off_t Position() = 0;
 
+  virtual int Fd() = 0;
+
   const char* name() const { return name_; }
 
  protected:
diff --git a/src/file_linux.h b/src/file_linux.h
index 7e36ada..3d381d5 100644
--- a/src/file_linux.h
+++ b/src/file_linux.h
@@ -22,6 +22,10 @@
   virtual off_t Length();
   virtual off_t Position();
 
+  virtual int Fd() {
+    return fd_;
+  }
+
  private:
   static const int kClosedFd = -1;
 
diff --git a/src/file_test.cc b/src/file_test.cc
index a836d83..385b3dd 100644
--- a/src/file_test.cc
+++ b/src/file_test.cc
@@ -44,4 +44,13 @@
   EXPECT_EQ(4, file->Position());
 }
 
+
+TEST_F(FileTest, FileFd) {
+  std::string filename = GetLibCoreDexFileName();
+  scoped_ptr<File> file(OS::OpenFile(filename.c_str(), false));
+  ASSERT_TRUE(file != NULL);
+  EXPECT_NE(-1, file->Fd());
+}
+
+
 }  // namespace art
diff --git a/src/heap.cc b/src/heap.cc
index b9998da..de801ce 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -14,7 +14,7 @@
 
 std::vector<Space*> Heap::spaces_;
 
-size_t Heap::startup_size_ = 0;
+Space* Heap::alloc_space_ = NULL;
 
 size_t Heap::maximum_size_ = 0;
 
@@ -28,14 +28,33 @@
 
 HeapBitmap* Heap::live_bitmap_ = NULL;
 
-bool Heap::Init(size_t startup_size, size_t maximum_size) {
-  Space* space = Space::Create(startup_size, maximum_size);
+bool Heap::Init(size_t initial_size, size_t maximum_size, const char* boot_image_file_name) {
+  Space* boot_space;
+  byte* requested_base;
+  if (boot_image_file_name == NULL) {
+    boot_space = NULL;
+    requested_base = NULL;
+  } else {
+    boot_space = Space::Create(boot_image_file_name);
+    if (boot_space == NULL) {
+      return false;
+    }
+    spaces_.push_back(boot_space);
+    requested_base = boot_space->GetBase() + RoundUp(boot_space->Size(), kPageSize);
+  }
+
+  Space* space = Space::Create(initial_size, maximum_size, requested_base);
   if (space == NULL) {
     return false;
   }
 
-  byte* base = space->GetBase();
-  size_t num_bytes = space->Size();
+  if (boot_space == NULL) {
+    boot_space = space;
+  }
+  byte* base = std::min(boot_space->GetBase(), space->GetBase());
+  byte* limit = std::max(boot_space->GetLimit(), space->GetLimit());
+  DCHECK_LT(base, limit);
+  size_t num_bytes = limit - base;
 
   // Allocate the initial live bitmap.
   scoped_ptr<HeapBitmap> live_bitmap(HeapBitmap::Create(base, num_bytes));
@@ -49,8 +68,8 @@
     return false;
   }
 
+  alloc_space_ = space;
   spaces_.push_back(space);
-  startup_size_ = startup_size;
   maximum_size_ = maximum_size;
   live_bitmap_ = live_bitmap.release();
   mark_bitmap_ = mark_bitmap.release();
@@ -62,8 +81,14 @@
 
 void Heap::Destroy() {
   STLDeleteElements(&spaces_);
-  delete mark_bitmap_;
-  delete live_bitmap_;
+  if (mark_bitmap_ != NULL) {
+    delete mark_bitmap_;
+    mark_bitmap_ = NULL;
+  }
+  if (live_bitmap_ != NULL) {
+    delete live_bitmap_;
+  }
+  live_bitmap_ = NULL;
 }
 
 Object* Heap::AllocObject(Class* klass, size_t num_bytes) {
@@ -100,8 +125,8 @@
 }
 
 Object* Heap::Allocate(size_t size) {
-  CHECK_EQ(spaces_.size(), 1u);
-  Space* space = spaces_[0];
+  DCHECK(alloc_space_ != NULL);
+  Space* space = alloc_space_;
   Object* obj = Allocate(space, size);
   if (obj != NULL) {
     RecordAllocation(space, obj);
diff --git a/src/heap.h b/src/heap.h
index 247c6ba..99f6554 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -24,7 +24,9 @@
 
   typedef void (RootVistor)(Object* root, void* arg);
 
-  static bool Init(size_t starting_size, size_t maximum_size);
+  // Create a heap with the requested sizes. optional boot image may
+  // be NULL, otherwise it is an image filename created by ImageWriter.
+  static bool Init(size_t starting_size, size_t maximum_size, const char* boot_image_file_name);
 
   static void Destroy();
 
@@ -53,10 +55,6 @@
     return mark_bitmap_;
   }
 
-  static size_t GetMaximumSize() {
-    return maximum_size_;
-  }
-
  private:
   // Allocates uninitialized storage.
   static Object* Allocate(size_t num_bytes);
@@ -73,13 +71,13 @@
 
   static std::vector<Space*> spaces_;
 
+  // default Space for allocations
+  static Space* alloc_space_;
+
   static HeapBitmap* mark_bitmap_;
 
   static HeapBitmap* live_bitmap_;
 
-  // The startup size of the heap in bytes.
-  static size_t startup_size_;
-
   // The maximum size of the heap in bytes.
   static size_t maximum_size_;
 
diff --git a/src/image.cc b/src/image.cc
new file mode 100644
index 0000000..a41bf5ba
--- /dev/null
+++ b/src/image.cc
@@ -0,0 +1,10 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "image.h"
+
+namespace art {
+
+const byte ImageHeader::kImageMagic[] = { 'i', 'm', 'g', '\n' };
+const byte ImageHeader::kImageVersion[] = { '0', '0', '1', '\0' };
+
+}  // namespace art
diff --git a/src/image.h b/src/image.h
new file mode 100644
index 0000000..c4123e8
--- /dev/null
+++ b/src/image.h
@@ -0,0 +1,47 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_IMAGE_H_
+#define ART_SRC_IMAGE_H_
+
+#include <string.h>
+
+#include "globals.h"
+
+namespace art {
+
+// header of image files written by ImageWriter, read and validated by Space.
+class ImageHeader {
+ public:
+  ImageHeader() {}
+
+  ImageHeader(uint32_t base_addr) : base_addr_(base_addr) {
+    memcpy(magic_, kImageMagic, sizeof(kImageMagic));
+    memcpy(version_, kImageVersion, sizeof(kImageVersion));
+  }
+
+  bool IsValid() {
+    if (memcmp(magic_, kImageMagic, sizeof(kImageMagic) != 0)) {
+      return false;
+    }
+    if (memcmp(version_, kImageVersion, sizeof(kImageVersion) != 0)) {
+      return false;
+    }
+    return true;
+  }
+
+  byte* GetBaseAddr() const {
+    return reinterpret_cast<byte*>(base_addr_);
+  }
+
+ private:
+  static const byte kImageMagic[4];
+  static const byte kImageVersion[4];
+
+  byte magic_[4];
+  byte version_[4];
+  uint32_t base_addr_;
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_IMAGE_H_
diff --git a/src/image_test.cc b/src/image_test.cc
index 55984f6..6cfa62d 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -1,7 +1,11 @@
 // Copyright 2011 Google Inc. All Rights Reserved.
 
 #include "common_test.h"
+#include "file.h"
+#include "image.h"
 #include "image_writer.h"
+#include "os.h"
+#include "space.h"
 
 #include "gtest/gtest.h"
 
@@ -13,16 +17,26 @@
   scoped_ptr<DexFile> libcore_dex_file(GetLibCoreDex());
   EXPECT_TRUE(libcore_dex_file.get() != NULL);
 
-  // TODO garbage collect before writing
+  // TODO: garbage collect before writing
   const std::vector<Space*>& spaces = Heap::GetSpaces();
   // can't currently deal with writing a space that might have pointers between spaces
-  CHECK_EQ(1U, spaces.size());
+  ASSERT_EQ(1U, spaces.size());
+  Space* space = spaces[0];
 
   ImageWriter writer;
   ScratchFile tmp;
   const int image_base = 0x5000000;
-  bool success = writer.Write(spaces[0], tmp.GetFilename(), reinterpret_cast<byte*>(image_base));
-  EXPECT_TRUE(success);
+  bool success = writer.Write(space, tmp.GetFilename(), reinterpret_cast<byte*>(image_base));
+  ASSERT_TRUE(success);
+
+  {
+    scoped_ptr<File> file(OS::OpenFile(tmp.GetFilename(), false));
+    ASSERT_TRUE(file != NULL);
+    ImageHeader image_header;
+    file->ReadFully(&image_header, sizeof(image_header));
+    ASSERT_TRUE(image_header.IsValid());
+    ASSERT_GE(sizeof(image_header) + space->Size(), static_cast<size_t>(file->Length()));
+  }
 
   // tear down old runtime and make a new one
   delete runtime_.release();
diff --git a/src/image_writer.cc b/src/image_writer.cc
index beb7366..b6625d0 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -8,6 +8,7 @@
 #include "file.h"
 #include "globals.h"
 #include "heap.h"
+#include "image.h"
 #include "logging.h"
 #include "object.h"
 #include "space.h"
@@ -33,11 +34,9 @@
 bool ImageWriter::Init(Space* space) {
   size_t size = space->Size();
   int prot = PROT_READ | PROT_WRITE;
-  int flags = MAP_PRIVATE | MAP_ANONYMOUS;
   size_t length = RoundUp(size, kPageSize);
-  image_.reset(MemMap::Map(length, prot, flags));
+  image_.reset(MemMap::Map(length, prot));
   if (image_ == NULL) {
-    PLOG(ERROR) << "mmap failed";
     return false;
   }
   return true;
@@ -56,8 +55,9 @@
   HeapBitmap* heap_bitmap = Heap::GetLiveBits();
   DCHECK(heap_bitmap != NULL);
   DCHECK_EQ(0U, image_top_);
-  // leave a header, ensures objects have non-zero offset for DCHECKs
-  image_top_ += 8; // 64-bit-alignment
+  ImageHeader image_header(reinterpret_cast<uint32_t>(image_base_));
+  memcpy(image_->GetAddress(), &image_header, sizeof(image_header));
+  image_top_ += RoundUp(sizeof(image_header), 8); // 64-bit-alignment
   heap_bitmap->Walk(CalculateNewObjectOffsetsCallback, this);
   DCHECK_LT(image_top_, image_->GetLength());
   // Note that top_ is left at end of used space
@@ -88,7 +88,7 @@
   DCHECK(orig != NULL);
   DCHECK(copy != NULL);
   copy->klass_ = down_cast<Class*>(GetImageAddress(orig->klass_));
-  // TODO specical case init of pointers to malloc data (or removal of these pointers)
+  // TODO: specical case init of pointers to malloc data (or removal of these pointers)
   if (orig->IsObjectArray()) {
     FixupObjectArray(orig->AsObjectArray<Object>(), down_cast<ObjectArray<Object>*>(copy));
   } else {
diff --git a/src/image_writer.h b/src/image_writer.h
index 0af45c6..003c4e9 100644
--- a/src/image_writer.h
+++ b/src/image_writer.h
@@ -1,7 +1,7 @@
 // Copyright 2011 Google Inc. All Rights Reserved.
 
-#ifndef ART_SRC_IMAGE_H_
-#define ART_SRC_IMAGE_H_
+#ifndef ART_SRC_IMAGE_WRITER_H_
+#define ART_SRC_IMAGE_WRITER_H_
 
 #include <cstddef>
 #include <stdint.h>
@@ -66,4 +66,4 @@
 
 }  // namespace art
 
-#endif  // ART_SRC_IMAGE_H_
+#endif  // ART_SRC_IMAGE_WRITER_H_
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index f3d13c8..440dda5 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -382,9 +382,7 @@
 JniCompiler::JniCompiler() {
   // TODO: this shouldn't be managed by the JniCompiler, we should have a
   // code cache.
-  jni_code_.reset(MemMap::Map(kPageSize,
-                              PROT_READ | PROT_WRITE | PROT_EXEC,
-                              MAP_ANONYMOUS | MAP_PRIVATE));
+  jni_code_.reset(MemMap::Map(kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC));
   CHECK(jni_code_ !=  NULL);
   jni_code_top_ = jni_code_->GetAddress();
 }
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index d4df48a..854df88 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -20,9 +20,7 @@
   virtual void SetUp() {
     CommonTest::SetUp();
     // Create thunk code that performs the native to managed transition
-    thunk_code_.reset(MemMap::Map(kPageSize,
-                                  PROT_READ | PROT_WRITE | PROT_EXEC,
-                                  MAP_ANONYMOUS | MAP_PRIVATE));
+    thunk_code_.reset(MemMap::Map(kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC));
     CHECK(thunk_code_ !=  NULL);
     Assembler thk_asm;
     // TODO: shouldn't have machine specific code in a general purpose file
diff --git a/src/mark_stack.cc b/src/mark_stack.cc
index 19dc1c1..663cdf7 100644
--- a/src/mark_stack.cc
+++ b/src/mark_stack.cc
@@ -10,9 +10,9 @@
 
 namespace art {
 
-MarkStack* MarkStack::Create(size_t maximum_size) {
+MarkStack* MarkStack::Create() {
   scoped_ptr<MarkStack> mark_stack(new MarkStack());
-  bool success = mark_stack->Init(maximum_size);
+  bool success = mark_stack->Init();
   if (!success) {
     return NULL;
   } else {
@@ -20,11 +20,10 @@
   }
 }
 
-bool MarkStack::Init(size_t maximum_size) {
+bool MarkStack::Init() {
   size_t length = 64 * MB;
-  mem_map_.reset(MemMap::Map(length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS));
+  mem_map_.reset(MemMap::Map(length, PROT_READ | PROT_WRITE));
   if (mem_map_ == NULL) {
-    PLOG(ERROR) << "mmap failed";
     return false;
   }
   byte* addr = mem_map_->GetAddress();
diff --git a/src/mark_stack.h b/src/mark_stack.h
index 38252c2..79026cc 100644
--- a/src/mark_stack.h
+++ b/src/mark_stack.h
@@ -14,7 +14,7 @@
 
 class MarkStack {
  public:
-  static MarkStack* Create(size_t maximum_size);
+  static MarkStack* Create();
 
   ~MarkStack();
 
@@ -41,7 +41,7 @@
       base_(NULL), limit_(NULL), ptr_(NULL) {
   }
 
-  bool Init(size_t maximum_size);
+  bool Init();
 
   // Memory mapping of the mark stack.
   scoped_ptr<MemMap> mem_map_;
diff --git a/src/mark_sweep.cc b/src/mark_sweep.cc
index 06a1c92..8541f76 100644
--- a/src/mark_sweep.cc
+++ b/src/mark_sweep.cc
@@ -22,7 +22,7 @@
 size_t MarkSweep::finalizer_reference_zombie_offset_ = 0;  // TODO
 
 bool MarkSweep::Init() {
-  mark_stack_ = MarkStack::Create(Heap::GetMaximumSize());
+  mark_stack_ = MarkStack::Create();
   if (mark_stack_ == NULL) {
     return false;
   }
diff --git a/src/mem_map.h b/src/mem_map.h
index 8645f44..f44939c 100644
--- a/src/mem_map.h
+++ b/src/mem_map.h
@@ -30,18 +30,28 @@
   // Request an anonymous region of a specified length.
   //
   // On success, returns returns a MemMap instance.  On failure, returns a NULL;
-  static MemMap* Map(size_t length, int prot, int flags) {
+  static MemMap* Map(size_t length, int prot) {
+    return Map(NULL, length, prot);
+  }
+
+  // Request an anonymous region of a specified length and a requested base address.
+  //
+  // On success, returns returns a MemMap instance.  On failure, returns a NULL;
+  static MemMap* Map(byte* addr, size_t length, int prot) {
+    CHECK_NE(0U, length);
+    CHECK_NE(0, prot);
     size_t page_aligned_size = RoundUp(length, kPageSize);
-    byte* addr = reinterpret_cast<byte*>(mmap(NULL,
-                                              page_aligned_size,
-                                              prot,
-                                              MAP_ANONYMOUS | flags,
-                                              -1,
-                                              0));
-    if (addr == MAP_FAILED) {
+    byte* actual = reinterpret_cast<byte*>(mmap(addr,
+                                                page_aligned_size,
+                                                prot,
+                                                MAP_PRIVATE | MAP_ANONYMOUS,
+                                                -1,
+                                                0));
+    if (actual == MAP_FAILED) {
+      PLOG(ERROR) << "mmap failed";
       return NULL;
     }
-    return new MemMap(addr, length, addr, page_aligned_size);
+    return new MemMap(actual, length, actual, page_aligned_size);
   }
 
   // Map part of a file, taking care of non-page aligned offsets.  The
@@ -49,20 +59,33 @@
   //
   // On success, returns returns a MemMap instance.  On failure, returns a NULL;
   static MemMap* Map(size_t length, int prot, int flags, int fd, off_t start) {
+    return Map(NULL, length, prot, flags, fd, start);
+  }
+
+  // Map part of a file, taking care of non-page aligned offsets.  The
+  // "start" offset is absolute, not relative. This version allows
+  // requesting a specific address for the base of the mapping.
+  //
+  // On success, returns returns a MemMap instance.  On failure, returns a NULL;
+  static MemMap* Map(byte* addr, size_t length, int prot, int flags, int fd, off_t start) {
+    CHECK_NE(0U, length);
+    CHECK_NE(0, prot);
+    CHECK(flags & MAP_SHARED || flags & MAP_PRIVATE);
     // adjust to be page-aligned
     int page_offset = start % kPageSize;
     off_t page_aligned_offset = start - page_offset;
-    size_t page_aligned_size = length + page_offset;
-    byte* addr = reinterpret_cast<byte*>(mmap(NULL,
-                                              page_aligned_size,
-                                              prot,
-                                              MAP_FILE | flags,
-                                              fd,
-                                              page_aligned_offset));
-    if (addr == MAP_FAILED) {
+    size_t page_aligned_size = RoundUp(length + page_offset, kPageSize);
+    byte* actual = reinterpret_cast<byte*>(mmap(addr,
+                                                page_aligned_size,
+                                                prot,
+                                                flags,
+                                                fd,
+                                                page_aligned_offset));
+    if (actual == MAP_FAILED) {
+      PLOG(ERROR) << "mmap failed";
       return NULL;
     }
-    return new MemMap(addr+page_offset, length, addr, page_aligned_size);
+    return new MemMap(actual + page_offset, length, actual, page_aligned_size);
   }
 
   ~MemMap() {
diff --git a/src/object.h b/src/object.h
index da4d6f3..1789ec8 100644
--- a/src/object.h
+++ b/src/object.h
@@ -712,7 +712,7 @@
   Object* packages_;
   ClassLoader* parent_;
 
-  // TODO remove once we can create a real PathClassLoader
+  // TODO: remove once we can create a real PathClassLoader
   std::vector<const DexFile*> class_path_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(ClassLoader);
@@ -1293,7 +1293,7 @@
                                 int32_t hash_code) {
     String* string = Alloc(GetJavaLangString(),
                            utf16_length);
-    // TODO use 16-bit wide memset variant
+    // TODO: use 16-bit wide memset variant
     for (int i = 0; i < utf16_length; i++ ) {
         string->array_->Set(i, utf16_data_in[i]);
     }
@@ -1435,7 +1435,7 @@
   }
 
   bool Equals(const String* that) const {
-    // TODO short circuit on hash_code_
+    // TODO: short circuit on hash_code_
     if (this->GetLength() != that->GetLength()) {
       return false;
     }
diff --git a/src/object_bitmap.cc b/src/object_bitmap.cc
index fc3c2ee4..7de4988 100644
--- a/src/object_bitmap.cc
+++ b/src/object_bitmap.cc
@@ -36,9 +36,8 @@
 bool HeapBitmap::Init(const byte* base, size_t max_size) {
   CHECK(base != NULL);
   size_t length = HB_OFFSET_TO_INDEX(max_size) * kWordSize;
-  mem_map_.reset(MemMap::Map(length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS));
+  mem_map_.reset(MemMap::Map(length, PROT_READ | PROT_WRITE));
   if (mem_map_ == NULL) {
-    LOG(ERROR) << "mmap failed";
     return false;
   }
   words_ = reinterpret_cast<word*>(mem_map_->GetAddress());
diff --git a/src/os_linux.cc b/src/os_linux.cc
index 8f5f56a..6d84b51 100644
--- a/src/os_linux.cc
+++ b/src/os_linux.cc
@@ -30,7 +30,7 @@
 bool OS::FileExists(const char* name) {
   struct stat st;
   if (stat(name, &st) == 0) {
-    return S_ISREG(st.st_mode);  // TODO Deal with symlinks?
+    return S_ISREG(st.st_mode);  // TODO: Deal with symlinks?
   } else {
     return false;
   }
diff --git a/src/runtime.cc b/src/runtime.cc
index 6108fe8..43427df 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -22,7 +22,7 @@
   Heap::Destroy();
   delete thread_list_;
   // TODO: acquire a static mutex on Runtime to avoid racing.
-  CHECK(instance_ == this);
+  CHECK(instance_ == NULL || instance_ == this);
   instance_ = NULL;
 }
 
@@ -231,7 +231,7 @@
         if (ignore_unrecognized) {
           continue;
         }
-        // TODO usage
+        // TODO: usage
         LOG(FATAL) << "Could not parse " << option;
         return NULL;
       }
@@ -242,7 +242,7 @@
         if (ignore_unrecognized) {
           continue;
         }
-        // TODO usage
+        // TODO: usage
         LOG(FATAL) << "Could not parse " << option;
         return NULL;
       }
@@ -253,7 +253,7 @@
         if (ignore_unrecognized) {
           continue;
         }
-        // TODO usage
+        // TODO: usage
         LOG(FATAL) << "Could not parse " << option;
         return NULL;
       }
@@ -329,13 +329,18 @@
   stack_size_ = options->stack_size_;
   thread_list_ = ThreadList::Create();
 
-  Heap::Init(options->heap_initial_size_,
-             options->heap_maximum_size_);
+  if (!Heap::Init(options->heap_initial_size_,
+                  options->heap_maximum_size_,
+                  options->boot_image_)) {
+    return false;
+  }
 
   bool verbose_jni = options->verbose_.find("jni") != options->verbose_.end();
   java_vm_.reset(new JavaVMExt(this, options->check_jni_, verbose_jni));
 
-  Thread::Init();
+  if (!Thread::Init()) {
+    return false;
+  }
   Thread* current_thread = Thread::Attach(this);
   thread_list_->Register(current_thread);
 
diff --git a/src/space.cc b/src/space.cc
index c3d763c..8048f29 100644
--- a/src/space.cc
+++ b/src/space.cc
@@ -4,16 +4,19 @@
 
 #include <sys/mman.h>
 
+#include "file.h"
+#include "image.h"
 #include "logging.h"
 #include "mspace.h"
+#include "os.h"
 #include "scoped_ptr.h"
 #include "utils.h"
 
 namespace art {
 
-Space* Space::Create(size_t startup_size, size_t maximum_size) {
-  scoped_ptr<Space> space(new Space(startup_size, maximum_size));
-  bool success = space->Init();
+Space* Space::Create(size_t initial_size, size_t maximum_size, byte* requested_base) {
+  scoped_ptr<Space> space(new Space());
+  bool success = space->Init(initial_size, maximum_size, requested_base);
   if (!success) {
     return NULL;
   } else {
@@ -21,18 +24,31 @@
   }
 }
 
+Space* Space::Create(const char* image_file_name) {
+  CHECK(image_file_name != NULL);
+  scoped_ptr<Space> space(new Space());
+  bool success = space->Init(image_file_name);
+  if (!success) {
+    return NULL;
+  } else {
+    return space.release();
+  }
+}
+
+Space::~Space() {}
+
 void* Space::CreateMallocSpace(void* base,
-                               size_t startup_size,
+                               size_t initial_size,
                                size_t maximum_size) {
   errno = 0;
   bool is_locked = false;
-  size_t commit_size = startup_size / 2;
+  size_t commit_size = initial_size / 2;
   void* msp = create_contiguous_mspace_with_base(commit_size, maximum_size,
                                                  is_locked, base);
   if (msp != NULL) {
     // Do not permit the heap grow past the starting size without our
     // intervention.
-    mspace_set_max_allowed_footprint(msp, startup_size);
+    mspace_set_max_allowed_footprint(msp, initial_size);
   } else {
     // There is no guarantee that errno has meaning when the call
     // fails, but it often does.
@@ -41,35 +57,60 @@
   return msp;
 }
 
-bool Space::Init() {
-  if (!(startup_size_ <= maximum_size_)) {
+bool Space::Init(size_t initial_size, size_t maximum_size, byte* requested_base) {
+  if (!(initial_size <= maximum_size)) {
     return false;
   }
-  size_t length = RoundUp(maximum_size_, kPageSize);
+  size_t length = RoundUp(maximum_size, kPageSize);
   int prot = PROT_READ | PROT_WRITE;
-  int flags = MAP_PRIVATE | MAP_ANONYMOUS;
-  mem_map_.reset(MemMap::Map(length, prot, flags));
-  if (mem_map_ == NULL) {
-    PLOG(ERROR) << "mmap failed";
+  scoped_ptr<MemMap> mem_map(MemMap::Map(requested_base, length, prot));
+  if (mem_map == NULL) {
     return false;
   }
+  Init(mem_map.release());
+  maximum_size_ = maximum_size;
+  mspace_ = CreateMallocSpace(base_, initial_size, maximum_size);
+  return (mspace_ != NULL);
+}
+
+void Space::Init(MemMap* mem_map) {
+  mem_map_.reset(mem_map);
   base_ = mem_map_->GetAddress();
-  limit_ = base_ + length;
-  mspace_ = CreateMallocSpace(base_, startup_size_, maximum_size_);
-  if (mspace_ == NULL) {
-    mem_map_->Unmap();
+  limit_ = base_ + mem_map->GetLength();
+}
+
+
+bool Space::Init(const char* image_file_name) {
+  scoped_ptr<File> file(OS::OpenFile(image_file_name, false));
+  if (file == NULL) {
     return false;
   }
+  ImageHeader image_header;
+  bool success = file->ReadFully(&image_header, sizeof(image_header));
+  if (!success || !image_header.IsValid()) {
+    return false;
+  }
+  scoped_ptr<MemMap> map(MemMap::Map(image_header.GetBaseAddr(),
+                                     file->Length(),
+                                     PROT_READ | PROT_WRITE,
+                                     MAP_PRIVATE | MAP_FIXED,
+                                     file->Fd(),
+                                     0));
+  if (map == NULL) {
+    return false;
+  }
+  CHECK_EQ(image_header.GetBaseAddr(), map->GetAddress());
+  Init(map.release());
   return true;
 }
 
-Space::~Space() {}
-
 Object* Space::AllocWithoutGrowth(size_t num_bytes) {
+  DCHECK(mspace_ != NULL);
   return reinterpret_cast<Object*>(mspace_calloc(mspace_, 1, num_bytes));
 }
 
 Object* Space::AllocWithGrowth(size_t num_bytes) {
+  DCHECK(mspace_ != NULL);
   // Grow as much as possible within the mspace.
   size_t max_allowed = maximum_size_;
   mspace_set_max_allowed_footprint(mspace_, max_allowed);
@@ -83,6 +124,7 @@
 }
 
 size_t Space::Free(void* ptr) {
+  DCHECK(mspace_ != NULL);
   DCHECK(ptr != NULL);
   size_t num_bytes = mspace_usable_size(mspace_, ptr);
   mspace_free(mspace_, ptr);
@@ -90,6 +132,7 @@
 }
 
 size_t Space::AllocationSize(const Object* obj) {
+  DCHECK(mspace_ != NULL);
   return mspace_usable_size(mspace_, obj) + kChunkOverhead;
 }
 
@@ -116,6 +159,7 @@
 }
 
 size_t Space::MaxAllowedFootprint() {
+  DCHECK(mspace_ != NULL);
   return mspace_max_allowed_footprint(mspace_);
 }
 
diff --git a/src/space.h b/src/space.h
index 7b72bba..57f676d 100644
--- a/src/space.h
+++ b/src/space.h
@@ -15,7 +15,11 @@
 // A space contains memory allocated for managed objects.
 class Space {
  public:
-  static Space* Create(size_t startup_size, size_t maximum_size);
+  // create a Space with the requested sizes requesting a specific base address.
+  static Space* Create(size_t initial_size, size_t maximum_size, byte* requested_base);
+
+  // create a Space from an image file. cannot be used for future allocation or collected.
+  static Space* Create(const char* image);
 
   ~Space();
 
@@ -53,22 +57,27 @@
   // The boundary tag overhead.
   static const size_t kChunkOverhead = kWordSize;
 
-  Space(size_t startup_size, size_t maximum_size) :
-      mspace_(NULL),
-      base_(NULL),
-      startup_size_(startup_size),
-      maximum_size_(maximum_size) {
-  }
+  // create a Space from an existing memory mapping, taking ownership of the address space.
+  static Space* Create(MemMap* mem_map);
+
+  Space() : mspace_(NULL), maximum_size_(0), base_(0), limit_(0) {}
 
   // Initializes the space and underlying storage.
-  bool Init();
+  bool Init(size_t initial_size, size_t maximum_size, byte* requested_base);
 
-  void* CreateMallocSpace(void* base, size_t startup_size,
-                          size_t maximum_size);
+  // Initializes the space from existing storage, taking ownership of the storage.
+  void Init(MemMap* map);
+
+  // Initializes the space from an image file
+  bool Init(const char* image_file_name);
+
+  void* CreateMallocSpace(void* base, size_t initial_size, size_t maximum_size);
 
   static void DontNeed(void* start, void* end, void* num_bytes);
 
+  // TODO: have a Space subclass for methods that depend on mspace_ and maximum_size_
   void* mspace_;
+  size_t maximum_size_;
 
   scoped_ptr<MemMap> mem_map_;
 
@@ -76,13 +85,9 @@
 
   byte* limit_;
 
-  size_t startup_size_;
+  // bool is_condemned_;  // TODO: with IsCondemned
 
-  size_t maximum_size_;
-
-  bool is_condemned_;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(Space);
+  DISALLOW_COPY_AND_ASSIGN(Space);
 };
 
 }  // namespace art
diff --git a/src/space_test.cc b/src/space_test.cc
index f3c713b..7384021 100644
--- a/src/space_test.cc
+++ b/src/space_test.cc
@@ -12,23 +12,23 @@
 TEST(SpaceTest, Init) {
   {
     // Less than
-    scoped_ptr<Space> space(Space::Create(16 * MB, 32 * MB));
+    scoped_ptr<Space> space(Space::Create(16 * MB, 32 * MB, NULL));
     EXPECT_TRUE(space != NULL);
   }
   {
     // Equal to
-    scoped_ptr<Space> space(Space::Create(16 * MB, 16 * MB));
+    scoped_ptr<Space> space(Space::Create(16 * MB, 16 * MB, NULL));
     EXPECT_TRUE(space != NULL);
   }
   {
     // Greater than
-    scoped_ptr<Space> space(Space::Create(32 * MB, 16 * MB));
+    scoped_ptr<Space> space(Space::Create(32 * MB, 16 * MB, NULL));
     EXPECT_TRUE(space == NULL);
   }
 }
 
 TEST(SpaceTest, AllocAndFree) {
-  scoped_ptr<Space> space(Space::Create(4 * MB, 16 * MB));
+  scoped_ptr<Space> space(Space::Create(4 * MB, 16 * MB, NULL));
   ASSERT_TRUE(space != NULL);
 
   // Succeeds, fits without adjusting the max allowed footprint.
diff --git a/src/thread.cc b/src/thread.cc
index aa3fe95..1001b29 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -55,7 +55,7 @@
 
 Thread* Thread::Create(const Runtime* runtime) {
   size_t stack_size = runtime->GetStackSize();
-  scoped_ptr<MemMap> stack(MemMap::Map(stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE));
+  scoped_ptr<MemMap> stack(MemMap::Map(stack_size, PROT_READ | PROT_WRITE));
   if (stack == NULL) {
     LOG(FATAL) << "failed to allocate thread stack";
     // notreached
@@ -212,9 +212,9 @@
   // Make sure that all threads have exited and unregistered when we
   // reach this point. This means that all daemon threads had been
   // shutdown cleanly.
-  CHECK_EQ(list_.size(), 1U);
+  CHECK_LE(list_.size(), 1U);
   // TODO: wait for all other threads to unregister
-  CHECK_EQ(list_.front(), Thread::Current());
+  CHECK(list_.size() == 0 || list_.front() == Thread::Current());
   // TODO: detach the current thread
   delete lock_;
   lock_ = NULL;
diff --git a/src/zip_archive.cc b/src/zip_archive.cc
index fe263ba..cccaf82 100644
--- a/src/zip_archive.cc
+++ b/src/zip_archive.cc
@@ -386,7 +386,6 @@
   // It all looks good.  Create a mapping for the CD.
   dir_map_.reset(MemMap::Map(dir_size, PROT_READ, MAP_SHARED, fd_, dir_offset));
   if (dir_map_ == NULL) {
-    LOG(WARNING) << "Zip: cd map failed " << strerror(errno);
     return false;
   }