Extend the DWARF library to support .debug_info section.

Change-Id: I9916abd8db227e7a73a3311294e675be5222a709
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h
index f550395..9181792 100644
--- a/compiler/cfi_test.h
+++ b/compiler/cfi_test.h
@@ -22,8 +22,8 @@
 #include <sstream>
 
 #include "arch/instruction_set.h"
-#include "dwarf/debug_frame_writer.h"
 #include "dwarf/dwarf_test.h"
+#include "dwarf/headers.h"
 #include "disassembler/disassembler.h"
 #include "gtest/gtest.h"
 
@@ -43,9 +43,10 @@
     HexDump(f, actual_cfi);
     fprintf(f, "\n};\n");
     // Pretty-print CFI opcodes.
-    dwarf::DebugFrameWriter<> eh_frame(&eh_frame_data_, false);
-    eh_frame.WriteCIE(dwarf::Reg(8), {});
-    eh_frame.WriteFDE(0, actual_asm.size(), actual_cfi.data(), actual_cfi.size());
+    constexpr bool is64bit = false;
+    dwarf::DebugFrameOpCodeWriter<> initial_opcodes;
+    dwarf::WriteEhFrameCIE(is64bit, dwarf::Reg(8), initial_opcodes, &eh_frame_data_);
+    dwarf::WriteEhFrameFDE(is64bit, 0, 0, actual_asm.size(), &actual_cfi, &eh_frame_data_);
     ReformatCfi(Objdump(false, "-W"), &lines);
     // Pretty-print assembly.
     auto* opts = new DisassemblerOptions(false, actual_asm.data(), true);
