ART stack unwinding fixes for libunwind/gdb/lldb.

dex2oat can already generate unwinding and symbol information which
allows tools to create backtrace of mixed native and Java code.

This is a cherry pick from aosp/master which fixes several issues.
Most notably:
 * It enables generation of ELF-64 on 64-bit systems (in dex2oat, C
   compilers already produce ELF-64).  Libunwind requires ELF-64 on
   64-bit systems for backtraces to work.
 * It enables loading of ELF files with dlopen.  This is required for
   libunwind to be able to generate backtrace of current process (i.e.
   the process requesting backtrace of itself).
 * It adds unit test to test the above (32 vs 64 bit, in-proces vs
   out-of-process, application code vs framework code).
 * Some other fixes or clean-ups which should not be of much
   significance but which are easier to include to make the
   important CLs cherry-pick cleanly.

This is squash of the following commits from aosp/master:
  7381010 ART: CFI Test
  e1bbed2 ART: Blacklist CFI test for non-compiled run-tests
  aab9f73 ART: Blacklist CFI test for JIT
  4437219 ART: Blacklist CFI test for Heap Poisoning
  a3a49fe Switch to using ELF-64 for 64-bit architectures.
  297ed22 Write 64-bit address in DWARF if we are on 64-bit architecture.
  24981a1 Set correct size of PT_PHDR ELF segment.
  1a146bf Link .dynamic to .dynstr
  67a0653 Make some parts of ELF more (pointer) aligned.
  f50fa82 Enable 64-bit CFI tests.
  49e1fab Use dlopen to load oat files.
  5dedb80 Add more logging output for dlopen.
  aa03870 Find the dlopened file using address rather than file path.
  82e73dc Release dummy MemMaps corresponding to dlopen.
  5c40961 Test that we can unwind framework code.
  020c543 Add more log output to the CFI test.
  88da3b0 ART: Fix CFI test wrt/ PIC
  a70e5b9 CFI test: kill the other process in native code.
  ad5fa8c Support generation of CFI in .debug_frame format.
  90688ae Fix build - large frame size of ElfWriterQuick<ElfTypes>::Write.
  97dabb7 Fix build breakage in dwarf_test.
  388d286 Generate just single ARM mapping symbol.
  f898087 Split .oat_patches to multiple sections.
  491a7fe Fix build - large frame size of ElfWriterQuick<ElfTypes>::Write (again).
  8363c77 Add --generate-debug-info flag and remove the other two flags.
  461d72a Generate debug info for core.oat files.

Bug: 21924613
Change-Id: I3f944a08dd2ed1df4d8a807da4fee423fdd35eb7
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 710b130..728469c 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -113,7 +113,7 @@
 	  --oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
 	  --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(3)ART_HOST_ARCH) \
 	  --instruction-set-features=$$($(3)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES) \
-	  --host --android-root=$$(HOST_OUT) --include-patch-information \
+	  --host --android-root=$$(HOST_OUT) --include-patch-information --generate-debug-info \
 	  $$(PRIVATE_CORE_COMPILE_OPTIONS)
 
 $$(core_oat_name): $$(core_image_name)
@@ -232,7 +232,7 @@
 	  --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(3)TARGET_ARCH) \
 	  --instruction-set-variant=$$($(3)DEX2OAT_TARGET_CPU_VARIANT) \
 	  --instruction-set-features=$$($(3)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
-	  --android-root=$$(PRODUCT_OUT)/system --include-patch-information \
+	  --android-root=$$(PRODUCT_OUT)/system --include-patch-information --generate-debug-info \
 	  $$(PRIVATE_CORE_COMPILE_OPTIONS) || (rm $$(PRIVATE_CORE_OAT_NAME); exit 1)
 
 $$(core_oat_name): $$(core_image_name)
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h
index f7501d2..5e345db 100644
--- a/compiler/cfi_test.h
+++ b/compiler/cfi_test.h
@@ -30,6 +30,8 @@
 
 namespace art {
 
+constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT;
+
 class CFITest : public dwarf::DwarfTest {
  public:
   void GenerateExpected(FILE* f, InstructionSet isa, const char* isa_str,
@@ -46,11 +48,11 @@
     // Pretty-print CFI opcodes.
     constexpr bool is64bit = false;
     dwarf::DebugFrameOpCodeWriter<> initial_opcodes;
-    dwarf::WriteEhFrameCIE(is64bit, dwarf::DW_EH_PE_absptr, dwarf::Reg(8),
-                           initial_opcodes, &eh_frame_data_);
-    std::vector<uintptr_t> eh_frame_patches;
-    dwarf::WriteEhFrameFDE(is64bit, 0, 0, actual_asm.size(), &actual_cfi,
-                           &eh_frame_data_, &eh_frame_patches);
+    dwarf::WriteDebugFrameCIE(is64bit, dwarf::DW_EH_PE_absptr, dwarf::Reg(8),
+                              initial_opcodes, kCFIFormat, &debug_frame_data_);
+    std::vector<uintptr_t> debug_frame_patches;
+    dwarf::WriteDebugFrameFDE(is64bit, 0, 0, actual_asm.size(), &actual_cfi,
+                              kCFIFormat, &debug_frame_data_, &debug_frame_patches);
     ReformatCfi(Objdump(false, "-W"), &lines);
     // Pretty-print assembly.
     auto* opts = new DisassemblerOptions(false, actual_asm.data(), true);
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index f4bf31f..c803e65 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -670,7 +670,7 @@
 
 
 void Mir2Lir::CreateMappingTables() {
-  bool generate_src_map = cu_->compiler_driver->GetCompilerOptions().GetIncludeDebugSymbols();
+  bool generate_src_map = cu_->compiler_driver->GetCompilerOptions().GetGenerateDebugInfo();
 
   uint32_t pc2dex_data_size = 0u;
   uint32_t pc2dex_entries = 0u;
@@ -1071,7 +1071,7 @@
       pc_rel_temp_(nullptr),
       dex_cache_arrays_min_offset_(std::numeric_limits<uint32_t>::max()),
       cfi_(&last_lir_insn_,
-           cu->compiler_driver->GetCompilerOptions().GetIncludeCFI(),
+           cu->compiler_driver->GetCompilerOptions().GetGenerateDebugInfo(),
            arena),
       in_to_reg_storage_mapping_(arena) {
   switch_tables_.reserve(4);
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
index 8694ebc..dd68dd4 100644
--- a/compiler/dex/quick/quick_cfi_test.cc
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -59,8 +59,7 @@
       false,
       CompilerOptions::kDefaultTopKProfileThreshold,
       false,
-      true,  // include_debug_symbols.
-      true,  // include_cfi
+      true,  // generate_debug_info.
       false,
       false,
       false,
diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
index f58f206..798e23f 100644
--- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc
+++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
@@ -42,8 +42,7 @@
         false,
         CompilerOptions::kDefaultTopKProfileThreshold,
         false,
-        false,
-        false,
+        CompilerOptions::kDefaultGenerateDebugInfo,
         false,
         false,
         false,
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e963c12..7b8b5b0 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -76,8 +76,8 @@
 
 static constexpr bool kTimeCompileMethod = !kIsDebugBuild;
 
-// Whether to produce 64-bit ELF files for 64-bit targets. Leave this off for now.
-static constexpr bool kProduce64BitELFFiles = false;
+// Whether to produce 64-bit ELF files for 64-bit targets.
+static constexpr bool kProduce64BitELFFiles = true;
 
 // Whether classes-to-compile and methods-to-compile are only applied to the boot image, or, when
 // given, too all compilations.
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index c5fc98a..226e6b7 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -30,8 +30,7 @@
       include_patch_information_(kDefaultIncludePatchInformation),
       top_k_profile_threshold_(kDefaultTopKProfileThreshold),
       debuggable_(false),
-      include_debug_symbols_(kDefaultIncludeDebugSymbols),
-      include_cfi_(false),
+      generate_debug_info_(kDefaultGenerateDebugInfo),
       implicit_null_checks_(true),
       implicit_so_checks_(true),
       implicit_suspend_checks_(false),
@@ -56,8 +55,7 @@
                                  bool include_patch_information,
                                  double top_k_profile_threshold,
                                  bool debuggable,
-                                 bool include_debug_symbols,
-                                 bool include_cfi,
+                                 bool generate_debug_info,
                                  bool implicit_null_checks,
                                  bool implicit_so_checks,
                                  bool implicit_suspend_checks,
@@ -76,8 +74,7 @@
     include_patch_information_(include_patch_information),
     top_k_profile_threshold_(top_k_profile_threshold),
     debuggable_(debuggable),
-    include_debug_symbols_(include_debug_symbols),
-    include_cfi_(include_cfi),
+    generate_debug_info_(generate_debug_info),
     implicit_null_checks_(implicit_null_checks),
     implicit_so_checks_(implicit_so_checks),
     implicit_suspend_checks_(implicit_suspend_checks),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index bf3f8ec..356663b 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -49,7 +49,7 @@
   static const size_t kDefaultTinyMethodThreshold = 20;
   static const size_t kDefaultNumDexMethodsThreshold = 900;
   static constexpr double kDefaultTopKProfileThreshold = 90.0;
-  static const bool kDefaultIncludeDebugSymbols = kIsDebugBuild;
+  static const bool kDefaultGenerateDebugInfo = kIsDebugBuild;
   static const bool kDefaultIncludePatchInformation = false;
 
   CompilerOptions();
@@ -64,8 +64,7 @@
                   bool include_patch_information,
                   double top_k_profile_threshold,
                   bool debuggable,
-                  bool include_debug_symbols,
-                  bool include_cfi,
+                  bool generate_debug_info,
                   bool implicit_null_checks,
                   bool implicit_so_checks,
                   bool implicit_suspend_checks,
@@ -146,13 +145,8 @@
     return debuggable_;
   }
 
-  bool GetIncludeDebugSymbols() const {
-    return include_debug_symbols_;
-  }
-
-  bool GetIncludeCFI() const {
-    // include-debug-symbols implies include-cfi.
-    return include_cfi_ || include_debug_symbols_;
+  bool GetGenerateDebugInfo() const {
+    return generate_debug_info_;
   }
 
   bool GetImplicitNullChecks() const {
@@ -212,8 +206,7 @@
   // When using a profile file only the top K% of the profiled samples will be compiled.
   const double top_k_profile_threshold_;
   const bool debuggable_;
-  const bool include_debug_symbols_;
-  const bool include_cfi_;
+  const bool generate_debug_info_;
   const bool implicit_null_checks_;
   const bool implicit_so_checks_;
   const bool implicit_suspend_checks_;
diff --git a/compiler/dwarf/dwarf_constants.h b/compiler/dwarf/dwarf_constants.h
index 61a44cd..3b570e5 100644
--- a/compiler/dwarf/dwarf_constants.h
+++ b/compiler/dwarf/dwarf_constants.h
@@ -680,6 +680,14 @@
   DW_EH_PE_aligned = 0x50,
 };
 
+enum CFIFormat : uint8_t {
+  // This is the original format as defined by the specification.
+  // It is used for the .debug_frame section.
+  DW_DEBUG_FRAME_FORMAT,
+  // Slightly modified format used for the .eh_frame section.
+  DW_EH_FRAME_FORMAT
+};
+
 }  // namespace dwarf
 }  // namespace art
 
diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc
index edba00a..4d423d0 100644
--- a/compiler/dwarf/dwarf_test.cc
+++ b/compiler/dwarf/dwarf_test.cc
@@ -29,6 +29,8 @@
 // Run the tests only on host since we need objdump.
 #ifndef HAVE_ANDROID_OS
 
+constexpr CFIFormat kCFIFormat = DW_DEBUG_FRAME_FORMAT;
+
 TEST_F(DwarfTest, DebugFrame) {
   const bool is64bit = false;
 
@@ -120,30 +122,30 @@
   DW_CHECK_NEXT("DW_CFA_restore: r5 (ebp)");
 
   DebugFrameOpCodeWriter<> initial_opcodes;
-  WriteEhFrameCIE(is64bit, DW_EH_PE_absptr, Reg(is64bit ? 16 : 8),
-                  initial_opcodes, &eh_frame_data_);
-  std::vector<uintptr_t> eh_frame_patches;
+  WriteDebugFrameCIE(is64bit, DW_EH_PE_absptr, Reg(is64bit ? 16 : 8),
+                     initial_opcodes, kCFIFormat, &debug_frame_data_);
+  std::vector<uintptr_t> debug_frame_patches;
   std::vector<uintptr_t> expected_patches { 28 };  // NOLINT
-  WriteEhFrameFDE(is64bit, 0, 0x01000000, 0x01000000, opcodes.data(),
-                  &eh_frame_data_, &eh_frame_patches);
+  WriteDebugFrameFDE(is64bit, 0, 0x01000000, 0x01000000, opcodes.data(),
+                     kCFIFormat, &debug_frame_data_, &debug_frame_patches);
 
-  EXPECT_EQ(expected_patches, eh_frame_patches);
+  EXPECT_EQ(expected_patches, debug_frame_patches);
   CheckObjdumpOutput(is64bit, "-W");
 }
 
 TEST_F(DwarfTest, DebugFrame64) {
   constexpr bool is64bit = true;
   DebugFrameOpCodeWriter<> initial_opcodes;
-  WriteEhFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16),
-                  initial_opcodes, &eh_frame_data_);
+  WriteDebugFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16),
+                     initial_opcodes, kCFIFormat, &debug_frame_data_);
   DebugFrameOpCodeWriter<> opcodes;
-  std::vector<uintptr_t> eh_frame_patches;
+  std::vector<uintptr_t> debug_frame_patches;
   std::vector<uintptr_t> expected_patches { 32 };  // NOLINT
-  WriteEhFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
-                  opcodes.data(), &eh_frame_data_, &eh_frame_patches);
+  WriteDebugFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
+                     opcodes.data(), kCFIFormat, &debug_frame_data_, &debug_frame_patches);
   DW_CHECK("FDE cie=00000000 pc=100000000000000..300000000000000");
 
-  EXPECT_EQ(expected_patches, eh_frame_patches);
+  EXPECT_EQ(expected_patches, debug_frame_patches);
   CheckObjdumpOutput(is64bit, "-W");
 }
 
@@ -173,11 +175,11 @@
   DW_CHECK_NEXT("DW_CFA_offset: r14 (r14)");
   DW_CHECK_NEXT("DW_CFA_offset: r15 (r15)");
   DebugFrameOpCodeWriter<> initial_opcodes;
-  WriteEhFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16),
-                  initial_opcodes, &eh_frame_data_);
-  std::vector<uintptr_t> eh_frame_patches;
-  WriteEhFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
-                  opcodes.data(), &eh_frame_data_, &eh_frame_patches);
+  WriteDebugFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16),
+                     initial_opcodes, kCFIFormat, &debug_frame_data_);
+  std::vector<uintptr_t> debug_frame_patches;
+  WriteDebugFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
+                     opcodes.data(), kCFIFormat, &debug_frame_data_, &debug_frame_patches);
 
   CheckObjdumpOutput(is64bit, "-W");
 }
diff --git a/compiler/dwarf/dwarf_test.h b/compiler/dwarf/dwarf_test.h
index 370c744..f819c49 100644
--- a/compiler/dwarf/dwarf_test.h
+++ b/compiler/dwarf/dwarf_test.h
@@ -68,7 +68,7 @@
     RawSection debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
     RawSection debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
     RawSection debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-    RawSection eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0);
+    RawSection debug_frame(".debug_frame", SHT_PROGBITS, 0, nullptr, 0, 8, 0);
     if (!debug_info_data_.empty()) {
       debug_info.SetBuffer(debug_info_data_);
       builder.RegisterSection(&debug_info);
@@ -85,9 +85,9 @@
       debug_line.SetBuffer(debug_line_data_);
       builder.RegisterSection(&debug_line);
     }
-    if (!eh_frame_data_.empty()) {
-      eh_frame.SetBuffer(eh_frame_data_);
-      builder.RegisterSection(&eh_frame);
+    if (!debug_frame_data_.empty()) {
+      debug_frame.SetBuffer(debug_frame_data_);
+      builder.RegisterSection(&debug_frame);
     }
     ScratchFile file;
     builder.Write(file.GetFile());
@@ -166,7 +166,7 @@
   }
 
   // Buffers which are going to assembled into ELF file and passed to objdump.
-  std::vector<uint8_t> eh_frame_data_;
+  std::vector<uint8_t> debug_frame_data_;
   std::vector<uint8_t> debug_info_data_;
   std::vector<uint8_t> debug_abbrev_data_;
   std::vector<uint8_t> debug_str_data_;
diff --git a/compiler/dwarf/headers.h b/compiler/dwarf/headers.h
index 9f64766..ad315ee 100644
--- a/compiler/dwarf/headers.h
+++ b/compiler/dwarf/headers.h
@@ -35,17 +35,18 @@
 // and compilers are expected *not* to use it by default.
 // In particular, it is not related to machine architecture.
 
-// Write common information entry (CIE) to .eh_frame section.
+// Write common information entry (CIE) to .debug_frame or .eh_frame section.
 template<typename Allocator>
-void WriteEhFrameCIE(bool is64bit,
-                     ExceptionHeaderValueApplication address_type,
-                     Reg return_address_register,
-                     const DebugFrameOpCodeWriter<Allocator>& opcodes,
-                     std::vector<uint8_t>* eh_frame) {
-  Writer<> writer(eh_frame);
+void WriteDebugFrameCIE(bool is64bit,
+                        ExceptionHeaderValueApplication address_type,
+                        Reg return_address_register,
+                        const DebugFrameOpCodeWriter<Allocator>& opcodes,
+                        CFIFormat format,
+                        std::vector<uint8_t>* debug_frame) {
+  Writer<> writer(debug_frame);
   size_t cie_header_start_ = writer.data()->size();
   writer.PushUint32(0);  // Length placeholder.
-  writer.PushUint32(0);  // CIE id.
+  writer.PushUint32((format == DW_EH_FRAME_FORMAT) ? 0 : 0xFFFFFFFF);  // CIE id.
   writer.PushUint8(1);   // Version.
   writer.PushString("zR");
   writer.PushUleb128(DebugFrameOpCodeWriter<Allocator>::kCodeAlignmentFactor);
@@ -62,20 +63,26 @@
   writer.UpdateUint32(cie_header_start_, writer.data()->size() - cie_header_start_ - 4);
 }
 
-// Write frame description entry (FDE) to .eh_frame section.
+// Write frame description entry (FDE) to .debug_frame or .eh_frame section.
 template<typename Allocator>
-void WriteEhFrameFDE(bool is64bit, size_t cie_offset,
-                     uint64_t initial_address, uint64_t address_range,
-                     const std::vector<uint8_t, Allocator>* opcodes,
-                     std::vector<uint8_t>* eh_frame,
-                     std::vector<uintptr_t>* eh_frame_patches) {
-  Writer<> writer(eh_frame);
+void WriteDebugFrameFDE(bool is64bit, size_t cie_offset,
+                        uint64_t initial_address, uint64_t address_range,
+                        const std::vector<uint8_t, Allocator>* opcodes,
+                        CFIFormat format,
+                        std::vector<uint8_t>* debug_frame,
+                        std::vector<uintptr_t>* debug_frame_patches) {
+  Writer<> writer(debug_frame);
   size_t fde_header_start = writer.data()->size();
   writer.PushUint32(0);  // Length placeholder.
-  uint32_t cie_pointer = writer.data()->size() - cie_offset;
-  writer.PushUint32(cie_pointer);
+  if (format == DW_EH_FRAME_FORMAT) {
+    uint32_t cie_pointer = writer.data()->size() - cie_offset;
+    writer.PushUint32(cie_pointer);
+  } else {
+    uint32_t cie_pointer = cie_offset;
+    writer.PushUint32(cie_pointer);
+  }
   // Relocate initial_address, but not address_range (it is size).
-  eh_frame_patches->push_back(writer.data()->size());
+  debug_frame_patches->push_back(writer.data()->size());
   if (is64bit) {
     writer.PushUint64(initial_address);
     writer.PushUint64(address_range);
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 2c68bb8..bbd962f 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -166,6 +166,10 @@
           patched_(false), patch_(patch), patch_base_section_(patch_base_section) {
     }
 
+    RawSection(const std::string& name, Elf_Word type)
+        : RawSection(name, type, 0, nullptr, 0, 1, 0, nullptr, nullptr) {
+    }
+
     Elf_Word GetSize() const OVERRIDE {
       return buffer_.size();
     }
@@ -263,7 +267,7 @@
   class StrtabSection FINAL : public Section {
    public:
     StrtabSection(const std::string& name, Elf_Word flags)
-        : Section(name, SHT_STRTAB, flags, nullptr, 0, 1, 1) {
+        : Section(name, SHT_STRTAB, flags, nullptr, 0, 1, 0) {
       buffer_.reserve(4 * KB);
       // The first entry of strtab must be empty string.
       buffer_ += '\0';
@@ -306,7 +310,7 @@
 
     SymtabSection(const std::string& name, Elf_Word type, Elf_Word flags,
                   StrtabSection* strtab)
-        : Section(name, type, flags, strtab, 0, sizeof(Elf_Word), sizeof(Elf_Sym)),
+        : Section(name, type, flags, strtab, 0, sizeof(Elf_Off), sizeof(Elf_Sym)),
           strtab_(strtab) {
     }
 
@@ -499,7 +503,7 @@
       text_(".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR,
             nullptr, 0, kPageSize, 0, text_size, text_writer),
       bss_(".bss", bss_size),
-      dynamic_(".dynamic", &dynsym_),
+      dynamic_(".dynamic", &dynstr_),
       strtab_(".strtab", 0),
       symtab_(".symtab", SHT_SYMTAB, 0, &strtab_),
       shstrtab_(".shstrtab", 0) {
@@ -641,11 +645,10 @@
     // It is easiest to just reserve a fixed amount of space for them.
     constexpr size_t kMaxProgramHeaders = 8;
     constexpr size_t kProgramHeadersOffset = sizeof(Elf_Ehdr);
-    constexpr size_t kProgramHeadersSize = sizeof(Elf_Phdr) * kMaxProgramHeaders;
 
     // Layout of all sections - determine the final file offsets and addresses.
     // This must be done after we have built all sections and know their size.
-    Elf_Off file_offset = kProgramHeadersOffset + kProgramHeadersSize;
+    Elf_Off file_offset = kProgramHeadersOffset + sizeof(Elf_Phdr) * kMaxProgramHeaders;
     Elf_Addr load_address = file_offset;
     std::vector<Elf_Shdr> section_headers;
     section_headers.reserve(1u + sections.size());
@@ -674,7 +677,7 @@
       // Collect section headers into continuous array for convenience.
       section_headers.push_back(*header);
     }
-    Elf_Off section_headers_offset = RoundUp(file_offset, sizeof(Elf_Word));
+    Elf_Off section_headers_offset = RoundUp(file_offset, sizeof(Elf_Off));
 
     // Create program headers now that we know the layout of the whole file.
     // Each segment contains one or more sections which are mapped together.
@@ -682,8 +685,7 @@
     // PT_LOAD does the mapping.  Other PT_* types allow the program to locate
     // interesting parts of memory and their addresses overlap with PT_LOAD.
     std::vector<Elf_Phdr> program_headers;
-    program_headers.push_back(MakeProgramHeader(PT_PHDR, PF_R,
-      kProgramHeadersOffset, kProgramHeadersSize, sizeof(Elf_Word)));
+    program_headers.push_back(Elf_Phdr());  // Placeholder for PT_PHDR.
     // Create the main LOAD R segment which spans all sections up to .rodata.
     const Elf_Shdr* rodata = rodata_.GetHeader();
     program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R,
@@ -709,6 +711,9 @@
         program_headers.push_back(MakeProgramHeader(PT_GNU_EH_FRAME, PF_R, *eh_frame_hdr));
       }
     }
+    DCHECK_EQ(program_headers[0].p_type, 0u);  // Check placeholder.
+    program_headers[0] = MakeProgramHeader(PT_PHDR, PF_R,
+      kProgramHeadersOffset, program_headers.size() * sizeof(Elf_Phdr), sizeof(Elf_Off));
     CHECK_LE(program_headers.size(), kMaxProgramHeaders);
 
     // Create the main ELF header.
@@ -777,10 +782,12 @@
 
   template<typename T>
   static bool WriteArray(File* elf_file, const T* data, size_t count) {
-    DCHECK(data != nullptr);
-    if (!elf_file->WriteFully(data, count * sizeof(T))) {
-      PLOG(ERROR) << "Failed to write to file " << elf_file->GetPath();
-      return false;
+    if (count != 0) {
+      DCHECK(data != nullptr);
+      if (!elf_file->WriteFully(data, count * sizeof(T))) {
+        PLOG(ERROR) << "Failed to write to file " << elf_file->GetPath();
+        return false;
+      }
     }
     return true;
   }
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index dbbe82e..c68bbc0 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -30,9 +30,10 @@
 namespace art {
 namespace dwarf {
 
-static void WriteEhFrameCIE(InstructionSet isa,
-                            ExceptionHeaderValueApplication addr_type,
-                            std::vector<uint8_t>* eh_frame) {
+static void WriteDebugFrameCIE(InstructionSet isa,
+                               ExceptionHeaderValueApplication addr_type,
+                               CFIFormat format,
+                               std::vector<uint8_t>* eh_frame) {
   // Scratch registers should be marked as undefined.  This tells the
   // debugger that its value in the previous frame is not recoverable.
   bool is64bit = Is64BitInstructionSet(isa);
@@ -58,7 +59,8 @@
         }
       }
       auto return_reg = Reg::ArmCore(14);  // R14(LR).
-      WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+      WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+                         opcodes, format, eh_frame);
       return;
     }
     case kArm64: {
@@ -81,7 +83,8 @@
         }
       }
       auto return_reg = Reg::Arm64Core(30);  // R30(LR).
-      WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+      WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+                         opcodes, format, eh_frame);
       return;
     }
     case kMips:
@@ -97,7 +100,8 @@
         }
       }
       auto return_reg = Reg::MipsCore(31);  // R31(RA).
-      WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+      WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+                         opcodes, format, eh_frame);
       return;
     }
     case kX86: {
@@ -123,7 +127,8 @@
         }
       }
       auto return_reg = Reg::X86Core(8);  // R8(EIP).