diff --git a/compiler/dwarf/debug_frame_writer.h b/compiler/dwarf/debug_frame_writer.h
deleted file mode 100644
index 3502906..0000000
--- a/compiler/dwarf/debug_frame_writer.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_COMPILER_DWARF_DEBUG_FRAME_WRITER_H_
-#define ART_COMPILER_DWARF_DEBUG_FRAME_WRITER_H_
-
-#include "debug_frame_opcode_writer.h"
-#include "dwarf.h"
-#include "writer.h"
-
-namespace art {
-namespace dwarf {
-
-// Writer for the .eh_frame section (which extends .debug_frame specification).
-template<typename Allocator = std::allocator<uint8_t>>
-class DebugFrameWriter FINAL : private Writer<Allocator> {
- public:
-  void WriteCIE(Reg return_address_register,
-                const uint8_t* initial_opcodes,
-                int initial_opcodes_size) {
-    DCHECK(cie_header_start_ == ~0u);
-    cie_header_start_ = this->data()->size();
-    if (use_64bit_address_) {
-      // TODO: This is not related to being 64bit.
-      this->PushUint32(0xffffffff);
-      this->PushUint64(0);  // Length placeholder.
-      this->PushUint64(0);  // CIE id.
-    } else {
-      this->PushUint32(0);  // Length placeholder.
-      this->PushUint32(0);  // CIE id.
-    }
-    this->PushUint8(1);   // Version.
-    this->PushString("zR");
-    this->PushUleb128(DebugFrameOpCodeWriter<Allocator>::kCodeAlignmentFactor);
-    this->PushSleb128(DebugFrameOpCodeWriter<Allocator>::kDataAlignmentFactor);
-    this->PushUleb128(return_address_register.num());  // ubyte in DWARF2.
-    this->PushUleb128(1);  // z: Augmentation data size.
-    if (use_64bit_address_) {
-      this->PushUint8(0x04);  // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata8).
-    } else {
-      this->PushUint8(0x03);  // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4).
-    }
-    this->PushData(initial_opcodes, initial_opcodes_size);
-    this->Pad(use_64bit_address_ ? 8 : 4);
-    if (use_64bit_address_) {
-      this->UpdateUint64(cie_header_start_ + 4, this->data()->size() - cie_header_start_ - 12);
-    } else {
-      this->UpdateUint32(cie_header_start_, this->data()->size() - cie_header_start_ - 4);
-    }
-  }
-
-  void WriteCIE(Reg return_address_register,
-                const DebugFrameOpCodeWriter<Allocator>& opcodes) {
-    WriteCIE(return_address_register, opcodes.data()->data(), opcodes.data()->size());
-  }
-
-  void WriteFDE(uint64_t initial_address,
-                uint64_t address_range,
-                const uint8_t* unwind_opcodes,
-                int unwind_opcodes_size) {
-    DCHECK(cie_header_start_ != ~0u);
-    size_t fde_header_start = this->data()->size();
-    if (use_64bit_address_) {
-      // TODO: This is not related to being 64bit.
-      this->PushUint32(0xffffffff);
-      this->PushUint64(0);  // Length placeholder.
-      this->PushUint64(this->data()->size() - cie_header_start_);  // 'CIE_pointer'
-    } else {
-      this->PushUint32(0);  // Length placeholder.
-      this->PushUint32(static_cast<uint32_t>(this->data()->size() - cie_header_start_));  // 'CIE_pointer'
-    }
-    if (use_64bit_address_) {
-      this->PushUint64(initial_address);
-      this->PushUint64(address_range);
-    } else {
-      this->PushUint32(initial_address);
-      this->PushUint32(address_range);
-    }
-    this->PushUleb128(0);  // Augmentation data size.
-    this->PushData(unwind_opcodes, unwind_opcodes_size);
-    this->Pad(use_64bit_address_ ? 8 : 4);
-    if (use_64bit_address_) {
-      this->UpdateUint64(fde_header_start + 4, this->data()->size() - fde_header_start - 12);
-    } else {
-      this->UpdateUint32(fde_header_start, this->data()->size() - fde_header_start - 4);
-    }
-  }
-
-  DebugFrameWriter(std::vector<uint8_t, Allocator>* buffer, bool use_64bit_address)
-      : Writer<Allocator>(buffer),
-        use_64bit_address_(use_64bit_address),
-        cie_header_start_(~0u) {
-  }
-
- private:
-  bool use_64bit_address_;
-  size_t cie_header_start_;
-
-  DISALLOW_COPY_AND_ASSIGN(DebugFrameWriter);
-};
-
-}  // namespace dwarf
-}  // namespace art
-
-#endif  // ART_COMPILER_DWARF_DEBUG_FRAME_WRITER_H_
diff --git a/compiler/dwarf/debug_info_entry_writer.h b/compiler/dwarf/debug_info_entry_writer.h
new file mode 100644
index 0000000..c0350b6
--- /dev/null
+++ b/compiler/dwarf/debug_info_entry_writer.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef ART_COMPILER_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
+#define ART_COMPILER_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
+
+#include <unordered_map>
+
+#include "dwarf.h"
+#include "leb128.h"
+#include "writer.h"
+
+namespace art {
+namespace dwarf {
+
+// 32-bit FNV-1a hash function which we use to find duplicate abbreviations.
+// See http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
+template< typename Allocator >
+struct FNVHash {
+  size_t operator()(const std::vector<uint8_t, Allocator>& v) const {
+    uint32_t hash = 2166136261u;
+    for (size_t i = 0; i < v.size(); i++) {
+      hash = (hash ^ v[i]) * 16777619u;
+    }
+    return hash;
+  }
+};
+
+/*
+ * Writer for debug information entries (DIE).
+ * It also handles generation of abbreviations.
+ *
+ * Usage:
+ *   StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes);
+ *     WriteStrp(DW_AT_producer, "Compiler name", debug_str);
+ *     StartTag(DW_TAG_subprogram, DW_CHILDREN_no);
+ *       WriteStrp(DW_AT_name, "Foo", debug_str);
+ *     EndTag();
+ *   EndTag();
+ */
+template< typename Allocator = std::allocator<uint8_t> >
+class DebugInfoEntryWriter FINAL : private Writer<Allocator> {
+ public:
+  // Start debugging information entry.
+  void StartTag(Tag tag, Children children) {
+    DCHECK(has_children) << "This tag can not have nested tags";
+    if (inside_entry_) {
+      // Write abbrev code for the previous entry.
+      this->UpdateUleb128(abbrev_code_offset_, EndAbbrev());
+      inside_entry_ = false;
+    }
+    StartAbbrev(tag, children);
+    // Abbrev code placeholder of sufficient size.
+    abbrev_code_offset_ = this->data()->size();
+    this->PushUleb128(NextAbbrevCode());
+    depth_++;
+    inside_entry_ = true;
+    has_children = (children == DW_CHILDREN_yes);
+  }
+
+  // End debugging information entry.
+  void EndTag() {
+    DCHECK_GT(depth_, 0);
+    if (inside_entry_) {
+      // Write abbrev code for this tag.
+      this->UpdateUleb128(abbrev_code_offset_, EndAbbrev());
+      inside_entry_ = false;
+    }
+    if (has_children) {
+      this->PushUint8(0);  // End of children.
+    }
+    depth_--;
+    has_children = true;  // Parent tag obviously has children.
+  }
+
+  void WriteAddr(Attribute attrib, uint64_t value) {
+    AddAbbrevAttribute(attrib, DW_FORM_addr);
+    if (is64bit_) {
+      this->PushUint64(value);
+    } else {
+      this->PushUint32(value);
+    }
+  }
+
+  void WriteBlock(Attribute attrib, const void* ptr, int size) {
+    AddAbbrevAttribute(attrib, DW_FORM_block);
+    this->PushUleb128(size);
+    this->PushData(ptr, size);
+  }
+
+  void WriteData1(Attribute attrib, uint8_t value) {
+    AddAbbrevAttribute(attrib, DW_FORM_data1);
+    this->PushUint8(value);
+  }
+
+  void WriteData2(Attribute attrib, uint16_t value) {
+    AddAbbrevAttribute(attrib, DW_FORM_data2);
+    this->PushUint16(value);
+  }
+
+  void WriteData4(Attribute attrib, uint32_t value) {
+    AddAbbrevAttribute(attrib, DW_FORM_data4);
+    this->PushUint32(value);
+  }
+
+  void WriteData8(Attribute attrib, uint64_t value) {
+    AddAbbrevAttribute(attrib, DW_FORM_data8);
+    this->PushUint64(value);
+  }
+
+  void WriteSdata(Attribute attrib, int value) {
+    AddAbbrevAttribute(attrib, DW_FORM_sdata);
+    this->PushSleb128(value);
+  }
+
+  void WriteUdata(Attribute attrib, int value) {
+    AddAbbrevAttribute(attrib, DW_FORM_udata);
+    this->PushUleb128(value);
+  }
+
+  void WriteUdata(Attribute attrib, uint32_t value) {
+    AddAbbrevAttribute(attrib, DW_FORM_udata);
+    this->PushUleb128(value);
+  }
+
+  void WriteFlag(Attribute attrib, bool value) {
+    AddAbbrevAttribute(attrib, DW_FORM_flag);
+    this->PushUint8(value ? 1 : 0);
+  }
+
+  void WriteRef4(Attribute attrib, int cu_offset) {
+    AddAbbrevAttribute(attrib, DW_FORM_ref4);
+    this->PushUint32(cu_offset);
+  }
+
+  void WriteRef(Attribute attrib, int cu_offset) {
+    AddAbbrevAttribute(attrib, DW_FORM_ref_udata);
+    this->PushUleb128(cu_offset);
+  }
+
+  void WriteString(Attribute attrib, const char* value) {
+    AddAbbrevAttribute(attrib, DW_FORM_string);
+    this->PushString(value);
+  }
+
+  void WriteStrp(Attribute attrib, int address) {
+    AddAbbrevAttribute(attrib, DW_FORM_strp);
+    this->PushUint32(address);
+  }
+
+  void WriteStrp(Attribute attrib, const char* value, std::vector<uint8_t>* debug_str) {
+    AddAbbrevAttribute(attrib, DW_FORM_strp);
+    int address = debug_str->size();
+    debug_str->insert(debug_str->end(), value, value + strlen(value) + 1);
+    this->PushUint32(address);
+  }
+
+  bool is64bit() const { return is64bit_; }
+
+  using Writer<Allocator>::data;
+
+  DebugInfoEntryWriter(bool is64bitArch,
+                       std::vector<uint8_t, Allocator>* debug_abbrev,
+                       const Allocator& alloc = Allocator())
+      : Writer<Allocator>(&entries_),
+        debug_abbrev_(debug_abbrev),
+        current_abbrev_(alloc),
+        abbrev_codes_(alloc),
+        entries_(alloc),
+        is64bit_(is64bitArch) {
+    debug_abbrev_.PushUint8(0);  // Add abbrev table terminator.
+  }
+
+  ~DebugInfoEntryWriter() {
+    DCHECK_EQ(depth_, 0);
+  }
+
+ private:
+  // Start abbreviation declaration.
+  void StartAbbrev(Tag tag, Children children) {
+    DCHECK(!inside_entry_);
+    current_abbrev_.clear();
+    EncodeUnsignedLeb128(&current_abbrev_, tag);
+    current_abbrev_.push_back(children);
+  }
+
+  // Add attribute specification.
+  void AddAbbrevAttribute(Attribute name, Form type) {
+    DCHECK(inside_entry_) << "Call StartTag before adding attributes.";
+    EncodeUnsignedLeb128(&current_abbrev_, name);
+    EncodeUnsignedLeb128(&current_abbrev_, type);
+  }
+
+  int NextAbbrevCode() {
+    return 1 + abbrev_codes_.size();
+  }
+
+  // End abbreviation declaration and return its code.
+  int EndAbbrev() {
+    DCHECK(inside_entry_);
+    auto it = abbrev_codes_.insert(std::make_pair(std::move(current_abbrev_),
+                                                  NextAbbrevCode()));
+    int abbrev_code = it.first->second;
+    if (UNLIKELY(it.second)) {  // Inserted new entry.
+      const std::vector<uint8_t, Allocator>& abbrev = it.first->first;
+      debug_abbrev_.Pop();  // Remove abbrev table terminator.
+      debug_abbrev_.PushUleb128(abbrev_code);
+      debug_abbrev_.PushData(abbrev.data(), abbrev.size());
+      debug_abbrev_.PushUint8(0);  // Attribute list end.
+      debug_abbrev_.PushUint8(0);  // Attribute list end.
+      debug_abbrev_.PushUint8(0);  // Add abbrev table terminator.
+    }
+    return abbrev_code;
+  }
+
+ private:
+  // Fields for writing and deduplication of abbrevs.
+  Writer<Allocator> debug_abbrev_;
+  std::vector<uint8_t, Allocator> current_abbrev_;
+  std::unordered_map<std::vector<uint8_t, Allocator>, int,
+                     FNVHash<Allocator> > abbrev_codes_;
+
+  // Fields for writing of debugging information entries.
+  std::vector<uint8_t, Allocator> entries_;
+  bool is64bit_;
+  int depth_ = 0;
+  size_t abbrev_code_offset_ = 0;  // Location to patch once we know the code.
+  bool inside_entry_ = false;  // Entry ends at first child (if any).
+  bool has_children = true;
+};
+
+}  // namespace dwarf
+}  // namespace art
+
+#endif  // ART_COMPILER_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
diff --git a/compiler/dwarf/debug_line_writer.h b/compiler/dwarf/debug_line_writer.h
deleted file mode 100644
index 4b7d8d9..0000000
--- a/compiler/dwarf/debug_line_writer.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_COMPILER_DWARF_DEBUG_LINE_WRITER_H_
-#define ART_COMPILER_DWARF_DEBUG_LINE_WRITER_H_
-
-#include "debug_line_opcode_writer.h"
-#include "dwarf.h"
-#include "writer.h"
-#include <string>
-
-namespace art {
-namespace dwarf {
-
-// Writer for the .debug_line section (DWARF-3).
-template<typename Allocator = std::allocator<uint8_t>>
-class DebugLineWriter FINAL : private Writer<Allocator> {
- public:
-  struct FileEntry {
-    std::string file_name;
-    int directory_index;
-    int modification_time;
-    int file_size;
-  };
-
-  void WriteTable(const std::vector<std::string>& include_directories,
-                  const std::vector<FileEntry>& files,
-                  const DebugLineOpCodeWriter<Allocator>& opcodes) {
-    size_t header_start = this->data()->size();
-    this->PushUint32(0);  // Section-length placeholder.
-    // Claim DWARF-2 version even though we use some DWARF-3 features.
-    // DWARF-2 consumers will ignore the unknown opcodes.
-    // This is what clang currently does.
-    this->PushUint16(2);  // .debug_line version.
-    size_t header_length_pos = this->data()->size();
-    this->PushUint32(0);  // Header-length placeholder.
-    this->PushUint8(1 << opcodes.GetCodeFactorBits());
-    this->PushUint8(DebugLineOpCodeWriter<Allocator>::kDefaultIsStmt ? 1 : 0);
-    this->PushInt8(DebugLineOpCodeWriter<Allocator>::kLineBase);
-    this->PushUint8(DebugLineOpCodeWriter<Allocator>::kLineRange);
-    this->PushUint8(DebugLineOpCodeWriter<Allocator>::kOpcodeBase);
-    static const int opcode_lengths[DebugLineOpCodeWriter<Allocator>::kOpcodeBase] = {
-        0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 };
-    for (int i = 1; i < DebugLineOpCodeWriter<Allocator>::kOpcodeBase; i++) {
-      this->PushUint8(opcode_lengths[i]);
-    }
-    for (const std::string& directory : include_directories) {
-      this->PushData(directory.data(), directory.size() + 1);
-    }
-    this->PushUint8(0);  // Terminate include_directories list.
-    for (const FileEntry& file : files) {
-      this->PushData(file.file_name.data(), file.file_name.size() + 1);
-      this->PushUleb128(file.directory_index);
-      this->PushUleb128(file.modification_time);
-      this->PushUleb128(file.file_size);
-    }
-    this->PushUint8(0);  // Terminate file list.
-    this->UpdateUint32(header_length_pos, this->data()->size() - header_length_pos - 4);
-    this->PushData(opcodes.data()->data(), opcodes.data()->size());
-    this->UpdateUint32(header_start, this->data()->size() - header_start - 4);
-  }
-
-  explicit DebugLineWriter(std::vector<uint8_t, Allocator>* buffer)
-    : Writer<Allocator>(buffer) {
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DebugLineWriter);
-};
-
-}  // namespace dwarf
-}  // namespace art
-
-#endif  // ART_COMPILER_DWARF_DEBUG_LINE_WRITER_H_
diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc
index 2b051c9..ec18e96 100644
--- a/compiler/dwarf/dwarf_test.cc
+++ b/compiler/dwarf/dwarf_test.cc
@@ -17,9 +17,9 @@
 #include "dwarf_test.h"
 
 #include "dwarf/debug_frame_opcode_writer.h"
-#include "dwarf/debug_frame_writer.h"
+#include "dwarf/debug_info_entry_writer.h"
 #include "dwarf/debug_line_opcode_writer.h"
-#include "dwarf/debug_line_writer.h"
+#include "dwarf/headers.h"
 #include "gtest/gtest.h"
 
 namespace art {
@@ -118,23 +118,20 @@
   DW_CHECK_NEXT("DW_CFA_restore: r2 (edx)");
   DW_CHECK_NEXT("DW_CFA_restore: r5 (ebp)");
 
-  DebugFrameWriter<> eh_frame(&eh_frame_data_, is64bit);
   DebugFrameOpCodeWriter<> initial_opcodes;
-  eh_frame.WriteCIE(Reg(is64bit ? 16 : 8),  // Return address register.
-                    initial_opcodes);  // Initial opcodes.
-  eh_frame.WriteFDE(0x01000000, 0x01000000,
-                    opcodes.data()->data(), opcodes.data()->size());
+  WriteEhFrameCIE(is64bit, Reg(is64bit ? 16 : 8), initial_opcodes, &eh_frame_data_);
+  WriteEhFrameFDE(is64bit, 0, 0x01000000, 0x01000000, opcodes.data(), &eh_frame_data_);
   CheckObjdumpOutput(is64bit, "-W");
 }
 
 // TODO: objdump seems to have trouble with 64bit CIE length.
 TEST_F(DwarfTest, DISABLED_DebugFrame64) {
-  const bool is64bit = true;
-  DebugFrameWriter<> eh_frame(&eh_frame_data_, is64bit);
-  DebugFrameOpCodeWriter<> no_opcodes;
-  eh_frame.WriteCIE(Reg(16), no_opcodes);
-  eh_frame.WriteFDE(0x0100000000000000, 0x0200000000000000,
-                    no_opcodes.data()->data(), no_opcodes.data()->size());
+  constexpr bool is64bit = true;
+  DebugFrameOpCodeWriter<> initial_opcodes;
+  WriteEhFrameCIE(is64bit, Reg(16), initial_opcodes, &eh_frame_data_);
+  DebugFrameOpCodeWriter<> opcodes;
+  WriteEhFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
+                  opcodes.data(), &eh_frame_data_);
   DW_CHECK("FDE cie=00000000 pc=100000000000000..300000000000000");
   CheckObjdumpOutput(is64bit, "-W");
 }