-      WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+      WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+                         opcodes, format, eh_frame);
       return;
     }
     case kX86_64: {
@@ -149,7 +154,8 @@
         }
       }
       auto return_reg = Reg::X86_64Core(16);  // R16(RIP).
-      WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+      WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+                         opcodes, format, eh_frame);
       return;
     }
     case kNone:
@@ -159,58 +165,61 @@
   UNREACHABLE();
 }
 
-void WriteEhFrame(const CompilerDriver* compiler,
-                  const OatWriter* oat_writer,
-                  ExceptionHeaderValueApplication address_type,
-                  std::vector<uint8_t>* eh_frame,
-                  std::vector<uintptr_t>* eh_frame_patches,
-                  std::vector<uint8_t>* eh_frame_hdr,
-                  std::vector<uintptr_t>* eh_frame_hdr_patches) {
+void WriteCFISection(const CompilerDriver* compiler,
+                     const OatWriter* oat_writer,
+                     ExceptionHeaderValueApplication address_type,
+                     CFIFormat format,
+                     std::vector<uint8_t>* debug_frame,
+                     std::vector<uintptr_t>* debug_frame_patches,
+                     std::vector<uint8_t>* eh_frame_hdr,
+                     std::vector<uintptr_t>* eh_frame_hdr_patches) {
   const auto& method_infos = oat_writer->GetMethodDebugInfo();
   const InstructionSet isa = compiler->GetInstructionSet();
 
-  // Write .eh_frame section.
+  // Write .eh_frame/.debug_frame section.
   std::map<uint32_t, size_t> address_to_fde_offset_map;
-  size_t cie_offset = eh_frame->size();
-  WriteEhFrameCIE(isa, address_type, eh_frame);
+  size_t cie_offset = debug_frame->size();
+  WriteDebugFrameCIE(isa, address_type, format, debug_frame);
   for (const OatWriter::DebugInfo& mi : method_infos) {
     if (!mi.deduped_) {  // Only one FDE per unique address.
       const SwapVector<uint8_t>* opcodes = mi.compiled_method_->GetCFIInfo();
       if (opcodes != nullptr) {
-        address_to_fde_offset_map.emplace(mi.low_pc_, eh_frame->size());
-        WriteEhFrameFDE(Is64BitInstructionSet(isa), cie_offset,
-                        mi.low_pc_, mi.high_pc_ - mi.low_pc_,
-                        opcodes, eh_frame, eh_frame_patches);
+        address_to_fde_offset_map.emplace(mi.low_pc_, debug_frame->size());
+        WriteDebugFrameFDE(Is64BitInstructionSet(isa), cie_offset,
+                           mi.low_pc_, mi.high_pc_ - mi.low_pc_,
+                           opcodes, format, debug_frame, debug_frame_patches);
       }
     }
   }
 
-  // Write .eh_frame_hdr section.
-  Writer<> header(eh_frame_hdr);
-  header.PushUint8(1);  // Version.
-  // Encoding of .eh_frame pointer - libunwind does not honor datarel here,
-  // so we have to use pcrel which means relative to the pointer's location.
-  header.PushUint8(DW_EH_PE_pcrel | DW_EH_PE_sdata4);
-  // Encoding of binary search table size.
-  header.PushUint8(DW_EH_PE_udata4);
-  // Encoding of binary search table addresses - libunwind supports only this
-  // specific combination, which means relative to the start of .eh_frame_hdr.
-  header.PushUint8(DW_EH_PE_datarel | DW_EH_PE_sdata4);
-  // .eh_frame pointer - .eh_frame_hdr section is after .eh_frame section
-  const int32_t relative_eh_frame_begin = -static_cast<int32_t>(eh_frame->size());
-  header.PushInt32(relative_eh_frame_begin - 4U);
-  // Binary search table size (number of entries).
-  header.PushUint32(dchecked_integral_cast<uint32_t>(address_to_fde_offset_map.size()));
-  // Binary search table.
-  for (const auto& address_to_fde_offset : address_to_fde_offset_map) {
-    u_int32_t code_address = address_to_fde_offset.first;
-    int32_t fde_address = dchecked_integral_cast<int32_t>(address_to_fde_offset.second);
-    eh_frame_hdr_patches->push_back(header.data()->size());
-    header.PushUint32(code_address);
-    // We know the exact layout (eh_frame is immediately before eh_frame_hdr)
-    // and the data is relative to the start of the eh_frame_hdr,
-    // so patching isn't necessary (in contrast to the code address above).
-    header.PushInt32(relative_eh_frame_begin + fde_address);
+  if (format == DW_EH_FRAME_FORMAT) {
+    // Write .eh_frame_hdr section.
+    Writer<> header(eh_frame_hdr);
+    header.PushUint8(1);  // Version.
+    // Encoding of .eh_frame pointer - libunwind does not honor datarel here,
+    // so we have to use pcrel which means relative to the pointer's location.
+    header.PushUint8(DW_EH_PE_pcrel | DW_EH_PE_sdata4);
+    // Encoding of binary search table size.
+    header.PushUint8(DW_EH_PE_udata4);
+    // Encoding of binary search table addresses - libunwind supports only this
+    // specific combination, which means relative to the start of .eh_frame_hdr.
+    header.PushUint8(DW_EH_PE_datarel | DW_EH_PE_sdata4);
+    // .eh_frame pointer - .eh_frame_hdr section is after .eh_frame section
+    const int32_t relative_eh_frame_begin = -static_cast<int32_t>(debug_frame->size());
+    header.PushInt32(relative_eh_frame_begin - 4U);
+    // Binary search table size (number of entries).
+    header.PushUint32(dchecked_integral_cast<uint32_t>(address_to_fde_offset_map.size()));
+    // Binary search table.
+    for (const auto& address_to_fde_offset : address_to_fde_offset_map) {
+      u_int32_t code_address = address_to_fde_offset.first;
+      int32_t fde_address = dchecked_integral_cast<int32_t>(address_to_fde_offset.second);
+      eh_frame_hdr_patches->push_back(header.data()->size());
+      header.PushUint32(code_address);
+      // We know the exact layout (eh_frame is immediately before eh_frame_hdr)
+      // and the data is relative to the start of the eh_frame_hdr,
+      // so patching isn't necessary (in contrast to the code address above).
+      header.PushInt32(relative_eh_frame_begin + fde_address);
+    }
   }
 }
 
@@ -235,6 +244,7 @@
                         std::vector<uintptr_t>* debug_line_patches) {
   const std::vector<OatWriter::DebugInfo>& method_infos = oat_writer->GetMethodDebugInfo();
   const InstructionSet isa = compiler->GetInstructionSet();
+  const bool is64bit = Is64BitInstructionSet(isa);
 
   // Find all addresses (low_pc) which contain deduped methods.
   // The first instance of method is not marked deduped_, but the rest is.
@@ -272,7 +282,7 @@
     }
 
     size_t debug_abbrev_offset = debug_abbrev->size();
-    DebugInfoEntryWriter<> info(false /* 32 bit */, debug_abbrev);
+    DebugInfoEntryWriter<> info(is64bit, debug_abbrev);
     info.StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes);
     info.WriteStrp(DW_AT_producer, "Android dex2oat", debug_str);
     info.WriteData1(DW_AT_language, DW_LANG_Java);
@@ -317,7 +327,7 @@
       case kX86_64:
         break;
     }