@@ -148,7 +145,7 @@
   include_directories.push_back("/path/to/source");
   DW_CHECK("/path/to/source");
 
-  std::vector<DebugLineWriter<>::FileEntry> files {
+  std::vector<FileEntry> files {
     { "file0.c", 0, 1000, 2000 },
     { "file1.c", 1, 1000, 2000 },
     { "file2.c", 1, 1000, 2000 },
@@ -187,8 +184,7 @@
   DW_CHECK_NEXT("Entry\tDir\tTime\tSize\tName");
   DW_CHECK_NEXT("1\t0\t1000\t2000\tfile.c");
 
-  DebugLineWriter<> debug_line(&debug_line_data_);
-  debug_line.WriteTable(include_directories, files, opcodes);
+  WriteDebugLineTable(include_directories, files, opcodes, &debug_line_data_);
   CheckObjdumpOutput(is64bit, "-W");
 }
 
@@ -222,14 +218,63 @@
   EXPECT_LT(opcodes.data()->size(), num_rows * 3);
 
   std::vector<std::string> directories;
-  std::vector<DebugLineWriter<>::FileEntry> files {
-    { "file.c", 0, 1000, 2000 },
-  };
-  DebugLineWriter<> debug_line(&debug_line_data_);
-  debug_line.WriteTable(directories, files, opcodes);
+  std::vector<FileEntry> files { { "file.c", 0, 1000, 2000 } };  // NOLINT
+  WriteDebugLineTable(directories, files, opcodes, &debug_line_data_);
   CheckObjdumpOutput(is64bit, "-W -WL");
 }
 
+TEST_F(DwarfTest, DebugInfo) {
+  constexpr bool is64bit = false;
+  DebugInfoEntryWriter<> info(is64bit, &debug_abbrev_data_);
+  DW_CHECK("Contents of the .debug_info section:");
+  info.StartTag(dwarf::DW_TAG_compile_unit, dwarf::DW_CHILDREN_yes);
+  DW_CHECK("Abbrev Number: 1 (DW_TAG_compile_unit)");
+  info.WriteStrp(dwarf::DW_AT_producer, "Compiler name", &debug_str_data_);
+  DW_CHECK_NEXT("DW_AT_producer    : (indirect string, offset: 0x0): Compiler name");
+  info.WriteAddr(dwarf::DW_AT_low_pc, 0x01000000);
+  DW_CHECK_NEXT("DW_AT_low_pc      : 0x1000000");
+  info.WriteAddr(dwarf::DW_AT_high_pc, 0x02000000);
+  DW_CHECK_NEXT("DW_AT_high_pc     : 0x2000000");
+  info.StartTag(dwarf::DW_TAG_subprogram, dwarf::DW_CHILDREN_no);
+  DW_CHECK("Abbrev Number: 2 (DW_TAG_subprogram)");
+  info.WriteStrp(dwarf::DW_AT_name, "Foo", &debug_str_data_);
+  DW_CHECK_NEXT("DW_AT_name        : (indirect string, offset: 0xe): Foo");
+  info.WriteAddr(dwarf::DW_AT_low_pc, 0x01010000);
+  DW_CHECK_NEXT("DW_AT_low_pc      : 0x1010000");
+  info.WriteAddr(dwarf::DW_AT_high_pc, 0x01020000);
+  DW_CHECK_NEXT("DW_AT_high_pc     : 0x1020000");
+  info.EndTag();  // DW_TAG_subprogram
+  info.StartTag(dwarf::DW_TAG_subprogram, dwarf::DW_CHILDREN_no);
+  DW_CHECK("Abbrev Number: 2 (DW_TAG_subprogram)");
+  info.WriteStrp(dwarf::DW_AT_name, "Bar", &debug_str_data_);
+  DW_CHECK_NEXT("DW_AT_name        : (indirect string, offset: 0x12): Bar");
+  info.WriteAddr(dwarf::DW_AT_low_pc, 0x01020000);
+  DW_CHECK_NEXT("DW_AT_low_pc      : 0x1020000");
+  info.WriteAddr(dwarf::DW_AT_high_pc, 0x01030000);
+  DW_CHECK_NEXT("DW_AT_high_pc     : 0x1030000");
+  info.EndTag();  // DW_TAG_subprogram
+  info.EndTag();  // DW_TAG_compile_unit
+  // Test that previous list was properly terminated and empty children.
+  info.StartTag(dwarf::DW_TAG_compile_unit, dwarf::DW_CHILDREN_yes);
+  info.EndTag();  // DW_TAG_compile_unit
+
+  // The abbrev table is just side product, but check it as well.
+  DW_CHECK("Abbrev Number: 3 (DW_TAG_compile_unit)");
+  DW_CHECK("Contents of the .debug_abbrev section:");
+  DW_CHECK("1      DW_TAG_compile_unit    [has children]");
+  DW_CHECK_NEXT("DW_AT_producer     DW_FORM_strp");
+  DW_CHECK_NEXT("DW_AT_low_pc       DW_FORM_addr");
+  DW_CHECK_NEXT("DW_AT_high_pc      DW_FORM_addr");
+  DW_CHECK("2      DW_TAG_subprogram    [no children]");
+  DW_CHECK_NEXT("DW_AT_name         DW_FORM_strp");
+  DW_CHECK_NEXT("DW_AT_low_pc       DW_FORM_addr");
+  DW_CHECK_NEXT("DW_AT_high_pc      DW_FORM_addr");
+  DW_CHECK("3      DW_TAG_compile_unit    [has children]");
+
+  dwarf::WriteDebugInfoCU(0 /* debug_abbrev_offset */, info, &debug_info_data_);
+  CheckObjdumpOutput(is64bit, "-W");
+}
+
 #endif  // HAVE_ANDROID_OS
 
 }  // namespace dwarf