-    DebugLineOpCodeWriter<> opcodes(false /* 32bit */, code_factor_bits_);
+    DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_);
     opcodes.SetAddress(cunit_low_pc);
     if (dwarf_isa != -1) {
       opcodes.SetISA(dwarf_isa);
diff --git a/compiler/elf_writer_debug.h b/compiler/elf_writer_debug.h
index 28d0e2c..69f7e0d 100644
--- a/compiler/elf_writer_debug.h
+++ b/compiler/elf_writer_debug.h
@@ -25,13 +25,14 @@
 namespace art {
 namespace dwarf {
 
-void WriteEhFrame(const CompilerDriver* compiler,
-                  const OatWriter* oat_writer,
-                  ExceptionHeaderValueApplication address_type,
-                  std::vector<uint8_t>* eh_frame,
-                  std::vector<uintptr_t>* eh_frame_patches,
-                  std::vector<uint8_t>* eh_frame_hdr,
-                  std::vector<uintptr_t>* eh_frame_hdr_patches);
+void WriteCFISection(const CompilerDriver* compiler,
+                     const OatWriter* oat_writer,
+                     ExceptionHeaderValueApplication address_type,
+                     CFIFormat format,
+                     std::vector<uint8_t>* debug_frame,
+                     std::vector<uintptr_t>* debug_frame_patches,
+                     std::vector<uint8_t>* eh_frame_hdr,
+                     std::vector<uintptr_t>* eh_frame_hdr_patches);
 
 void WriteDebugSections(const CompilerDriver* compiler,
                         const OatWriter* oat_writer,
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 79f9955..ddee3ba 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -19,6 +19,7 @@
 #include <unordered_map>
 #include <unordered_set>
 
+#include "base/casts.h"
 #include "base/logging.h"
 #include "base/unix_file/fd_file.h"
 #include "compiled_method.h"
@@ -37,6 +38,23 @@
 
 namespace art {
 
+// .eh_frame and .debug_frame are almost identical.
+// Except for some minor formatting differences, the main difference
+// is that .eh_frame is allocated within the running program because
+// it is used by C++ exception handling (which we do not use so we
+// can choose either).  C++ compilers generally tend to use .eh_frame
+// because if they need it sometimes, they might as well always use it.
+constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_EH_FRAME_FORMAT;
+
+// The ARM specification defines three special mapping symbols
+// $a, $t and $d which mark ARM, Thumb and data ranges respectively.
+// These symbols can be used by tools, for example, to pretty
+// print instructions correctly.  Objdump will use them if they
+// exist, but it will still work well without them.
+// However, these extra symbols take space, so let's just generate
+// one symbol which marks the whole .text section as code.
+constexpr bool kGenerateSingleArmMappingSymbol = true;
+
 template <typename ElfTypes>
 bool ElfWriterQuick<ElfTypes>::Create(File* elf_file,
                                       OatWriter* oat_writer,
@@ -51,36 +69,17 @@
 template <typename ElfTypes>
 static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writer);
 
-// Encode patch locations in .oat_patches format.
+// Encode patch locations as LEB128 list of deltas between consecutive addresses.
 template <typename ElfTypes>
-void ElfWriterQuick<ElfTypes>::EncodeOatPatches(
-    const OatWriter::PatchLocationsMap& sections,
-    std::vector<uint8_t>* buffer) {
-  for (const auto& section : sections) {
-    const std::string& name = section.first;
-    std::vector<uintptr_t>* locations = section.second.get();
-    DCHECK(!name.empty());
-    std::sort(locations->begin(), locations->end());
-    // Reserve buffer space - guess 2 bytes per ULEB128.
-    buffer->reserve(buffer->size() + name.size() + locations->size() * 2);
-    // Write null-terminated section name.
-    const uint8_t* name_data = reinterpret_cast<const uint8_t*>(name.c_str());
-    buffer->insert(buffer->end(), name_data, name_data + name.size() + 1);
-    // Write placeholder for data length.
-    size_t length_pos = buffer->size();
-    EncodeUnsignedLeb128(buffer, UINT32_MAX);
-    // Write LEB128 encoded list of advances (deltas between consequtive addresses).
-    size_t data_pos = buffer->size();
-    uintptr_t address = 0;  // relative to start of section.
-    for (uintptr_t location : *locations) {
-      DCHECK_LT(location - address, UINT32_MAX) << "Large gap between patch locations";
-      EncodeUnsignedLeb128(buffer, location - address);
-      address = location;
-    }
-    // Update length.
-    UpdateUnsignedLeb128(buffer->data() + length_pos, buffer->size() - data_pos);
+void ElfWriterQuick<ElfTypes>::EncodeOatPatches(const std::vector<uintptr_t>& locations,
+                                                std::vector<uint8_t>* buffer) {
+  buffer->reserve(buffer->size() + locations.size() * 2);  // guess 2 bytes per ULEB128.
+  uintptr_t address = 0;  // relative to start of section.
+  for (uintptr_t location : locations) {
+    DCHECK_GE(location, address) << "Patch locations are not in sorted order";
+    EncodeUnsignedLeb128(buffer, dchecked_integral_cast<uint32_t>(location - address));
+    address = location;
   }
-  buffer->push_back(0);  // End of sections.
 }
 
 class RodataWriter FINAL : public CodeOutput {
@@ -156,61 +155,95 @@
       isa, rodata_size, &rodata_writer, text_size, &text_writer, bss_size));
 
   // Add debug sections.
-  // They are stack allocated here (in the same scope as the builder),
-  // but they are registred with the builder only if they are used.
+  // They are allocated here (in the same scope as the builder),
+  // but they are registered with the builder only if they are used.
   using RawSection = typename ElfBuilder<ElfTypes>::RawSection;
   const auto* text = builder->GetText();
   const bool is64bit = Is64BitInstructionSet(isa);
-  RawSection eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0,
-                      is64bit ? Patch<Elf_Addr, uint64_t, kPointerRelativeAddress> :
-                                Patch<Elf_Addr, uint32_t, kPointerRelativeAddress>,
-                      text);
-  RawSection eh_frame_hdr(".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0,
-                          Patch<Elf_Addr, uint32_t, kSectionRelativeAddress>, text);
-  RawSection debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0,
-                        Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text);
-  RawSection debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-  RawSection debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-  RawSection debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0,
-                        Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text);
+  const int pointer_size = GetInstructionSetPointerSize(isa);
+  std::unique_ptr<RawSection> eh_frame(new RawSection(
+      ".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0,
+      is64bit ? Patch<Elf_Addr, uint64_t, kPointerRelativeAddress> :
+                Patch<Elf_Addr, uint32_t, kPointerRelativeAddress>,
+      text));
+  std::unique_ptr<RawSection> eh_frame_hdr(new RawSection(
+      ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0,
+      Patch<Elf_Addr, uint32_t, kSectionRelativeAddress>, text));
+  std::unique_ptr<RawSection> debug_frame(new RawSection(
+      ".debug_frame", SHT_PROGBITS, 0, nullptr, 0, pointer_size, 0,
+      is64bit ? Patch<Elf_Addr, uint64_t, kAbsoluteAddress> :
+                Patch<Elf_Addr, uint32_t, kAbsoluteAddress>,
+      text));
+  std::unique_ptr<RawSection> debug_frame_oat_patches(new RawSection(
+      ".debug_frame.oat_patches", SHT_OAT_PATCH));
+  std::unique_ptr<RawSection> debug_info(new RawSection(
+      ".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0,
+      Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text));
+  std::unique_ptr<RawSection> debug_info_oat_patches(new RawSection(
+      ".debug_info.oat_patches", SHT_OAT_PATCH));
+  std::unique_ptr<RawSection> debug_abbrev(new RawSection(
+      ".debug_abbrev", SHT_PROGBITS));
+  std::unique_ptr<RawSection> debug_str(new RawSection(
+      ".debug_str", SHT_PROGBITS));
+  std::unique_ptr<RawSection> debug_line(new RawSection(
+      ".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0,
+      Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text));
+  std::unique_ptr<RawSection> debug_line_oat_patches(new RawSection(
+      ".debug_line.oat_patches", SHT_OAT_PATCH));
   if (!oat_writer->GetMethodDebugInfo().empty()) {
-    if (compiler_driver_->GetCompilerOptions().GetIncludeCFI()) {
-      dwarf::WriteEhFrame(
-          compiler_driver_, oat_writer, dwarf::DW_EH_PE_pcrel,
-          eh_frame.GetBuffer(), eh_frame.GetPatchLocations(),
-          eh_frame_hdr.GetBuffer(), eh_frame_hdr.GetPatchLocations());
-      builder->RegisterSection(&eh_frame);
-      builder->RegisterSection(&eh_frame_hdr);
-    }
-    if (compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
+    if (compiler_driver_->GetCompilerOptions().GetGenerateDebugInfo()) {
+      // Generate CFI (stack unwinding information).
+      if (kCFIFormat == dwarf::DW_EH_FRAME_FORMAT) {
+        dwarf::WriteCFISection(
+            compiler_driver_, oat_writer,
+            dwarf::DW_EH_PE_pcrel, kCFIFormat,
+            eh_frame->GetBuffer(), eh_frame->GetPatchLocations(),
+            eh_frame_hdr->GetBuffer(), eh_frame_hdr->GetPatchLocations());
+        builder->RegisterSection(eh_frame.get());
+        builder->RegisterSection(eh_frame_hdr.get());
+      } else {
+        DCHECK(kCFIFormat == dwarf::DW_DEBUG_FRAME_FORMAT);
+        dwarf::WriteCFISection(
+            compiler_driver_, oat_writer,
+            dwarf::DW_EH_PE_absptr, kCFIFormat,
+            debug_frame->GetBuffer(), debug_frame->GetPatchLocations(),
+            nullptr, nullptr);
+        builder->RegisterSection(debug_frame.get());
+        EncodeOatPatches(*debug_frame->GetPatchLocations(),
+                         debug_frame_oat_patches->GetBuffer());
+        builder->RegisterSection(debug_frame_oat_patches.get());
+      }
       // Add methods to .symtab.
       WriteDebugSymbols(builder.get(), oat_writer);
       // Generate DWARF .debug_* sections.
       dwarf::WriteDebugSections(
           compiler_driver_, oat_writer,
-          debug_info.GetBuffer(), debug_info.GetPatchLocations(),
-          debug_abbrev.GetBuffer(),
-          debug_str.GetBuffer(),
-          debug_line.GetBuffer(), debug_line.GetPatchLocations());
-      builder->RegisterSection(&debug_info);
-      builder->RegisterSection(&debug_abbrev);
-      builder->RegisterSection(&debug_str);
-      builder->RegisterSection(&debug_line);
-      *oat_writer->GetAbsolutePatchLocationsFor(".debug_info") =
-          *debug_info.GetPatchLocations();
-      *oat_writer->GetAbsolutePatchLocationsFor(".debug_line") =
-          *debug_line.GetPatchLocations();
+          debug_info->GetBuffer(), debug_info->GetPatchLocations(),
+          debug_abbrev->GetBuffer(),
+          debug_str->GetBuffer(),
+          debug_line->GetBuffer(), debug_line->GetPatchLocations());
+      builder->RegisterSection(debug_info.get());
+      EncodeOatPatches(*debug_info->GetPatchLocations(),
+                       debug_info_oat_patches->GetBuffer());
+      builder->RegisterSection(debug_info_oat_patches.get());
+      builder->RegisterSection(debug_abbrev.get());
+      builder->RegisterSection(debug_str.get());
+      builder->RegisterSection(debug_line.get());
+      EncodeOatPatches(*debug_line->GetPatchLocations(),
+                       debug_line_oat_patches->GetBuffer());
+      builder->RegisterSection(debug_line_oat_patches.get());
     }
   }
 
-  // Add relocation section.
-  RawSection oat_patches(".oat_patches", SHT_OAT_PATCH, 0, nullptr, 0, 1, 0);
-  if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation() ||
-      // ElfWriter::Fixup will be called regardless and it needs to be able
-      // to patch debug sections so we have to include patches for them.
-      compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
-    EncodeOatPatches(oat_writer->GetAbsolutePatchLocations(), oat_patches.GetBuffer());
-    builder->RegisterSection(&oat_patches);
+  // Add relocation section for .text.
+  std::unique_ptr<RawSection> text_oat_patches(new RawSection(
+      ".text.oat_patches", SHT_OAT_PATCH));
+  if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) {
+    // Note that ElfWriter::Fixup will be called regardless and therefore
+    // we need to include oat_patches for debug sections unconditionally.
+    EncodeOatPatches(oat_writer->GetAbsolutePatchLocations(),
+                     text_oat_patches->GetBuffer());
+    builder->RegisterSection(text_oat_patches.get());
   }
 
   return builder->Write(elf_file_);
@@ -219,6 +252,7 @@
 template <typename ElfTypes>
 static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writer) {
   const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetMethodDebugInfo();
+  bool generated_mapping_symbol = false;
 
   // Find all addresses (low_pc) which contain deduped methods.
   // The first instance of method is not marked deduped_, but the rest is.
@@ -247,9 +281,14 @@
 
     // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2
     // instructions, so that disassembler tools can correctly disassemble.
+    // Note that even if we generate just a single mapping symbol, ARM's Streamline
+    // requires it to match function symbol.  Just address 0 does not work.
     if (it->compiled_method_->GetInstructionSet() == kThumb2) {
-      symtab->AddSymbol("$t", builder->GetText(), it->low_pc_ & ~1, true,
-                        0, STB_LOCAL, STT_NOTYPE);
+      if (!generated_mapping_symbol || !kGenerateSingleArmMappingSymbol) {
+        symtab->AddSymbol("$t", builder->GetText(), it->low_pc_ & ~1, true,
+                          0, STB_LOCAL, STT_NOTYPE);
+        generated_mapping_symbol = true;
+      }
     }
   }
 }
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index 955b568..fd202ee 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -35,7 +35,7 @@
                      const CompilerDriver& driver)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static void EncodeOatPatches(const OatWriter::PatchLocationsMap& sections,
+  static void EncodeOatPatches(const std::vector<uintptr_t>& locations,
                                std::vector<uint8_t>* buffer);
 
  protected:
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index 08523d8..ccf34b8 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -88,73 +88,41 @@
   }
 }
 
-// Run only on host since we do unaligned memory accesses.
-#ifndef HAVE_ANDROID_OS
-
-static void PatchSection(const std::vector<uintptr_t>& patch_locations,
-                         std::vector<uint8_t>* section, int32_t delta) {
-  for (uintptr_t location : patch_locations) {
-    *reinterpret_cast<int32_t*>(section->data() + location) += delta;
-  }
-}
-
 TEST_F(ElfWriterTest, EncodeDecodeOatPatches) {
-  std::vector<uint8_t> oat_patches;  // Encoded patches.
+  const std::vector<std::vector<uintptr_t>> test_data {
+      { 0, 4, 8, 15, 128, 200 },
+      { 8, 8 + 127 },
+      { 8, 8 + 128 },
+      { },
+  };
+  for (const auto& patch_locations : test_data) {
+    constexpr int32_t delta = 0x11235813;
 
-  // Encode patch locations for a few sections.
-  OatWriter::PatchLocationsMap sections;
-  std::vector<uintptr_t> patches0 { 0, 4, 8, 15, 128, 200 };  // NOLINT
-  sections.emplace(".section0", std::unique_ptr<std::vector<uintptr_t>>(
-      new std::vector<uintptr_t> { patches0 }));
-  std::vector<uintptr_t> patches1 { 8, 127 };  // NOLINT
-  sections.emplace(".section1", std::unique_ptr<std::vector<uintptr_t>>(
-      new std::vector<uintptr_t> { patches1 }));
-  std::vector<uintptr_t> patches2 { };  // NOLINT
-  sections.emplace(".section2", std::unique_ptr<std::vector<uintptr_t>>(
-      new std::vector<uintptr_t> { patches2 }));
-  ElfWriterQuick32::EncodeOatPatches(sections, &oat_patches);
+    // Encode patch locations.
+    std::vector<uint8_t> oat_patches;
+    ElfWriterQuick32::EncodeOatPatches(patch_locations, &oat_patches);
 
-  // Create buffers to be patched.
-  std::vector<uint8_t> initial_data(256);
-  for (size_t i = 0; i < initial_data.size(); i++) {
-    initial_data[i] = i;
+    // Create buffer to be patched.
+    std::vector<uint8_t> initial_data(256);
+    for (size_t i = 0; i < initial_data.size(); i++) {
+      initial_data[i] = i;
+    }
+
+    // Patch manually.
+    std::vector<uint8_t> expected = initial_data;
+    for (uintptr_t location : patch_locations) {
+      typedef __attribute__((__aligned__(1))) uint32_t UnalignedAddress;
+      *reinterpret_cast<UnalignedAddress*>(expected.data() + location) += delta;
+    }
+
+    // Decode and apply patch locations.
+    std::vector<uint8_t> actual = initial_data;
+    ElfFileImpl32::ApplyOatPatches(
+        oat_patches.data(), oat_patches.data() + oat_patches.size(), delta,
+        actual.data(), actual.data() + actual.size());
+
+    EXPECT_EQ(expected, actual);
   }
-  std::vector<uint8_t> section0_expected = initial_data;
-  std::vector<uint8_t> section1_expected = initial_data;
-  std::vector<uint8_t> section2_expected = initial_data;
-  std::vector<uint8_t> section0_actual = initial_data;
-  std::vector<uint8_t> section1_actual = initial_data;
-  std::vector<uint8_t> section2_actual = initial_data;
-
-  // Patch manually.
-  constexpr int32_t delta = 0x11235813;
-  PatchSection(patches0, &section0_expected, delta);
-  PatchSection(patches1, &section1_expected, delta);
-  PatchSection(patches2, &section2_expected, delta);
-
-  // Decode and apply patch locations.
-  bool section0_successful = ElfFileImpl32::ApplyOatPatches(
-      oat_patches.data(), oat_patches.data() + oat_patches.size(),
-      ".section0", delta,
-      section0_actual.data(), section0_actual.data() + section0_actual.size());
-  EXPECT_TRUE(section0_successful);
-  EXPECT_EQ(section0_expected, section0_actual);
-
-  bool section1_successful = ElfFileImpl32::ApplyOatPatches(
-      oat_patches.data(), oat_patches.data() + oat_patches.size(),
-      ".section1", delta,
-      section1_actual.data(), section1_actual.data() + section1_actual.size());
-  EXPECT_TRUE(section1_successful);
-  EXPECT_EQ(section1_expected, section1_actual);
-
-  bool section2_successful = ElfFileImpl32::ApplyOatPatches(
-      oat_patches.data(), oat_patches.data() + oat_patches.size(),
-      ".section2", delta,
-      section2_actual.data(), section2_actual.data() + section2_actual.size());
-  EXPECT_TRUE(section2_successful);
-  EXPECT_EQ(section2_expected, section2_actual);
 }
 
-#endif
-
 }  // namespace art
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index ce277cd..a1d8226 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -74,8 +74,7 @@
       false,
       CompilerOptions::kDefaultTopKProfileThreshold,
       false,  // TODO: Think about debuggability of JIT-compiled code.
-      false,
-      false,
+      CompilerOptions::kDefaultGenerateDebugInfo,
       false,
       false,
       false,
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 0347c5e..4d7d86c 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -94,7 +94,7 @@
 
   // Assembler that holds generated instructions
   std::unique_ptr<Assembler> jni_asm(Assembler::Create(instruction_set));
-  jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GetIncludeCFI());
+  jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GetGenerateDebugInfo());
 
   // Offsets into data structures
   // TODO: if cross compiling these offsets are for the host not the target
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 633bf64..a98a304 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -351,9 +351,8 @@
  public:
   InitCodeMethodVisitor(OatWriter* writer, size_t offset)
     : OatDexMethodVisitor(writer, offset),
-      text_absolute_patch_locations_(writer->GetAbsolutePatchLocationsFor(".text")),
       debuggable_(writer->GetCompilerDriver()->GetCompilerOptions().GetDebuggable()) {
-    text_absolute_patch_locations_->reserve(
+    writer_->absolute_patch_locations_.reserve(
         writer_->compiler_driver_->GetNonRelativeLinkerPatchCount());
   }
 
@@ -444,14 +443,13 @@
           uintptr_t base_loc = offset_ - code_size - writer_->oat_header_->GetExecutableOffset();
           for (const LinkerPatch& patch : compiled_method->GetPatches()) {
             if (!patch.IsPcRelative()) {
-              text_absolute_patch_locations_->push_back(base_loc + patch.LiteralOffset());
+              writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset());
             }
           }
         }
       }
 