diff --git a/compiler/dwarf/headers.h b/compiler/dwarf/headers.h
new file mode 100644
index 0000000..d866b91
--- /dev/null
+++ b/compiler/dwarf/headers.h
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_DWARF_HEADERS_H_
+#define ART_COMPILER_DWARF_HEADERS_H_
+
+#include "debug_frame_opcode_writer.h"
+#include "debug_info_entry_writer.h"
+#include "debug_line_opcode_writer.h"
+#include "register.h"
+#include "writer.h"
+
+namespace art {
+namespace dwarf {
+
+// Write common information entry (CIE) to .eh_frame section.
+template<typename Allocator>
+void WriteEhFrameCIE(bool is64bit, Reg return_address_register,
+                     const DebugFrameOpCodeWriter<Allocator>& opcodes,
+                     std::vector<uint8_t>* eh_frame) {
+  Writer<> writer(eh_frame);
+  size_t cie_header_start_ = writer.data()->size();
+  if (is64bit) {
+    // TODO: This is not related to being 64bit.
+    writer.PushUint32(0xffffffff);
+    writer.PushUint64(0);  // Length placeholder.
+    writer.PushUint64(0);  // CIE id.
+  } else {
+    writer.PushUint32(0);  // Length placeholder.
+    writer.PushUint32(0);  // CIE id.
+  }
+  writer.PushUint8(1);   // Version.
+  writer.PushString("zR");
+  writer.PushUleb128(DebugFrameOpCodeWriter<Allocator>::kCodeAlignmentFactor);
+  writer.PushSleb128(DebugFrameOpCodeWriter<Allocator>::kDataAlignmentFactor);
+  writer.PushUleb128(return_address_register.num());  // ubyte in DWARF2.
+  writer.PushUleb128(1);  // z: Augmentation data size.
+  if (is64bit) {
+    writer.PushUint8(0x04);  // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata8).
+  } else {
+    writer.PushUint8(0x03);  // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4).
+  }
+  writer.PushData(opcodes.data());
+  writer.Pad(is64bit ? 8 : 4);
+  if (is64bit) {
+    writer.UpdateUint64(cie_header_start_ + 4, writer.data()->size() - cie_header_start_ - 12);
+  } else {
+    writer.UpdateUint32(cie_header_start_, writer.data()->size() - cie_header_start_ - 4);
+  }
+}
+
+// Write frame description entry (FDE) to .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) {
+  Writer<> writer(eh_frame);
+  size_t fde_header_start = writer.data()->size();
+  if (is64bit) {
+    // TODO: This is not related to being 64bit.
+    writer.PushUint32(0xffffffff);
+    writer.PushUint64(0);  // Length placeholder.
+    uint64_t cie_pointer = writer.data()->size() - cie_offset;
+    writer.PushUint64(cie_pointer);
+  } else {
+    writer.PushUint32(0);  // Length placeholder.
+    uint32_t cie_pointer = writer.data()->size() - cie_offset;
+    writer.PushUint32(cie_pointer);
+  }
+  if (is64bit) {
+    writer.PushUint64(initial_address);
+    writer.PushUint64(address_range);
+  } else {
+    writer.PushUint32(initial_address);
+    writer.PushUint32(address_range);
+  }
+  writer.PushUleb128(0);  // Augmentation data size.
+  writer.PushData(opcodes);
+  writer.Pad(is64bit ? 8 : 4);
+  if (is64bit) {
+    writer.UpdateUint64(fde_header_start + 4, writer.data()->size() - fde_header_start - 12);
+  } else {
+    writer.UpdateUint32(fde_header_start, writer.data()->size() - fde_header_start - 4);
+  }
+}
+
+// Write compilation unit (CU) to .debug_info section.
+template<typename Allocator>
+void WriteDebugInfoCU(uint32_t debug_abbrev_offset,
+                      const DebugInfoEntryWriter<Allocator>& entries,
+                      std::vector<uint8_t>* debug_info) {
+  Writer<> writer(debug_info);
+  size_t start = writer.data()->size();
+  writer.PushUint32(0);  // Length placeholder.
+  writer.PushUint16(3);  // Version.
+  writer.PushUint32(debug_abbrev_offset);
+  writer.PushUint8(entries.is64bit() ? 8 : 4);
+  writer.PushData(entries.data());
+  writer.UpdateUint32(start, writer.data()->size() - start - 4);
+}
+
+struct FileEntry {
+  std::string file_name;
+  int directory_index;
+  int modification_time;
+  int file_size;
+};
+
+// Write line table to .debug_line section.
+template<typename Allocator>
+void WriteDebugLineTable(const std::vector<std::string>& include_directories,
+                         const std::vector<FileEntry>& files,
+                         const DebugLineOpCodeWriter<Allocator>& opcodes,
+                         std::vector<uint8_t>* debug_line) {
+  Writer<> writer(debug_line);
+  size_t header_start = writer.data()->size();
+  writer.PushUint32(0);  // Section-length placeholder.
+  // Claim DWARF-2 version even though we use some DWARF-3 features.
+  // DWARF-2 consumers will ignore the unknown opcodes.
+  // This is what clang currently does.
+  writer.PushUint16(2);  // .debug_line version.
+  size_t header_length_pos = writer.data()->size();
+  writer.PushUint32(0);  // Header-length placeholder.
+  writer.PushUint8(1 << opcodes.GetCodeFactorBits());
+  writer.PushUint8(DebugLineOpCodeWriter<Allocator>::kDefaultIsStmt ? 1 : 0);
+  writer.PushInt8(DebugLineOpCodeWriter<Allocator>::kLineBase);
+  writer.PushUint8(DebugLineOpCodeWriter<Allocator>::kLineRange);
+  writer.PushUint8(DebugLineOpCodeWriter<Allocator>::kOpcodeBase);
+  static const int opcode_lengths[DebugLineOpCodeWriter<Allocator>::kOpcodeBase] = {
+      0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 };
+  for (int i = 1; i < DebugLineOpCodeWriter<Allocator>::kOpcodeBase; i++) {
+    writer.PushUint8(opcode_lengths[i]);
+  }
+  for (const std::string& directory : include_directories) {
+    writer.PushData(directory.data(), directory.size() + 1);
+  }
+  writer.PushUint8(0);  // Terminate include_directories list.
+  for (const FileEntry& file : files) {
+    writer.PushData(file.file_name.data(), file.file_name.size() + 1);
+    writer.PushUleb128(file.directory_index);
+    writer.PushUleb128(file.modification_time);
+    writer.PushUleb128(file.file_size);
+  }
+  writer.PushUint8(0);  // Terminate file list.
+  writer.UpdateUint32(header_length_pos, writer.data()->size() - header_length_pos - 4);
+  writer.PushData(opcodes.data()->data(), opcodes.data()->size());
+  writer.UpdateUint32(header_start, writer.data()->size() - header_start - 4);
+}
+
+}  // namespace dwarf
+}  // namespace art
+
+#endif  // ART_COMPILER_DWARF_HEADERS_H_
diff --git a/compiler/dwarf/writer.h b/compiler/dwarf/writer.h
index d8e29f0..3b9c558 100644
--- a/compiler/dwarf/writer.h
+++ b/compiler/dwarf/writer.h
@@ -116,6 +116,11 @@
     data_->insert(data_->end(), p, p + size);
   }
 