-      if (writer_->compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols() ||
-          writer_->compiler_driver_->GetCompilerOptions().GetIncludeCFI()) {
+      if (writer_->compiler_driver_->GetCompilerOptions().GetGenerateDebugInfo()) {
         // Record debug information for this function if we are doing that.
         const uint32_t quick_code_start = quick_code_offset -
             writer_->oat_header_->GetExecutableOffset() - thumb_offset;
@@ -547,9 +545,6 @@
   // so we can simply compare the pointers to find out if things are duplicated.
   SafeMap<const CompiledMethod*, uint32_t, CodeOffsetsKeyComparator> dedupe_map_;
 
-  // Patch locations for the .text section.
-  std::vector<uintptr_t>* const text_absolute_patch_locations_;
-
   // Cache of compiler's --debuggable option.
   const bool debuggable_;
 };
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 6f1b4ec..82b9377 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -19,7 +19,6 @@
 
 #include <stdint.h>
 #include <cstddef>
-#include <map>
 #include <memory>
 
 #include "linker/relative_patcher.h"  // For linker::RelativePatcherTargetProvider.
@@ -82,8 +81,6 @@
 //
 class OatWriter {
  public:
-  typedef std::map<std::string, std::unique_ptr<std::vector<uintptr_t>>> PatchLocationsMap;
-
   OatWriter(const std::vector<const DexFile*>& dex_files,
             uint32_t image_file_location_oat_checksum,
             uintptr_t image_file_location_oat_begin,
@@ -105,19 +102,10 @@
     return bss_size_;
   }
 
-  const PatchLocationsMap& GetAbsolutePatchLocations() const {
+  const std::vector<uintptr_t>& GetAbsolutePatchLocations() const {
     return absolute_patch_locations_;
   }
 
-  std::vector<uintptr_t>* GetAbsolutePatchLocationsFor(const char* section_name) {
-    auto it = absolute_patch_locations_.emplace(
-        std::string(section_name), std::unique_ptr<std::vector<uintptr_t>>());
-    if (it.second) {  // Inserted new item.
-      it.first->second.reset(new std::vector<uintptr_t>());
-    }
-    return it.first->second.get();
-  }
-
   bool WriteRodata(OutputStream* out);
   bool WriteCode(OutputStream* out);
 
@@ -339,9 +327,8 @@
 
   std::unique_ptr<linker::RelativePatcher> relative_patcher_;
 
-  // The locations of absolute patches relative to the start of section.
-  // The map's key is the ELF's section name (including the dot).
-  PatchLocationsMap absolute_patch_locations_;
+  // The locations of absolute patches relative to the start of the executable section.
+  std::vector<uintptr_t> absolute_patch_locations_;
 
   // Map method reference to assigned offset.
   // Wrap the map in a class implementing linker::RelativePatcherTargetProvider.
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index c7b2c67..5632434 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -404,7 +404,7 @@
   codegen->CompileOptimized(&allocator);
 
   DefaultSrcMap src_mapping_table;
-  if (compiler_driver->GetCompilerOptions().GetIncludeDebugSymbols()) {
+  if (compiler_driver->GetCompilerOptions().GetGenerateDebugInfo()) {
     codegen->BuildSourceMap(&src_mapping_table);
   }
 
@@ -441,7 +441,7 @@
   std::vector<uint8_t> mapping_table;
   codegen->BuildMappingTable(&mapping_table);
   DefaultSrcMap src_mapping_table;
-  if (compiler_driver->GetCompilerOptions().GetIncludeDebugSymbols()) {
+  if (compiler_driver->GetCompilerOptions().GetGenerateDebugInfo()) {
     codegen->BuildSourceMap(&src_mapping_table);
   }
   std::vector<uint8_t> vmap_table;
@@ -533,7 +533,7 @@
     return nullptr;
   }
   codegen->GetAssembler()->cfi().SetEnabled(
-      compiler_driver->GetCompilerOptions().GetIncludeCFI());
+      compiler_driver->GetCompilerOptions().GetGenerateDebugInfo());
 
   PassInfoPrinter pass_info_printer(graph,
                                     method_name.c_str(),
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 7fa4328..d45ab1b 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -230,14 +230,14 @@
   UsageError("");
   UsageError("  --no-include-patch-information: Do not include patching information.");
   UsageError("");
-  UsageError("  --include-debug-symbols: Include ELF symbols in this oat file");
+  UsageError("  -g");
+  UsageError("  --generate-debug-info: Generate debug information for native debugging,");
+  UsageError("      such as stack unwinding information, ELF symbols and DWARF sections.");
+  UsageError("      This generates all the available information. Unneeded parts can be");
+  UsageError("      stripped using standard command line tools such as strip or objcopy.");
+  UsageError("      (enabled by default in debug builds, disabled by default otherwise)");
   UsageError("");
-  UsageError("  --no-include-debug-symbols: Do not include ELF symbols in this oat file");
-  UsageError("");
-  UsageError("  --include-cfi: Include call frame information in the .eh_frame section.");
-  UsageError("      The --include-debug-symbols option implies --include-cfi.");
-  UsageError("");
-  UsageError("  --no-include-cfi: Do not include call frame information in the .eh_frame section.");
+  UsageError("  --no-generate-debug-info: Do not generate debug information for native debugging.");
   UsageError("");
   UsageError("  --runtime-arg <argument>: used to specify various arguments for the runtime,");
   UsageError("      such as initial heap size, maximum heap size, and verbose output.");
@@ -499,8 +499,7 @@
 
     bool debuggable = false;
     bool include_patch_information = CompilerOptions::kDefaultIncludePatchInformation;
-    bool include_debug_symbols = kIsDebugBuild;
-    bool include_cfi = kIsDebugBuild;
+    bool generate_debug_info = kIsDebugBuild;
     bool watch_dog_enabled = true;
     bool abort_on_hard_verifier_error = false;
     bool requested_specific_compiler = false;
@@ -682,18 +681,13 @@
         dump_cfg_file_name_ = option.substr(strlen("--dump-cfg=")).data();
       } else if (option == "--dump-stats") {
         dump_stats_ = true;
-      } else if (option == "--include-debug-symbols" || option == "--no-strip-symbols") {
-        include_debug_symbols = true;
-      } else if (option == "--no-include-debug-symbols" || option == "--strip-symbols") {
-        include_debug_symbols = false;
-      } else if (option == "--include-cfi") {
-        include_cfi = true;
-      } else if (option == "--no-include-cfi") {
-        include_cfi = false;
+      } else if (option == "--generate-debug-info" || option == "-g") {
+        generate_debug_info = true;
+      } else if (option == "--no-generate-debug-info") {
+        generate_debug_info = false;
       } else if (option == "--debuggable") {
         debuggable = true;
-        include_debug_symbols = true;
-        include_cfi = true;
+        generate_debug_info = true;
       } else if (option.starts_with("--profile-file=")) {
         profile_file_ = option.substr(strlen("--profile-file=")).data();
         VLOG(compiler) << "dex2oat: profile file is " << profile_file_;
@@ -933,10 +927,6 @@
         break;
     }
 
-    if (debuggable) {
-      // TODO: Consider adding CFI info and symbols here.
-    }
-
     compiler_options_.reset(new CompilerOptions(compiler_filter,
                                                 huge_method_threshold,
                                                 large_method_threshold,
@@ -946,8 +936,7 @@
                                                 include_patch_information,
                                                 top_k_profile_threshold,
                                                 debuggable,
-                                                include_debug_symbols,
-                                                include_cfi,
+                                                generate_debug_info,
                                                 implicit_null_checks,
                                                 implicit_so_checks,
                                                 implicit_suspend_checks,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 91812e7..ae952e6 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -808,18 +808,11 @@
 }
 
 const OatFile* ClassLinker::GetBootOatFile() {
-  // To grab the boot oat, look at the dex files in the boot classpath. Any of those is fine, as
-  // they were all compiled into the same oat file. So grab the first one, which is guaranteed to
-  // exist if the boot class-path isn't empty.
-  if (boot_class_path_.empty()) {
+  gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
+  if (image_space == nullptr) {
     return nullptr;
   }
-  const DexFile* boot_dex_file = boot_class_path_[0];
-  // Is it from an oat file?
-  if (boot_dex_file->GetOatDexFile() != nullptr) {
-    return boot_dex_file->GetOatDexFile()->GetOatFile();
-  }
-  return nullptr;
+  return image_space->GetOatFile();
 }
 
 const OatFile* ClassLinker::GetPrimaryOatFile() {
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 0c5210d..9fd8c87 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -1401,88 +1401,53 @@
 }
 
 template <typename ElfTypes>
-bool ElfFileImpl<ElfTypes>::FixupDebugSections(typename std::make_signed<Elf_Off>::type base_address_delta) {
-  const Elf_Shdr* debug_info = FindSectionByName(".debug_info");
-  const Elf_Shdr* debug_abbrev = FindSectionByName(".debug_abbrev");
-  const Elf_Shdr* debug_str = FindSectionByName(".debug_str");
-  const Elf_Shdr* strtab_sec = FindSectionByName(".strtab");
-  const Elf_Shdr* symtab_sec = FindSectionByName(".symtab");
-
-  if (debug_info == nullptr || debug_abbrev == nullptr ||
-      debug_str == nullptr || strtab_sec == nullptr || symtab_sec == nullptr) {
-    // Release version of ART does not generate debug info.
-    return true;
-  }
+bool ElfFileImpl<ElfTypes>::FixupDebugSections(Elf_Addr base_address_delta) {
   if (base_address_delta == 0) {
     return true;
   }
-  if (!ApplyOatPatchesTo(".debug_info", base_address_delta)) {
-    return false;
-  }
-  if (!ApplyOatPatchesTo(".debug_line", base_address_delta)) {
-    return false;
-  }
-  return true;
+  return ApplyOatPatchesTo(".debug_frame", base_address_delta) &&
+         ApplyOatPatchesTo(".debug_info", base_address_delta) &&
+         ApplyOatPatchesTo(".debug_line", base_address_delta);
 }
 
 template <typename ElfTypes>
 bool ElfFileImpl<ElfTypes>::ApplyOatPatchesTo(
-    const char* target_section_name,
-    typename std::make_signed<Elf_Off>::type delta) {
-  auto patches_section = FindSectionByName(".oat_patches");
+    const char* target_section_name, Elf_Addr delta) {
+  auto target_section = FindSectionByName(target_section_name);
+  if (target_section == nullptr) {
+    return true;
+  }
+  std::string patches_name = target_section_name + std::string(".oat_patches");
+  auto patches_section = FindSectionByName(patches_name.c_str());
   if (patches_section == nullptr) {
-    LOG(ERROR) << ".oat_patches section not found.";
+    LOG(ERROR) << patches_name << " section not found.";
     return false;
   }
   if (patches_section->sh_type != SHT_OAT_PATCH) {
-    LOG(ERROR) << "Unexpected type of .oat_patches.";
+    LOG(ERROR) << "Unexpected type of " << patches_name;
     return false;
   }
-  auto target_section = FindSectionByName(target_section_name);
-  if (target_section == nullptr) {
-    LOG(ERROR) << target_section_name << " section not found.";
-    return false;
-  }
-  if (!ApplyOatPatches(
+  ApplyOatPatches(
       Begin() + patches_section->sh_offset,
       Begin() + patches_section->sh_offset + patches_section->sh_size,
-      target_section_name, delta,
+      delta,
       Begin() + target_section->sh_offset,
-      Begin() + target_section->sh_offset + target_section->sh_size)) {
-    LOG(ERROR) << target_section_name << " section not found in .oat_patches.";
-  }
+      Begin() + target_section->sh_offset + target_section->sh_size);
   return true;
 }
 
-// Apply .oat_patches to given section.
+// Apply LEB128 encoded patches to given section.
 template <typename ElfTypes>
-bool ElfFileImpl<ElfTypes>::ApplyOatPatches(
-    const uint8_t* patches, const uint8_t* patches_end,
-    const char* target_section_name,
-    typename std::make_signed<Elf_Off>::type delta,
+void ElfFileImpl<ElfTypes>::ApplyOatPatches(
+    const uint8_t* patches, const uint8_t* patches_end, Elf_Addr delta,
     uint8_t* to_patch, const uint8_t* to_patch_end) {
-  // Read null-terminated section name.
-  const char* section_name;
-  while ((section_name = reinterpret_cast<const char*>(patches))[0] != '\0') {
-    patches += strlen(section_name) + 1;
-    uint32_t length = DecodeUnsignedLeb128(&patches);
-    const uint8_t* next_section = patches + length;
-    // Is it the section we want to patch?
-    if (strcmp(section_name, target_section_name) == 0) {
-      // Read LEB128 encoded list of advances.
-      while (patches < next_section) {
-        DCHECK_LT(patches, patches_end) << "Unexpected end of .oat_patches.";
-        to_patch += DecodeUnsignedLeb128(&patches);
-        DCHECK_LT(to_patch, to_patch_end) << "Patch past the end of " << section_name;
-        // TODO: 32-bit vs 64-bit.  What is the right type to use here?
-        auto* patch_loc = reinterpret_cast<typename std::make_signed<Elf_Off>::type*>(to_patch);
-        *patch_loc += delta;
-      }
-      return true;
-    }
-    patches = next_section;
+  typedef __attribute__((__aligned__(1))) Elf_Addr UnalignedAddress;
+  while (patches < patches_end) {
+    to_patch += DecodeUnsignedLeb128(&patches);
+    DCHECK_LE(patches, patches_end) << "Unexpected end of patch list.";
+    DCHECK_LT(to_patch, to_patch_end) << "Patch past the end of section.";
+    *reinterpret_cast<UnalignedAddress*>(to_patch) += delta;
   }
-  return false;
 }
 
 template <typename ElfTypes>
diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h
index 3ad096f..0f466bd 100644
--- a/runtime/elf_file_impl.h
+++ b/runtime/elf_file_impl.h
@@ -119,12 +119,9 @@
   bool FixupProgramHeaders(Elf_Addr base_address);
   bool FixupSymbols(Elf_Addr base_address, bool dynamic);
   bool FixupRelocations(Elf_Addr base_address);
-  bool FixupDebugSections(typename std::make_signed<Elf_Off>::type base_address_delta);
-  bool ApplyOatPatchesTo(const char* target_section_name,
-                         typename std::make_signed<Elf_Off>::type base_address_delta);
-  static bool ApplyOatPatches(const uint8_t* patches, const uint8_t* patches_end,
-                              const char* target_section_name,
-                              typename std::make_signed<Elf_Off>::type delta,
+  bool FixupDebugSections(Elf_Addr base_address_delta);
+  bool ApplyOatPatchesTo(const char* target_section_name, Elf_Addr base_address_delta);
+  static void ApplyOatPatches(const uint8_t* patches, const uint8_t* patches_end, Elf_Addr delta,
                               uint8_t* to_patch, const uint8_t* to_patch_end);
 
   bool Strip(std::string* error_msg);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index eabbbec..9a70d69 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -502,6 +502,7 @@
     bool no_gap = MemMap::CheckNoGaps(GetImageSpace()->GetMemMap(),
                                       non_moving_space_->GetMemMap());
     if (!no_gap) {
+      PrintFileToLog("/proc/self/maps", LogSeverity::ERROR);
       MemMap::DumpMaps(LOG(ERROR), true);
       LOG(FATAL) << "There's a gap between the image space and the non-moving space";
     }
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index f7ceb84..1923d24 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -755,6 +755,7 @@
     DCHECK(!error_msg->empty());
     return nullptr;
   }
+  space->oat_file_non_owned_ = space->oat_file_.get();
 
   if (validate_oat_file && !space->ValidateOatFile(error_msg)) {
     DCHECK(!error_msg->empty());
@@ -838,10 +839,12 @@
   return true;
 }
 
+
 const OatFile* ImageSpace::GetOatFile() const {
-  return oat_file_.get();
+  return oat_file_non_owned_;
 }
 
+
 OatFile* ImageSpace::ReleaseOatFile() {
   CHECK(oat_file_.get() != nullptr);
   return oat_file_.release();
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 54dc7a6..93ff8aa 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -152,6 +152,10 @@
   // the ClassLinker during it's initialization.
   std::unique_ptr<OatFile> oat_file_;
 
+  // There are times when we need to find the boot image oat file. As
+  // we release ownership during startup, keep a non-owned reference.
+  const OatFile* oat_file_non_owned_;
+
   const std::string image_location_;
 
   DISALLOW_COPY_AND_ASSIGN(ImageSpace);
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index d6d71f2..6566060 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -439,6 +439,14 @@
                     page_aligned_byte_count, prot, false);
 }
 
+MemMap* MemMap::MapDummy(const char* name, uint8_t* addr, size_t byte_count) {
+  if (byte_count == 0) {
+    return new MemMap(name, nullptr, 0, nullptr, 0, 0, false);
+  }
+  const size_t page_aligned_byte_count = RoundUp(byte_count, kPageSize);
+  return new MemMap(name, addr, byte_count, addr, page_aligned_byte_count, 0, true /* reuse */);
+}
+
 MemMap* MemMap::MapFileAtAddress(uint8_t* expected_ptr, size_t byte_count, int prot, int flags,
                                  int fd, off_t start, bool reuse, const char* filename,
                                  std::string* error_msg) {
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index 6023a70..14387ee 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -64,6 +64,12 @@
   static MemMap* MapAnonymous(const char* ashmem_name, uint8_t* addr, size_t byte_count, int prot,
                               bool low_4gb, bool reuse, std::string* error_msg);
 
+  // Create placeholder for a region allocated by direct call to mmap.
+  // This is useful when we do not have control over the code calling mmap,
+  // but when we still want to keep track of it in the list.
+  // The region is not considered to be owned and will not be unmmaped.
+  static MemMap* MapDummy(const char* name, uint8_t* addr, size_t byte_count);
+
   // Map part of a file, taking care of non-page aligned offsets.  The
   // "start" offset is absolute, not relative.
   //
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 1a7a3e5..1d06706 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -65,7 +65,7 @@
     DEBUG_ENABLE_SAFEMODE           = 1 << 3,
     DEBUG_ENABLE_JNI_LOGGING        = 1 << 4,
     DEBUG_ENABLE_JIT                = 1 << 5,
-    DEBUG_GENERATE_CFI              = 1 << 6,
+    DEBUG_GENERATE_DEBUG_INFO       = 1 << 6,
   };
 
   Runtime* const runtime = Runtime::Current();
@@ -112,10 +112,10 @@
   }
   runtime->GetJITOptions()->SetUseJIT(use_jit);
 
-  const bool generate_cfi = (debug_flags & DEBUG_GENERATE_CFI) != 0;
-  if (generate_cfi) {
-    runtime->AddCompilerOption("--include-cfi");
-    debug_flags &= ~DEBUG_GENERATE_CFI;
+  const bool generate_debug_info = (debug_flags & DEBUG_GENERATE_DEBUG_INFO) != 0;
+  if (generate_debug_info) {
+    runtime->AddCompilerOption("--generate-debug-info");
+    debug_flags &= ~DEBUG_GENERATE_DEBUG_INFO;
   }
 
   // This is for backwards compatibility with Dalvik.
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 6fda790..ad5741e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -21,6 +21,9 @@
 #include <unistd.h>
 
 #include <cstdlib>
+#ifndef __APPLE__
+#include <link.h>  // for dl_iterate_phdr.
+#endif
 #include <sstream>
 
 // dlopen_ext support from bionic.
@@ -35,6 +38,7 @@
 #include "elf_file.h"
 #include "elf_utils.h"
 #include "oat.h"
+#include "mem_map.h"
 #include "mirror/class.h"
 #include "mirror/object-inl.h"
 #include "os.h"
@@ -45,13 +49,13 @@
 namespace art {
 
 // Whether OatFile::Open will try DlOpen() first. Fallback is our own ELF loader.
-static constexpr bool kUseDlopen = false;
+static constexpr bool kUseDlopen = true;
 
 // Whether OatFile::Open will try DlOpen() on the host. On the host we're not linking against
 // bionic, so cannot take advantage of the support for changed semantics (loading the same soname
 // multiple times). However, if/when we switch the above, we likely want to switch this, too,
 // to get test coverage of the code paths.
-static constexpr bool kUseDlopenOnHost = false;
+static constexpr bool kUseDlopenOnHost = true;
 
 // For debugging, Open will print DlOpen error message if set to true.
 static constexpr bool kPrintDlOpenErrorMessage = false;
@@ -210,6 +214,15 @@
 
 bool OatFile::Dlopen(const std::string& elf_filename, uint8_t* requested_base,
                      const char* abs_dex_location, std::string* error_msg) {
+#ifdef __APPLE__
+  // The dl_iterate_phdr syscall is missing.  There is similar API on OSX,
+  // but let's fallback to the custom loading code for the time being.
+  UNUSED(elf_filename);
+  UNUSED(requested_base);
+  UNUSED(abs_dex_location);
+  UNUSED(error_msg);
+  return false;
+#else
   std::unique_ptr<char> absolute_path(realpath(elf_filename.c_str(), nullptr));
   if (absolute_path == nullptr) {
     *error_msg = StringPrintf("Failed to find absolute path for '%s'", elf_filename.c_str());
@@ -217,7 +230,7 @@
   }
 #ifdef HAVE_ANDROID_OS
   android_dlextinfo extinfo;
-  extinfo.flags = ANDROID_DLEXT_FORCE_LOAD;
+  extinfo.flags = ANDROID_DLEXT_FORCE_LOAD | ANDROID_DLEXT_FORCE_FIXED_VADDR;
   dlopen_handle_ = android_dlopen_ext(absolute_path.get(), RTLD_NOW, &extinfo);
 #else
   dlopen_handle_ = dlopen(absolute_path.get(), RTLD_NOW);
@@ -264,7 +277,49 @@
     bss_end_ += sizeof(uint32_t);
   }
 
+  // Ask the linker where it mmaped the file and notify our mmap wrapper of the regions.
+  struct dl_iterate_context {
+    static int callback(struct dl_phdr_info *info, size_t /* size */, void *data) {
+      auto* context = reinterpret_cast<dl_iterate_context*>(data);
+      // See whether this callback corresponds to the file which we have just loaded.
+      bool contains_begin = false;
+      for (int i = 0; i < info->dlpi_phnum; i++) {
+        if (info->dlpi_phdr[i].p_type == PT_LOAD) {
+          uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
+                                                      info->dlpi_phdr[i].p_vaddr);
+          size_t memsz = info->dlpi_phdr[i].p_memsz;
+          if (vaddr <= context->begin_ && context->begin_ < vaddr + memsz) {
+            contains_begin = true;
+            break;
+          }
+        }
+      }
+      // Add dummy mmaps for this file.
+      if (contains_begin) {
+        for (int i = 0; i < info->dlpi_phnum; i++) {
+          if (info->dlpi_phdr[i].p_type == PT_LOAD) {
+            uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
+                                                        info->dlpi_phdr[i].p_vaddr);
+            size_t memsz = info->dlpi_phdr[i].p_memsz;
+            MemMap* mmap = MemMap::MapDummy(info->dlpi_name, vaddr, memsz);
+            context->dlopen_mmaps_->push_back(std::unique_ptr<MemMap>(mmap));
+          }
+        }
+        return 1;  // Stop iteration and return 1 from dl_iterate_phdr.
+      }
+      return 0;  // Continue iteration and return 0 from dl_iterate_phdr when finished.
+    }
+    const uint8_t* const begin_;
+    std::vector<std::unique_ptr<MemMap>>* const dlopen_mmaps_;
+  } context = { begin_, &dlopen_mmaps_ };
+
+  if (dl_iterate_phdr(dl_iterate_context::callback, &context) == 0) {
+    PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
+    LOG(ERROR) << "File " << elf_filename << " loaded with dlopen but can not find its mmaps.";
+  }
+
   return Setup(abs_dex_location, error_msg);
+#endif  // __APPLE__
 }
 
 bool OatFile::ElfFileOpen(File* file, uint8_t* requested_base, uint8_t* oat_file_begin,
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index c58b029..1a782de 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -321,6 +321,9 @@
   // dlopen handle during runtime.
   void* dlopen_handle_;
 
+  // Dummy memory map objects corresponding to the regions mapped by dlopen.
+  std::vector<std::unique_ptr<MemMap>> dlopen_mmaps_;
+
   // Owning storage for the OatDexFile objects.
   std::vector<const OatDexFile*> oat_dex_files_storage_;
 
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
new file mode 100644
index 0000000..59722ad
--- /dev/null
+++ b/test/137-cfi/cfi.cc
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if __linux__
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#endif
+
+#include "jni.h"
+
+#include <backtrace/Backtrace.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
+#include "oat_file.h"
+#include "utils.h"
+
+namespace art {
+
+// For testing debuggerd. We do not have expected-death tests, so can't test this by default.
+// Code for this is copied from SignalTest.
+static constexpr bool kCauseSegfault = false;
+char* go_away_compiler_cfi = nullptr;
+
+static void CauseSegfault() {
+#if defined(__arm__) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
+  // On supported architectures we cause a real SEGV.
+  *go_away_compiler_cfi = 'a';
+#else
+  // On other architectures we simulate SEGV.
+  kill(getpid(), SIGSEGV);
+#endif
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_sleep(JNIEnv*, jobject, jint, jboolean, jdouble) {
+  // Keep pausing.
+  for (;;) {
+    pause();
+  }
+}
+
+// Helper to look for a sequence in the stack trace.
+#if __linux__
+static bool CheckStack(Backtrace* bt, const std::vector<std::string>& seq) {
+  size_t cur_search_index = 0;  // The currently active index in seq.
+  CHECK_GT(seq.size(), 0U);
+
+  for (Backtrace::const_iterator it = bt->begin(); it != bt->end(); ++it) {
+    if (BacktraceMap::IsValid(it->map)) {
+      LOG(INFO) << "Got " << it->func_name << ", looking for " << seq[cur_search_index];
+      if (it->func_name == seq[cur_search_index]) {
+        cur_search_index++;
+        if (cur_search_index == seq.size()) {
+          return true;
+        }
+      }
+    }
+  }
+
+  printf("Can not find %s in backtrace:\n", seq[cur_search_index].c_str());
+  for (Backtrace::const_iterator it = bt->begin(); it != bt->end(); ++it) {
+    if (BacktraceMap::IsValid(it->map)) {
+      printf("  %s\n", it->func_name.c_str());
+    }
+  }
+
+  return false;
+}
+#endif
+
+// Currently we have to fall back to our own loader for the boot image when it's compiled PIC
+// because its base is zero. Thus in-process unwinding through it won't work. This is a helper
+// detecting this.
+#if __linux__
+static bool IsPicImage() {
+  gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
+  CHECK(image_space != nullptr);  // We should be running with an image.
+  const OatFile* oat_file = image_space->GetOatFile();
+  CHECK(oat_file != nullptr);     // We should have an oat file to go with the image.
+  return oat_file->IsPic();
+}
+#endif
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(JNIEnv*, jobject, jint, jboolean) {
+#if __linux__
+  if (IsPicImage()) {
+    LOG(INFO) << "Image is pic, in-process unwinding check bypassed.";
+    return JNI_TRUE;
+  }
+
+  // TODO: What to do on Valgrind?
+
+  std::unique_ptr<Backtrace> bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid()));
+  if (!bt->Unwind(0, nullptr)) {
+    printf("Can not unwind in process.\n");
+    return JNI_FALSE;
+  } else if (bt->NumFrames() == 0) {
+    printf("No frames for unwind in process.\n");
+    return JNI_FALSE;
+  }
+
+  // We cannot really parse an exact stack, as the optimizing compiler may inline some functions.
+  // This is also risky, as deduping might play a trick on us, so the test needs to make sure that
+  // only unique functions are being expected.
+  std::vector<std::string> seq = {
+      "Java_Main_unwindInProcess",                   // This function.
+      "boolean Main.unwindInProcess(int, boolean)",  // The corresponding Java native method frame.
+      "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)",  // Framework method.
+      "void Main.main(java.lang.String[])"           // The Java entry method.
+  };
+
+  bool result = CheckStack(bt.get(), seq);
+  if (!kCauseSegfault) {
+    return result ? JNI_TRUE : JNI_FALSE;
+  } else {
+    LOG(INFO) << "Result of check-stack: " << result;
+  }
+#endif
+
+  if (kCauseSegfault) {
+    CauseSegfault();
+  }
+
+  return JNI_FALSE;
+}
+
+#if __linux__
+static constexpr int kSleepTimeMicroseconds = 50000;            // 0.05 seconds
+static constexpr int kMaxTotalSleepTimeMicroseconds = 1000000;  // 1 second
+
+// Wait for a sigstop. This code is copied from libbacktrace.
+int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed ATTRIBUTE_UNUSED) {
+  for (;;) {
+    int status;
+    pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL | WNOHANG));
+    if (n == -1) {
+      PLOG(WARNING) << "waitpid failed: tid " << tid;
+      break;
+    } else if (n == tid) {
+      if (WIFSTOPPED(status)) {
+        return WSTOPSIG(status);
+      } else {
+        PLOG(ERROR) << "unexpected waitpid response: n=" << n << ", status=" << std::hex << status;
+        break;
+      }
+    }
+
+    if (*total_sleep_time_usec > kMaxTotalSleepTimeMicroseconds) {
+      PLOG(WARNING) << "timed out waiting for stop signal: tid=" << tid;
+      break;
+    }
+
+    usleep(kSleepTimeMicroseconds);
+    *total_sleep_time_usec += kSleepTimeMicroseconds;
+  }
+
+  return -1;
+}
+#endif
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jobject, jint pid_int) {
+#if __linux__
+  // TODO: What to do on Valgrind?
+  pid_t pid = static_cast<pid_t>(pid_int);
+
+  // OK, this is painful. debuggerd uses ptrace to unwind other processes.
+
+  if (ptrace(PTRACE_ATTACH, pid, 0, 0)) {
+    // Were not able to attach, bad.
+    printf("Failed to attach to other process.\n");
+    PLOG(ERROR) << "Failed to attach.";
+    kill(pid, SIGKILL);
+    return JNI_FALSE;
+  }
+
+  kill(pid, SIGSTOP);
+
+  bool detach_failed = false;
+  int total_sleep_time_usec = 0;
+  int signal = wait_for_sigstop(pid, &total_sleep_time_usec, &detach_failed);
+  if (signal == -1) {
+    LOG(WARNING) << "wait_for_sigstop failed.";
+  }
+
+  std::unique_ptr<Backtrace> bt(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
+  bool result = true;
+  if (!bt->Unwind(0, nullptr)) {
+    printf("Can not unwind other process.\n");
+    result = false;
+  } else if (bt->NumFrames() == 0) {
+    printf("No frames for unwind of other process.\n");
+    result = false;
+  }
+
+  if (result) {
+    // See comment in unwindInProcess for non-exact stack matching.
+    std::vector<std::string> seq = {
+        // "Java_Main_sleep",                        // The sleep function being executed in the
+                                                     // other runtime.
+                                                     // Note: For some reason, the name isn't
+                                                     // resolved, so don't look for it right now.
+        "boolean Main.sleep(int, boolean, double)",  // The corresponding Java native method frame.
+        "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)",  // Framework method.
+        "void Main.main(java.lang.String[])"         // The Java entry method.
+    };
+
+    result = CheckStack(bt.get(), seq);
+  }
+
+  if (ptrace(PTRACE_DETACH, pid, 0, 0) != 0) {
+    PLOG(ERROR) << "Detach failed";
+  }
+
+  // Kill the other process once we are done with it.
+  kill(pid, SIGKILL);
+
+  return result ? JNI_TRUE : JNI_FALSE;
+#else
+  return JNI_FALSE;
+#endif
+}
+
+}  // namespace art
diff --git a/test/137-cfi/expected.txt b/test/137-cfi/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/137-cfi/expected.txt
diff --git a/test/137-cfi/info.txt b/test/137-cfi/info.txt
new file mode 100644
index 0000000..7d59605
--- /dev/null
+++ b/test/137-cfi/info.txt
@@ -0,0 +1 @@
+Test that unwinding with CFI info works.
diff --git a/test/137-cfi/run b/test/137-cfi/run
new file mode 100755
index 0000000..78cf2aa
--- /dev/null
+++ b/test/137-cfi/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Temporarily disable address space layout randomization (ASLR).
+# This is need on host so that the linker loads core.oat at fixed address.
+export LD_USE_LOAD_BIAS=1
+
+exec ${RUN} "$@"
diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java
new file mode 100644
index 0000000..6cd187a
--- /dev/null
+++ b/test/137-cfi/src/Main.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class Main implements Comparator<Main> {
+  // Whether to test local unwinding. Libunwind uses linker info to find executables. As we do
+  // not dlopen at the moment, this doesn't work, so keep it off for now.
+  public final static boolean TEST_LOCAL_UNWINDING = true;
+
+  // Unwinding another process, modelling debuggerd. This doesn't use the linker, so should work
+  // no matter whether we're using dlopen or not.
+  public final static boolean TEST_REMOTE_UNWINDING = true;
+
+  private boolean secondary;
+
+  private boolean passed;
+
+  public Main(boolean secondary) {
+      this.secondary = secondary;
+  }
+
+  public static void main(String[] args) throws Exception {
+      boolean secondary = false;
+      if (args.length > 0 && args[args.length - 1].equals("--secondary")) {
+          secondary = true;
+      }
+      new Main(secondary).run();
+  }
+
+  static {
+      System.loadLibrary("arttest");
+  }
+
+  private void run() {
+      if (secondary) {
+          if (!TEST_REMOTE_UNWINDING) {
+              throw new RuntimeException("Should not be running secondary!");
+          }
+          runSecondary();
+      } else {
+          runPrimary();
+      }
+  }
+
+  private void runSecondary() {
+      foo();
+      throw new RuntimeException("Didn't expect to get back...");
+  }
+
+  private void runPrimary() {
+      // First do the in-process unwinding.
+      if (TEST_LOCAL_UNWINDING && !foo()) {
+          System.out.println("Unwinding self failed.");
+      }
+
+      if (!TEST_REMOTE_UNWINDING) {
+          // Skip the remote step.
+          return;
+      }
+
+      // Fork the secondary.
+      String[] cmdline = getCmdLine();
+      String[] secCmdLine = new String[cmdline.length + 1];
+      System.arraycopy(cmdline, 0, secCmdLine, 0, cmdline.length);
+      secCmdLine[secCmdLine.length - 1] = "--secondary";
+      Process p = exec(secCmdLine);
+
+      try {
+          int pid = getPid(p);
+          if (pid <= 0) {
+              throw new RuntimeException("Couldn't parse process");
+          }
+
+          // Wait a bit, so the forked process has time to run until its sleep phase.
+          try {
+              Thread.sleep(5000);
+          } catch (Exception e) {
+              throw new RuntimeException(e);
+          }
+
+          if (!unwindOtherProcess(pid)) {
+              System.out.println("Unwinding other process failed.");
+          }
+      } finally {
+          // Kill the forked process if it is not already dead.
+          p.destroy();
+      }
+  }
+
+  private static Process exec(String[] args) {
+      try {
+          return Runtime.getRuntime().exec(args);
+      } catch (Exception exc) {
+          throw new RuntimeException(exc);
+      }
+  }
+
+  private static int getPid(Process p) {
+      // Could do reflection for the private pid field, but String parsing is easier.
+      String s = p.toString();
+      if (s.startsWith("Process[pid=")) {
+          return Integer.parseInt(s.substring("Process[pid=".length(), s.length() - 1));
+      } else {
+          return -1;
+      }
+  }
+
+  // Read /proc/self/cmdline to find the invocation command line (so we can fork another runtime).
+  private static String[] getCmdLine() {
+      try {
+          BufferedReader in = new BufferedReader(new FileReader("/proc/self/cmdline"));
+          String s = in.readLine();
+          in.close();
+          return s.split("\0");
+      } catch (Exception exc) {
+          throw new RuntimeException(exc);
+      }
+  }
+
+  public boolean foo() {
+      // Call bar via Arrays.binarySearch.
+      // This tests that we can unwind from framework code.
+      Main[] array = { this, this, this };
+      Arrays.binarySearch(array, 0, 3, this /* value */, this /* comparator */);
+      return passed;
+  }
+
+  public int compare(Main lhs, Main rhs) {
+      passed = bar(secondary);
+      // Returning "equal" ensures that we terminate search
+      // after first item and thus call bar() only once.
+      return 0;
+  }
+
+  public boolean bar(boolean b) {
+      if (b) {
+          return sleep(2, b, 1.0);
+      } else {
+          return unwindInProcess(1, b);
+      }
+  }
+
+  // Native functions. Note: to avoid deduping, they must all have different signatures.
+
+  public native boolean sleep(int i, boolean b, double dummy);
+
+  public native boolean unwindInProcess(int i, boolean b);
+  public native boolean unwindOtherProcess(int pid);
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 6ad8787..57d06c4 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -28,6 +28,7 @@
   116-nodex2oat/nodex2oat.cc \
   117-nopatchoat/nopatchoat.cc \
   118-noimage-dex2oat/noimage-dex2oat.cc \
+  137-cfi/cfi.cc \
   139-register-natives/regnative.cc \
   454-get-vreg/get_vreg_jni.cc \
   455-set-vreg/set_vreg_jni.cc \
@@ -57,7 +58,7 @@
     LOCAL_MODULE_TAGS := tests
   endif
   LOCAL_SRC_FILES := $(LIBARTTEST_COMMON_SRC_FILES)
-  LOCAL_SHARED_LIBRARIES += libartd
+  LOCAL_SHARED_LIBRARIES += libartd libbacktrace
   LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
   LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libarttest.mk
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index c9e512e..5eda539 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -269,10 +269,12 @@
   117-nopatchoat \
   118-noimage-dex2oat \
   119-noimage-patchoat \
+  137-cfi \
   138-duplicate-classes-check2
 
 # This test fails without an image.
 TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \
+  137-cfi \
   138-duplicate-classes-check
 
 ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES)))
@@ -299,9 +301,13 @@
 
 TEST_ART_BROKEN_FALLBACK_RUN_TESTS :=
 
+# 137:
+# This test unrolls and expects managed frames, but tracing means we run the interpreter.
+# 802:
 # This test dynamically enables tracing to force a deoptimization. This makes the test meaningless
 # when already tracing, and writes an error message that we do not want to check for.
 TEST_ART_BROKEN_TRACING_RUN_TESTS := \
+  137-cfi \
   802-deoptimization
 
 ifneq (,$(filter trace,$(TRACE_TYPES)))
@@ -328,6 +334,7 @@
   118-noimage-dex2oat \
   119-noimage-patchoat \
   131-structural-change \
+  137-cfi \
   139-register-natives \
   454-get-vreg \
   455-set-vreg \
@@ -343,6 +350,33 @@
 
 TEST_ART_BROKEN_NDEBUG_TESTS :=
 
+# Known broken tests for the interpreter.
+# CFI unwinding expects managed frames.
+TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := \
+  137-cfi
+
+ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      interpreter,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_INTERPRETER_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_INTERPRETER_RUN_TESTS :=
+
+# Known broken tests for the JIT.
+# CFI unwinding expects managed frames, and the test does not iterate enough to even compile. JIT
+# also uses Generic JNI instead of the JNI compiler.
+TEST_ART_BROKEN_JIT_RUN_TESTS := \
+  137-cfi
+
+ifneq (,$(filter jit,$(COMPILER_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      jit,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_JIT_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_JIT_RUN_TESTS :=
+
 # Known broken tests for the default compiler (Quick).
 TEST_ART_BROKEN_DEFAULT_RUN_TESTS := \
   457-regs
@@ -434,8 +468,11 @@
 TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS :=
 
 # Tests that should fail in the read barrier configuration.
+# 098: b/20720510
+# 137: Read barrier forces interpreter. Cannot run this with the interpreter.
 TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS := \
-  098-ddmc  # b/20720510
+  098-ddmc \
+  137-cfi \
 
 ifeq ($(ART_USE_READ_BARRIER),true)
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -445,6 +482,19 @@
 
 TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS :=
 
+# Tests that should fail in the heap poisoning configuration.
+# 137: Heap poisoning forces interpreter. Cannot run this with the interpreter.
+TEST_ART_BROKEN_HEAP_POISONING_RUN_TESTS := \
+  137-cfi
+
+ifeq ($(ART_HEAP_POISONING),true)
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_HEAP_POISONING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_HEAP_POISONING_RUN_TESTS :=
+
 # Clear variables ahead of appending to them when defining tests.
 $(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=))
 $(foreach target, $(TARGET_TYPES), \
diff --git a/tools/art b/tools/art
index f167a73..2ee8940 100644
--- a/tools/art
+++ b/tools/art
@@ -97,7 +97,7 @@
     -XXlib:$LIBART \
     -Xnorelocate \
     -Ximage:$ANDROID_ROOT/framework/core.art \
-    -Xcompiler-option --include-debug-symbols \
+    -Xcompiler-option --generate-debug-info \
     "$@"
 
 EXIT_STATUS=$?