+  template<typename Allocator2>
+  void PushData(const std::vector<uint8_t, Allocator2>* buffer) {
+    data_->insert(data_->end(), buffer->begin(), buffer->end());
+  }
+
   void UpdateUint32(size_t offset, uint32_t value) {
     DCHECK_LT(offset + 3, data_->size());
     (*data_)[offset + 0] = (value >> 0) & 0xFF;
@@ -136,6 +141,15 @@
     (*data_)[offset + 7] = (value >> 56) & 0xFF;
   }
 
+  void UpdateUleb128(size_t offset, uint32_t value) {
+    DCHECK_LE(offset + UnsignedLeb128Size(value), data_->size());
+    UpdateUnsignedLeb128(data_->data() + offset, value);
+  }
+
+  void Pop() {
+    return data_->pop_back();
+  }
+
   void Pad(int alignment) {
     DCHECK_NE(alignment, 0);
     data_->resize(RoundUp(data_->size(), alignment), 0);
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 2756af1..f780dea 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -26,8 +26,7 @@
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
 #include "dwarf.h"
-#include "dwarf/debug_frame_writer.h"
-#include "dwarf/debug_line_writer.h"
+#include "dwarf/headers.h"
 #include "elf_builder.h"
 #include "elf_file.h"
 #include "elf_utils.h"
@@ -40,42 +39,6 @@
 
 namespace art {
 
-static void PushByte(std::vector<uint8_t>* buf, int data) {
-  buf->push_back(data & 0xff);
-}
-
-static uint32_t PushStr(std::vector<uint8_t>* buf, const char* str, const char* def = nullptr) {
-  if (str == nullptr) {
-    str = def;
-  }
-
-  uint32_t offset = buf->size();
-  for (size_t i = 0; str[i] != '\0'; ++i) {
-    buf->push_back(str[i]);
-  }
-  buf->push_back('\0');
-  return offset;
-}
-
-static uint32_t PushStr(std::vector<uint8_t>* buf, const std::string &str) {
-  uint32_t offset = buf->size();
-  buf->insert(buf->end(), str.begin(), str.end());
-  buf->push_back('\0');
-  return offset;
-}
-
-static void UpdateWord(std::vector<uint8_t>* buf, int offset, int data) {
-  (*buf)[offset+0] = data;
-  (*buf)[offset+1] = data >> 8;
-  (*buf)[offset+2] = data >> 16;
-  (*buf)[offset+3] = data >> 24;
-}
-
-static void PushHalf(std::vector<uint8_t>* buf, int data) {
-  buf->push_back(data & 0xff);
-  buf->push_back((data >> 8) & 0xff);
-}
-
 template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
           typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
           typename Elf_Phdr, typename Elf_Shdr>
@@ -90,7 +53,8 @@
   return elf_writer.Write(oat_writer, dex_files, android_root, is_host);
 }
 
-void WriteCIE(dwarf::DebugFrameWriter<>* cfi, InstructionSet isa) {
+void WriteCIE(InstructionSet isa, std::vector<uint8_t>* eh_frame) {
+  const bool is64bit = Is64BitInstructionSet(isa);
   // Scratch registers should be marked as undefined.  This tells the
   // debugger that its value in the previous frame is not recoverable.
   switch (isa) {
@@ -115,7 +79,7 @@
         }
       }
       auto return_address_reg = dwarf::Reg::ArmCore(14);  // R14(LR).
-      cfi->WriteCIE(return_address_reg, opcodes);
+      dwarf::WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame);
       return;
     }
     case kArm64: {
@@ -138,7 +102,7 @@
         }
       }
       auto return_address_reg = dwarf::Reg::Arm64Core(30);  // R30(LR).
-      cfi->WriteCIE(return_address_reg, opcodes);
+      dwarf::WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame);
       return;
     }
     case kMips:
@@ -154,7 +118,7 @@
         }
       }
       auto return_address_reg = dwarf::Reg::MipsCore(31);  // R31(RA).
-      cfi->WriteCIE(return_address_reg, opcodes);
+      dwarf::WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame);
       return;
     }
     case kX86: {
@@ -176,7 +140,7 @@
         opcodes.Undefined(dwarf::Reg::X86Fp(reg));
       }
       auto return_address_reg = dwarf::Reg::X86Core(8);  // R8(EIP).
-      cfi->WriteCIE(return_address_reg, opcodes);
+      dwarf::WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame);
       return;
     }
     case kX86_64: {
@@ -202,7 +166,7 @@
         }
       }
       auto return_address_reg = dwarf::Reg::X86_64Core(16);  // R16(RIP).
-      cfi->WriteCIE(return_address_reg, opcodes);
+      dwarf::WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame);
       return;
     }
     case kNone:
@@ -298,123 +262,29 @@
  * @param dbg_str Debug strings.
  */
 static void FillInCFIInformation(OatWriter* oat_writer,
-                                 std::vector<uint8_t>* dbg_info,
-                                 std::vector<uint8_t>* dbg_abbrev,
-                                 std::vector<uint8_t>* dbg_str,
-                                 std::vector<uint8_t>* dbg_line,
+                                 std::vector<uint8_t>* debug_info_data,
+                                 std::vector<uint8_t>* debug_abbrev_data,
+                                 std::vector<uint8_t>* debug_str_data,
+                                 std::vector<uint8_t>* debug_line_data,
                                  uint32_t text_section_offset) {
   const std::vector<OatWriter::DebugInfo>& method_infos = oat_writer->GetCFIMethodInfo();
 
-  uint32_t producer_str_offset = PushStr(dbg_str, "Android dex2oat");
-
-  constexpr bool use_64bit_addresses = false;
-
-  // Create the debug_abbrev section with boilerplate information.
-  // We only care about low_pc and high_pc right now for the compilation
-  // unit and methods.
-
-  // Tag 1: Compilation unit: DW_TAG_compile_unit.
-  PushByte(dbg_abbrev, 1);
-  PushByte(dbg_abbrev, dwarf::DW_TAG_compile_unit);
-
-  // There are children (the methods).
-  PushByte(dbg_abbrev, dwarf::DW_CHILDREN_yes);
-
-  // DW_AT_producer DW_FORM_data1.
-  // REVIEW: we can get rid of dbg_str section if
-  // DW_FORM_string (immediate string) was used everywhere instead of
-  // DW_FORM_strp (ref to string from .debug_str section).
-  // DW_FORM_strp makes sense only if we reuse the strings.
-  PushByte(dbg_abbrev, dwarf::DW_AT_producer);
-  PushByte(dbg_abbrev, dwarf::DW_FORM_strp);
-
-  // DW_LANG_Java DW_FORM_data1.
-  PushByte(dbg_abbrev, dwarf::DW_AT_language);
-  PushByte(dbg_abbrev, dwarf::DW_FORM_data1);
-
-  // DW_AT_low_pc DW_FORM_addr.
-  PushByte(dbg_abbrev, dwarf::DW_AT_low_pc);
-  PushByte(dbg_abbrev, dwarf::DW_FORM_addr);
-
-  // DW_AT_high_pc DW_FORM_addr.
-  PushByte(dbg_abbrev, dwarf::DW_AT_high_pc);
-  PushByte(dbg_abbrev, dwarf::DW_FORM_addr);
-
-  if (dbg_line != nullptr) {
-    // DW_AT_stmt_list DW_FORM_sec_offset.
-    PushByte(dbg_abbrev, dwarf::DW_AT_stmt_list);
-    PushByte(dbg_abbrev, dwarf::DW_FORM_data4);
-  }
-
-  // End of DW_TAG_compile_unit.
-  PushByte(dbg_abbrev, 0);  // DW_AT.
-  PushByte(dbg_abbrev, 0);  // DW_FORM.
-
-  // Tag 2: Compilation unit: DW_TAG_subprogram.
-  PushByte(dbg_abbrev, 2);
-  PushByte(dbg_abbrev, dwarf::DW_TAG_subprogram);
-
-  // There are no children.
-  PushByte(dbg_abbrev, dwarf::DW_CHILDREN_no);
-
-  // Name of the method.
-  PushByte(dbg_abbrev, dwarf::DW_AT_name);
-  PushByte(dbg_abbrev, dwarf::DW_FORM_strp);
-
-  // DW_AT_low_pc DW_FORM_addr.
-  PushByte(dbg_abbrev, dwarf::DW_AT_low_pc);
-  PushByte(dbg_abbrev, dwarf::DW_FORM_addr);
-
-  // DW_AT_high_pc DW_FORM_addr.
-  PushByte(dbg_abbrev, dwarf::DW_AT_high_pc);
-  PushByte(dbg_abbrev, dwarf::DW_FORM_addr);
-
-  // End of DW_TAG_subprogram.
-  PushByte(dbg_abbrev, 0);  // DW_AT.
-  PushByte(dbg_abbrev, 0);  // DW_FORM.
-
-  // End of abbrevs for compilation unit
-  PushByte(dbg_abbrev, 0);
-
-  // Start the debug_info section with the header information
-  // 'unit_length' will be filled in later.
-  int cunit_length = dbg_info->size();
-  Push32(dbg_info, 0);
-
-  // 'version' - 3.
-  PushHalf(dbg_info, 3);
-
-  // Offset into .debug_abbrev section (always 0).
-  Push32(dbg_info, 0);
-
-  // Address size: 4 or 8.
-  PushByte(dbg_info, use_64bit_addresses ? 8 : 4);
-
-  // Start the description for the compilation unit.
-  // This uses tag 1.
-  PushByte(dbg_info, 1);
-
-  // The producer is Android dex2oat.
-  Push32(dbg_info, producer_str_offset);
-
-  // The language is Java.
-  PushByte(dbg_info, dwarf::DW_LANG_Java);
-
-  // low_pc and high_pc.
   uint32_t cunit_low_pc = static_cast<uint32_t>(-1);
   uint32_t cunit_high_pc = 0;
   for (auto method_info : method_infos) {
     cunit_low_pc = std::min(cunit_low_pc, method_info.low_pc_);
     cunit_high_pc = std::max(cunit_high_pc, method_info.high_pc_);
   }
-  Push32(dbg_info, cunit_low_pc + text_section_offset);
-  Push32(dbg_info, cunit_high_pc + text_section_offset);
 
-  if (dbg_line != nullptr) {
-    // Line number table offset.
-    Push32(dbg_info, dbg_line->size());
+  dwarf::DebugInfoEntryWriter<> info(false /* 32 bit */, debug_abbrev_data);
+  info.StartTag(dwarf::DW_TAG_compile_unit, dwarf::DW_CHILDREN_yes);
+  info.WriteStrp(dwarf::DW_AT_producer, "Android dex2oat", debug_str_data);
+  info.WriteData1(dwarf::DW_AT_language, dwarf::DW_LANG_Java);
+  info.WriteAddr(dwarf::DW_AT_low_pc, cunit_low_pc + text_section_offset);
+  info.WriteAddr(dwarf::DW_AT_high_pc, cunit_high_pc + text_section_offset);
+  if (debug_line_data != nullptr) {
+    info.WriteData4(dwarf::DW_AT_stmt_list, debug_line_data->size());
   }
-
   for (auto method_info : method_infos) {
     std::string method_name = PrettyMethod(method_info.dex_method_index_,
                                            *method_info.dex_file_, true);
@@ -423,17 +293,16 @@
       // so that it will show up in a debuggerd crash report.
       method_name += " [ DEDUPED ]";
     }
-
-    // Start a new TAG: subroutine (2).
-    PushByte(dbg_info, 2);
-
-    // Enter name, low_pc, high_pc.
-    Push32(dbg_info, PushStr(dbg_str, method_name));
-    Push32(dbg_info, method_info.low_pc_ + text_section_offset);
-    Push32(dbg_info, method_info.high_pc_ + text_section_offset);
+    info.StartTag(dwarf::DW_TAG_subprogram, dwarf::DW_CHILDREN_no);
+    info.WriteStrp(dwarf::DW_AT_name, method_name.data(), debug_str_data);
+    info.WriteAddr(dwarf::DW_AT_low_pc, method_info.low_pc_ + text_section_offset);
+    info.WriteAddr(dwarf::DW_AT_high_pc, method_info.high_pc_ + text_section_offset);
+    info.EndTag();  // DW_TAG_subprogram
   }
+  info.EndTag();  // DW_TAG_compile_unit
+  dwarf::WriteDebugInfoCU(0 /* debug_abbrev_offset */, info, debug_info_data);
 
-  if (dbg_line != nullptr) {
+  if (debug_line_data != nullptr) {
     // TODO: in gdb info functions <regexp> - reports Java functions, but
     // source file is <unknown> because .debug_line is formed as one
     // compilation unit. To fix this it is possible to generate
@@ -441,7 +310,7 @@
     // Each of the these compilation units can have several non-adjacent
     // method ranges.
 
-    std::vector<dwarf::DebugLineWriter<>::FileEntry> files;
+    std::vector<dwarf::FileEntry> files;
     std::unordered_map<std::string, size_t> files_map;
     std::vector<std::string> directories;
     std::unordered_map<std::string, size_t> directories_map;
@@ -465,7 +334,7 @@
         break;
     }
 
-    dwarf::DebugLineOpCodeWriter<> opcodes(use_64bit_addresses, code_factor_bits_);
+    dwarf::DebugLineOpCodeWriter<> opcodes(false /* 32bit */, code_factor_bits_);
     opcodes.SetAddress(text_section_offset + cunit_low_pc);
     if (isa != -1) {
       opcodes.SetISA(isa);
@@ -529,7 +398,7 @@
         if (it2 == files_map.end()) {
           file_index = 1 + files.size();
           files_map.emplace(full_path, file_index);
-          files.push_back(dwarf::DebugLineWriter<>::FileEntry {
+          files.push_back(dwarf::FileEntry {
             file_name,
             directory_index,
             0,  // Modification time - NA.
@@ -574,19 +443,10 @@
         opcodes.AddRow(low_pc, 0);
       }
     }
-
     opcodes.AdvancePC(text_section_offset + cunit_high_pc);
     opcodes.EndSequence();
-
-    dwarf::DebugLineWriter<> dbg_line_writer(dbg_line);
-    dbg_line_writer.WriteTable(directories, files, opcodes);
+    dwarf::WriteDebugLineTable(directories, files, opcodes, debug_line_data);
   }
-
-  // One byte terminator.
-  PushByte(dbg_info, 0);
-
-  // We have now walked all the methods.  Fill in lengths.
-  UpdateWord(dbg_info, cunit_length, dbg_info->size() - cunit_length - 4);
 }
 
 template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
@@ -599,9 +459,9 @@
                                          Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
                               OatWriter* oat_writer) {
   std::vector<uint8_t> cfi_data;
-  bool is_64bit = Is64BitInstructionSet(compiler_driver->GetInstructionSet());
-  dwarf::DebugFrameWriter<> cfi(&cfi_data, is_64bit);
-  WriteCIE(&cfi, compiler_driver->GetInstructionSet());
+  const int cie_offset = 0;
+  bool is64bit = Is64BitInstructionSet(compiler_driver->GetInstructionSet());
+  WriteCIE(compiler_driver->GetInstructionSet(), &cfi_data);
 
   Elf_Addr text_section_address = builder->GetTextBuilder().GetSection()->sh_addr;
 
@@ -632,12 +492,12 @@
 
     // Include FDE for compiled method, if possible.
     DCHECK(it->compiled_method_ != nullptr);
-    const SwapVector<uint8_t>* unwind_opcodes = it->compiled_method_->GetCFIInfo();
-    if (unwind_opcodes != nullptr) {
+    const SwapVector<uint8_t>* opcodes = it->compiled_method_->GetCFIInfo();
+    if (opcodes != nullptr) {
       // TUNING: The headers take a lot of space. Can we have 1 FDE per file?
       // TUNING: Some tools support compressed DWARF sections (.zdebug_*).
-      cfi.WriteFDE(text_section_address + it->low_pc_, it->high_pc_ - it->low_pc_,
-                   unwind_opcodes->data(), unwind_opcodes->size());
+      dwarf::WriteEhFrameFDE(is64bit, cie_offset, text_section_address + it->low_pc_,
+                             it->high_pc_ - it->low_pc_, opcodes, &cfi_data);
     }
   }