Merge pull request #235 from haberman/dwarf5

DWARF 5 support
diff --git a/src/bloaty.h b/src/bloaty.h
index 9ee2640..e5a1128 100644
--- a/src/bloaty.h
+++ b/src/bloaty.h
@@ -293,16 +293,19 @@
 namespace dwarf {
 
 struct File {
-  absl::string_view debug_info;
-  absl::string_view debug_types;
-  absl::string_view debug_str;
   absl::string_view debug_abbrev;
+  absl::string_view debug_addr;
   absl::string_view debug_aranges;
+  absl::string_view debug_info;
   absl::string_view debug_line;
   absl::string_view debug_loc;
   absl::string_view debug_pubnames;
   absl::string_view debug_pubtypes;
   absl::string_view debug_ranges;
+  absl::string_view debug_rnglists;
+  absl::string_view debug_str;
+  absl::string_view debug_str_offsets;
+  absl::string_view debug_types;
 
   absl::string_view* GetFieldByName(absl::string_view name);
   void SetFieldByName(absl::string_view name, absl::string_view contents) {
diff --git a/src/dwarf.cc b/src/dwarf.cc
index 244876b..dad4317 100644
--- a/src/dwarf.cc
+++ b/src/dwarf.cc
@@ -134,6 +134,10 @@
   // DWARF version of this unit.
   uint8_t dwarf_version() const { return dwarf_version_; }
 
+  uint64_t addr_base() const { return addr_base_; }
+  uint64_t str_offsets_base() const { return str_offsets_base_; }
+  uint64_t range_lists_base() const { return range_lists_base_; }
+
   void SetAddressSize(uint8_t address_size) {
     if (address_size != 4 && address_size != 8) {
       THROWF("Unexpected address size: $0", address_size);
@@ -141,6 +145,18 @@
     address_size_ = address_size;
   }
 
+  void SetAddrBase(uint64_t addr_base) {
+    addr_base_ = addr_base;
+  }
+
+  void SetStrOffsetsBase(uint64_t str_offsets_base) {
+    str_offsets_base_ = str_offsets_base;
+  }
+
+  void SetRangeListsBase(uint64_t range_lists_base) {
+    range_lists_base_ = range_lists_base;
+  }
+
   // To allow this as the key in a map.
   bool operator<(const CompilationUnitSizes& rhs) const {
     return std::tie(dwarf64_, address_size_) <
@@ -202,6 +218,9 @@
   uint16_t dwarf_version_;
   bool dwarf64_;
   uint8_t address_size_;
+  uint64_t addr_base_ = 0;
+  uint64_t str_offsets_base_ = 0;
+  uint64_t range_lists_base_ = 0;
 };
 
 
@@ -285,7 +304,8 @@
         abbrev.has_child = false;
         break;
       default:
-        THROW("DWARF has_child is neither true nor false.");
+        THROWF("DWARF has_child is neither true nor false: $0, code=$1, tag=$2",
+               has_child, abbrev.code, abbrev.tag);
     }
 
     while (true) {
@@ -447,44 +467,6 @@
   return available.substr(0, list.read_offset() - available.data());
 }
 
-
-// RangeList ///////////////////////////////////////////////////////////////////
-
-// Code for reading entries out of a range list.
-// For the moment we only care about finding the bounds of a list given its
-// offset, so we don't actually vend any of the data.
-
-class RangeList {
- public:
-  RangeList(CompilationUnitSizes sizes, string_view data)
-      : sizes_(sizes), remaining_(data) {}
-
-  const char* read_offset() const { return remaining_.data(); }
-  bool NextEntry();
-
- private:
-  CompilationUnitSizes sizes_;
-  string_view remaining_;
-};
-
-bool RangeList::NextEntry() {
-  uint64_t start, end;
-  start = sizes_.ReadAddress(&remaining_);
-  end = sizes_.ReadAddress(&remaining_);
-  if (start == 0 && end == 0) {
-    return false;
-  }
-  return true;
-}
-
-string_view GetRangeListRange(CompilationUnitSizes sizes,
-                              string_view available) {
-  RangeList list(sizes, available);
-  while (list.NextEntry()) {
-  }
-  return available.substr(0, list.read_offset() - available.data());
-}
-
 // DIEReader ///////////////////////////////////////////////////////////////////
 
 // Reads a sequence of DWARF DIE's (Debugging Information Entries) from the
@@ -546,10 +528,13 @@
   // Requires that ReadCode() has been called at least once.
   bool HasChild() const { return GetAbbrev().has_child; }
 
+  template <class T>
+  void ReadAttributes(T&& func);
+
   const File& dwarf() const { return dwarf_; }
 
   string_view unit_range() const { return unit_range_; }
-  CompilationUnitSizes unit_sizes() const { return unit_sizes_; }
+  const CompilationUnitSizes& unit_sizes() const { return unit_sizes_; }
   uint32_t abbrev_version() const { return abbrev_version_; }
   uint64_t debug_abbrev_offset() const { return debug_abbrev_offset_; }
 
@@ -568,30 +553,9 @@
     }
   }
 
+  CompilationUnitSizes* mutable_unit_sizes() { return &unit_sizes_; }
+
  private:
-  template<typename> friend class AttrReader;
-
-  // APIs for our friends to use to update our state.
-
-  // Call to get the current read head where attributes should be parsed.
-  string_view ReadAttributesBegin() {
-    assert(state_ == State::kReadyToReadAttributes);
-    return remaining_;
-  }
-
-  // When some data has been parsed, this updates our read head.
-  bool ReadAttributesEnd(string_view remaining, uint64_t sibling) {
-    assert(state_ == State::kReadyToReadAttributes);
-    if (remaining.data() == nullptr) {
-      THROW("premature EOF reading DWARF attributes");
-    } else {
-      remaining_ = remaining;
-      sibling_offset_ = sibling;
-      state_ = State::kReadyToNext;
-      return true;
-    }
-  }
-
   // Internal APIs.
 
   bool ReadCompilationUnitHeader();
@@ -649,6 +613,296 @@
   uint64_t unit_type_offset_;
 };
 
+class AttrValue {
+ public:
+  static AttrValue ParseAttr(const DIEReader &reader, uint8_t form,
+                             string_view *data);
+
+  AttrValue(const AttrValue &) = default;
+  AttrValue &operator=(const AttrValue &) = default;
+
+  bool IsUint() const {
+    return type_ == Type::kUint || type_ == Type::kUnresolvedUint;
+  }
+
+  bool IsString() const {
+    return type_ == Type::kString || type_ == Type::kUnresolvedString;
+  }
+
+  absl::optional<uint64_t> ToUint(const DIEReader& reader) const {
+    if (IsUint()) return GetUint(reader);
+    string_view str = GetString(reader);
+    switch (str.size()) {
+      case 1:
+        return ReadFixed<uint8_t>(&str);
+      case 2:
+        return ReadFixed<uint8_t>(&str);
+      case 4:
+        return ReadFixed<uint32_t>(&str);
+      case 8:
+        return ReadFixed<uint64_t>(&str);
+    }
+    return absl::nullopt;
+  }
+
+  uint64_t GetUint(const DIEReader& reader) const {
+    if (type_ == Type::kUnresolvedUint) {
+      return ResolveIndirectAddress(reader);
+    } else {
+      assert(type_ == Type::kUint);
+      return uint_;
+    }
+  }
+
+  string_view GetString(const DIEReader& reader) const {
+    if (type_ == Type::kUnresolvedString) {
+      return ResolveDoubleIndirectString(reader);
+    } else {
+      assert(type_ == Type::kString);
+      return string_;
+    }
+  }
+
+  void SetForm(uint16_t form) {
+    form_ = form;
+  }
+
+  uint16_t form() const { return form_; }
+
+  std::string DebugString() const {
+    if (type_ == Type::kUint) {
+      return absl::Substitute("AttrValue{type_=Type::kUint, form_=$0, uint_=$1}", form_, uint_);
+    } else {
+      return absl::Substitute("AttrValue{type_=Type::kString, form_=$0, string_=$1}", form_, string_);
+    }
+  }
+
+ private:
+  explicit AttrValue(uint64_t val) : uint_(val), type_(Type::kUint) {}
+  explicit AttrValue(string_view val) : string_(val), type_(Type::kString) {}
+  
+  // Some attribute values remain unresolved after being parsed.
+  // We have to delay the resolution of some indirect values because they are
+  // dependent on bases that come after it in the sequence of attributes, eg.
+  //
+  // $ dwarfdump -i bloaty
+  //   COMPILE_UNIT<header overall offset = 0x00000000>:
+  // < 0><0x0000000c>  DW_TAG_compile_unit
+  //                     DW_AT_producer              (indexed string: 0x00000000)Debian clang version 11.0.1-2
+  //                     DW_AT_language              DW_LANG_C_plus_plus_14
+  //                     DW_AT_name                  (indexed string: 0x00000001)../src/main.cc
+  //                     DW_AT_str_offsets_base      0x00000008
+  //
+  // Note that DW_AT_name comes before DW_AT_str_offset_base, but the latter
+  // value is required to resolve the name attribute.
+  enum class Type {
+    kUint,
+    kString,
+    kUnresolvedUint,
+    kUnresolvedString
+  };
+
+  Type type() const { return type_; }
+
+  static AttrValue UnresolvedUint(uint64_t val) {
+     AttrValue ret(val);
+     ret.type_ = Type::kUnresolvedUint;
+     return ret;
+  }
+
+  static AttrValue UnresolvedString(uint64_t val) {
+     AttrValue ret(val);
+     ret.type_ = Type::kUnresolvedString;
+     return ret;
+  }
+
+  union {
+    uint64_t uint_;
+    string_view string_;
+  };
+
+  Type type_;
+  uint16_t form_;
+
+  template <class D>
+  static string_view ReadBlock(string_view* data);
+  static string_view ReadVariableBlock(string_view* data);
+  template <class D>
+  static string_view ReadIndirectString(const DIEReader &reader,
+                                        string_view *data);
+  static string_view ResolveIndirectString(const DIEReader &reader,
+                                           uint64_t ofs);
+
+  string_view ResolveDoubleIndirectString(const DIEReader &reader) const;
+  uint64_t ResolveIndirectAddress(const DIEReader& reader) const;
+};
+
+uint64_t ReadIndirectAddress(const DIEReader& reader, uint64_t val) {
+  string_view addrs = reader.dwarf().debug_addr;
+  const dwarf::CompilationUnitSizes& sizes = reader.unit_sizes();
+  switch (sizes.address_size()) {
+    case 4:
+      SkipBytes((val * 4) + sizes.addr_base(), &addrs);
+      return ReadFixed<uint32_t>(&addrs);
+    case 8:
+      SkipBytes((val * 8) + sizes.addr_base(), &addrs);
+      return ReadFixed<uint64_t>(&addrs);
+    default:
+      BLOATY_UNREACHABLE();
+  }
+}
+
+template <class D>
+string_view AttrValue::ReadBlock(string_view* data) {
+  D len = ReadFixed<D>(data);
+  return ReadBytes(len, data);
+}
+
+string_view AttrValue::ReadVariableBlock(string_view* data) {
+  uint64_t len = ReadLEB128<uint64_t>(data);
+  return ReadBytes(len, data);
+}
+
+string_view AttrValue::ResolveIndirectString(const DIEReader &reader,
+                                             uint64_t ofs) {
+  StringTable table(reader.dwarf().debug_str);
+  string_view ret = table.ReadEntry(ofs);
+  reader.AddIndirectString(ret);
+  return ret;
+}
+
+template <class D>
+string_view AttrValue::ReadIndirectString(const DIEReader &reader,
+                                          string_view *data) {
+  return ResolveIndirectString(reader, ReadFixed<D>(data));
+}
+
+string_view
+AttrValue::ResolveDoubleIndirectString(const DIEReader &reader) const {
+  uint64_t ofs = uint_;
+  string_view offsets = reader.dwarf().debug_str_offsets;
+  uint64_t ofs2;
+  if (reader.unit_sizes().dwarf64()) {
+    SkipBytes((ofs * 8) + reader.unit_sizes().str_offsets_base(), &offsets);
+    ofs2 = ReadFixed<uint64_t>(&offsets);
+  } else {
+    SkipBytes((ofs * 4) + reader.unit_sizes().str_offsets_base(), &offsets);
+    ofs2 = ReadFixed<uint32_t>(&offsets);
+  }
+  StringTable table(reader.dwarf().debug_str);
+  string_view ret = table.ReadEntry(ofs2);
+  reader.AddIndirectString(ret);
+  return ret;
+}
+
+uint64_t AttrValue::ResolveIndirectAddress(const DIEReader& reader) const {
+  return ReadIndirectAddress(reader, uint_);
+}
+
+AttrValue AttrValue::ParseAttr(const DIEReader &reader, uint8_t form,
+                               string_view *data) {
+  switch (form) {
+    case DW_FORM_indirect: {
+      uint16_t indirect_form = ReadLEB128<uint16_t>(data);
+      if (indirect_form == DW_FORM_indirect) {
+        THROW("indirect attribute has indirect form type");
+      }
+      return ParseAttr(reader, indirect_form, data);
+    }
+    case DW_FORM_ref1:
+      return AttrValue(ReadFixed<uint8_t>(data));
+    case DW_FORM_ref2:
+      return AttrValue(ReadFixed<uint16_t>(data));
+    case DW_FORM_ref4:
+      return AttrValue(ReadFixed<uint32_t>(data));
+    case DW_FORM_ref_sig8:
+    case DW_FORM_ref8:
+      return AttrValue(ReadFixed<uint64_t>(data));
+    case DW_FORM_ref_udata:
+    case DW_FORM_strx1:
+      return AttrValue::UnresolvedString(ReadFixed<uint8_t>(data));
+    case DW_FORM_strx2:
+      return AttrValue::UnresolvedString(ReadFixed<uint16_t>(data));
+    case DW_FORM_strx4:
+      return AttrValue::UnresolvedString(ReadFixed<uint32_t>(data));
+    case DW_FORM_strx:
+      return AttrValue::UnresolvedString(ReadLEB128<uint64_t>(data));
+    case DW_FORM_addrx1:
+      return AttrValue::UnresolvedUint(ReadFixed<uint8_t>(data));
+    case DW_FORM_addrx2:
+      return AttrValue::UnresolvedUint(ReadFixed<uint16_t>(data));
+    case DW_FORM_addrx3:
+      return AttrValue::UnresolvedUint(ReadFixed<uint32_t, 3>(data));
+    case DW_FORM_addrx4:
+      return AttrValue::UnresolvedUint(ReadFixed<uint32_t>(data));
+    case DW_FORM_addrx:
+      return AttrValue::UnresolvedUint(ReadLEB128<uint64_t>(data));
+    case DW_FORM_addr:
+    address_size:
+      switch (reader.unit_sizes().address_size()) {
+        case 4:
+          return AttrValue(ReadFixed<uint32_t>(data));
+        case 8:
+          return AttrValue(ReadFixed<uint64_t>(data));
+        default:
+          BLOATY_UNREACHABLE();
+      }
+    case DW_FORM_ref_addr:
+      if (reader.unit_sizes().dwarf_version() <= 2) {
+        goto address_size;
+      }
+      ABSL_FALLTHROUGH_INTENDED;
+    case DW_FORM_sec_offset:
+      if (reader.unit_sizes().dwarf64()) {
+        return AttrValue(ReadFixed<uint64_t>(data));
+      } else {
+        return AttrValue(ReadFixed<uint32_t>(data));
+      }
+    case DW_FORM_udata:
+      return AttrValue(ReadLEB128<uint64_t>(data));
+    case DW_FORM_block1:
+      return AttrValue(ReadBlock<uint8_t>(data));
+    case DW_FORM_block2:
+      return AttrValue(ReadBlock<uint16_t>(data));
+    case DW_FORM_block4:
+      return AttrValue(ReadBlock<uint32_t>(data));
+    case DW_FORM_block:
+    case DW_FORM_exprloc:
+      return AttrValue(ReadVariableBlock(data));
+    case DW_FORM_string:
+      return AttrValue(ReadNullTerminated(data));
+    case DW_FORM_strp:
+      if (reader.unit_sizes().dwarf64()) {
+        return AttrValue(ReadIndirectString<uint64_t>(reader, data));
+      } else {
+        return AttrValue(ReadIndirectString<uint32_t>(reader, data));
+      }
+    case DW_FORM_data1:
+      return AttrValue(ReadBytes(1, data));
+    case DW_FORM_data2:
+      return AttrValue(ReadBytes(2, data));
+    case DW_FORM_data4:
+      return AttrValue(ReadBytes(4, data));
+    case DW_FORM_data8:
+      return AttrValue(ReadBytes(8, data));
+    case DW_FORM_rnglistx: {
+      auto val = AttrValue(ReadLEB128<uint64_t>(data));
+      return val;
+    }
+
+    // Bloaty doesn't currently care about any bool or signed data.
+    // So we fudge it a bit and just stuff these in a uint64.
+    case DW_FORM_flag_present:
+      return AttrValue(1);
+    case DW_FORM_flag:
+      return AttrValue(ReadFixed<uint8_t>(data));
+    case DW_FORM_sdata:
+      return AttrValue(ReadLEB128<uint64_t>(data));
+    default:
+      THROWF("Don't know how to parse DWARF form: $0", form);
+  }
+}
+
 void DIEReader::SkipNullEntries() {
   while (!remaining_.empty() && remaining_[0] == 0) {
     // null entry terminates a chain of sibling entries.
@@ -717,12 +971,26 @@
 
   unit_sizes_.ReadDWARFVersion(&remaining_);
 
-  if (unit_sizes_.dwarf_version() > 4) {
+  if (unit_sizes_.dwarf_version() > 5) {
     THROWF("Data for $0 is in DWARF $1 format which we don't understand",
         unit_name_, unit_sizes_.dwarf_version());
   }
 
-  debug_abbrev_offset_ = unit_sizes_.ReadDWARFOffset(&remaining_);
+  if (unit_sizes_.dwarf_version() == 5) {
+    uint8_t unit_type = ReadFixed<uint8_t>(&remaining_);
+    (void)unit_type;  // We don't use this currently.
+    unit_sizes_.SetAddressSize(ReadFixed<uint8_t>(&remaining_));
+    debug_abbrev_offset_ = unit_sizes_.ReadDWARFOffset(&remaining_);
+  } else {
+    debug_abbrev_offset_ = unit_sizes_.ReadDWARFOffset(&remaining_);
+    unit_sizes_.SetAddressSize(ReadFixed<uint8_t>(&remaining_));
+
+    if (section_ == Section::kDebugTypes) {
+      unit_type_signature_ = ReadFixed<uint64_t>(&remaining_);
+      unit_type_offset_ = unit_sizes_.ReadDWARFOffset(&remaining_);
+    }
+  }
+
   unit_abbrev_ = &abbrev_tables_[debug_abbrev_offset_];
 
   // If we haven't already read abbreviations for this debug_abbrev_offset_, we
@@ -733,13 +1001,6 @@
     unit_abbrev_->ReadAbbrevs(abbrev_data);
   }
 
-  unit_sizes_.SetAddressSize(ReadFixed<uint8_t>(&remaining_));
-
-  if (section_ == Section::kDebugTypes) {
-    unit_type_signature_ = ReadFixed<uint64_t>(&remaining_);
-    unit_type_offset_ = unit_sizes_.ReadDWARFOffset(&remaining_);
-  }
-
   auto abbrev_id = std::make_pair(unit_abbrev_, unit_sizes_);
   auto insert_pair = abbrev_versions_.insert(
       std::make_pair(abbrev_id, abbrev_versions_.size()));
@@ -751,195 +1012,6 @@
   return ReadCode();
 }
 
-
-// DWARF form parsing //////////////////////////////////////////////////////////
-
-class AttrValue {
- public:
-  AttrValue(uint64_t val) : uint_(val), type_(Type::kUint) {}
-  AttrValue(string_view val) : string_(val), type_(Type::kString) {}
-
-  enum class Type {
-    kUint,
-    kString
-  };
-
-  Type type() const { return type_; }
-  bool IsUint() const { return type_ == Type::kUint; }
-  bool IsString() const { return type_ == Type::kString; }
-
-  absl::optional<uint64_t> ToUint() const {
-    if (IsUint()) return uint_;
-    string_view str = string_;
-    switch (str.size()) {
-      case 1:
-        return ReadFixed<uint8_t>(&str);
-      case 2:
-        return ReadFixed<uint8_t>(&str);
-      case 4:
-        return ReadFixed<uint32_t>(&str);
-      case 8:
-        return ReadFixed<uint64_t>(&str);
-    }
-    return absl::nullopt;
-  }
-
-  uint64_t GetUint() const {
-    assert(type_ == Type::kUint);
-    return uint_;
-  }
-
-  string_view GetString() const {
-    assert(type_ == Type::kString);
-    return string_;
-  }
-
- private:
-  union {
-    uint64_t uint_;
-    string_view string_;
-  };
-
-  Type type_;
-};
-
-template <class D>
-string_view ReadBlock(string_view* data) {
-  D len = ReadFixed<D>(data);
-  return ReadBytes(len, data);
-}
-
-string_view ReadVariableBlock(string_view* data) {
-  uint64_t len = ReadLEB128<uint64_t>(data);
-  return ReadBytes(len, data);
-}
-
-template <class D>
-string_view ReadIndirectString(const DIEReader& reader, string_view* data) {
-  D ofs = ReadFixed<D>(data);
-  StringTable table(reader.dwarf().debug_str);
-  string_view ret = table.ReadEntry(ofs);
-  reader.AddIndirectString(ret);
-  return ret;
-}
-
-AttrValue ParseAttr(const DIEReader& reader, uint8_t form, string_view* data) {
-  switch (form) {
-    case DW_FORM_indirect: {
-      uint16_t indirect_form = ReadLEB128<uint16_t>(data);
-      if (indirect_form == DW_FORM_indirect) {
-        THROW("indirect attribute has indirect form type");
-      }
-      return ParseAttr(reader, indirect_form, data);
-    }
-    case DW_FORM_ref1:
-      return AttrValue(ReadFixed<uint8_t>(data));
-    case DW_FORM_ref2:
-      return AttrValue(ReadFixed<uint16_t>(data));
-    case DW_FORM_ref4:
-      return AttrValue(ReadFixed<uint32_t>(data));
-    case DW_FORM_ref_sig8:
-    case DW_FORM_ref8:
-      return AttrValue(ReadFixed<uint64_t>(data));
-    case DW_FORM_ref_udata:
-      return AttrValue(ReadLEB128<uint64_t>(data));
-    case DW_FORM_addr:
-    address_size:
-      switch (reader.unit_sizes().address_size()) {
-        case 4:
-          return AttrValue(ReadFixed<uint32_t>(data));
-        case 8:
-          return AttrValue(ReadFixed<uint64_t>(data));
-        default:
-          BLOATY_UNREACHABLE();
-      }
-    case DW_FORM_ref_addr:
-      if (reader.unit_sizes().dwarf_version() <= 2) {
-        goto address_size;
-      }
-      ABSL_FALLTHROUGH_INTENDED;
-    case DW_FORM_sec_offset:
-      if (reader.unit_sizes().dwarf64()) {
-        return AttrValue(ReadFixed<uint64_t>(data));
-      } else {
-        return AttrValue(ReadFixed<uint32_t>(data));
-      }
-    case DW_FORM_udata:
-      return AttrValue(ReadLEB128<uint64_t>(data));
-    case DW_FORM_block1:
-      return AttrValue(ReadBlock<uint8_t>(data));
-    case DW_FORM_block2:
-      return AttrValue(ReadBlock<uint16_t>(data));
-    case DW_FORM_block4:
-      return AttrValue(ReadBlock<uint32_t>(data));
-    case DW_FORM_block:
-    case DW_FORM_exprloc:
-      return AttrValue(ReadVariableBlock(data));
-    case DW_FORM_string:
-      return AttrValue(ReadNullTerminated(data));
-    case DW_FORM_strp:
-      if (reader.unit_sizes().dwarf64()) {
-        return AttrValue(ReadIndirectString<uint64_t>(reader, data));
-      } else {
-        return AttrValue(ReadIndirectString<uint32_t>(reader, data));
-      }
-    case DW_FORM_data1:
-      return AttrValue(ReadBytes(1, data));
-    case DW_FORM_data2:
-      return AttrValue(ReadBytes(2, data));
-    case DW_FORM_data4:
-      return AttrValue(ReadBytes(4, data));
-    case DW_FORM_data8:
-      return AttrValue(ReadBytes(8, data));
-
-    // Bloaty doesn't currently care about any bool or signed data.
-    // So we fudge it a bit and just stuff these in a uint64.
-    case DW_FORM_flag_present:
-      return AttrValue(1);
-    case DW_FORM_flag:
-      return AttrValue(ReadFixed<uint8_t>(data));
-    case DW_FORM_sdata:
-      return AttrValue(ReadLEB128<uint64_t>(data));
-    default:
-      THROWF("Don't know how to parse DWARF form: $0", form);
-  }
-}
-
-
-// AttrReader //////////////////////////////////////////////////////////////////
-
-// Parses a DIE's attributes, calling user callbacks with the parsed values.
-
-template <class T>
-class AttrReader {
- public:
-  typedef void CallbackFunc(T* container, AttrValue val);
-
-  void OnAttribute(DwarfAttribute attr, CallbackFunc* func) {
-    attributes_[attr] = func;
-  }
-
-  // Reads all attributes for this DIE, storing the ones we were expecting.
-  void ReadAttributes(DIEReader* reader, T* container) {
-    string_view data = reader->ReadAttributesBegin();
-    const AbbrevTable::Abbrev& abbrev = reader->GetAbbrev();
-
-    for (auto attr : abbrev.attr) {
-      AttrValue value = ParseAttr(*reader, attr.form, &data);
-      auto it = attributes_.find(attr.name);
-      if (it != attributes_.end()) {
-        it->second(container, value);
-      }
-    }
-
-    reader->ReadAttributesEnd(data, 0);
-  }
-
- private:
-  std::unordered_map<int, CallbackFunc*> attributes_;
-};
-
-// From DIEReader, defined here because it depends on FixedAttrReader.
 bool DIEReader::SkipChildren() {
   assert(state_ == State::kReadyToNext);
   if (!HasChild()) {
@@ -947,7 +1019,6 @@
   }
 
   int target_depth = depth_ - 1;
-  dwarf::AttrReader<void> attr_reader;
   SkipNullEntries();
   while (depth_ > target_depth) {
     // TODO(haberman): use DW_AT_sibling to optimize skipping when it is
@@ -955,12 +1026,49 @@
     if (!NextDIE()) {
       return false;
     }
-    attr_reader.ReadAttributes(this, nullptr);
+    ReadAttributes([](uint16_t, dwarf::AttrValue) {});
     SkipNullEntries();
   }
   return true;
 }
 
+// Reads all attributes for this DIE, storing the ones we were expecting.
+template <class T>
+void DIEReader::ReadAttributes(T&& func) {
+  assert(state_ == State::kReadyToReadAttributes);
+
+  for (auto attr : GetAbbrev().attr) {
+    AttrValue value = AttrValue::ParseAttr(*this, attr.form, &remaining_);
+    value.SetForm(attr.form);
+    func(attr.name, value);
+  }
+
+  if (remaining_.data() == nullptr) {
+    THROW("premature EOF reading DWARF attributes");
+  } else {
+    sibling_offset_ = 0;
+    state_ = State::kReadyToNext;
+  }
+}
+
+// RangeList ///////////////////////////////////////////////////////////////////
+
+void ReadRangeList(const DIEReader& die_reader, uint64_t low_pc,
+                   string_view name, RangeSink* sink, string_view* data) {
+  std::string name_str(name);
+  while (true) {
+    uint64_t start, end;
+    start = die_reader.unit_sizes().ReadAddress(data);
+    end = die_reader.unit_sizes().ReadAddress(data);
+    if (start == 0 && end == 0) {
+      return;
+    }
+    uint64_t size = end - start;
+    sink->AddVMRangeIgnoreDuplicate("dwarf_rangelist", low_pc + start, size,
+                                    name_str);
+  }
+}
+
 // LineInfoReader //////////////////////////////////////////////////////////////
 
 // Code to read the .line_info programs in a DWARF file.
@@ -1293,8 +1401,12 @@
 string_view* File::GetFieldByName(string_view name) {
   if (name == "aranges") {
     return &debug_aranges;
+  } else if (name == "addr") {
+    return &debug_addr;
   } else if (name == "str") {
     return &debug_str;
+  } else if (name == "str_offsets") {
+    return &debug_str_offsets;
   } else if (name == "info") {
     return &debug_info;
   } else if (name == "types") {
@@ -1311,6 +1423,8 @@
     return &debug_pubtypes;
   } else if (name == "ranges") {
     return &debug_ranges;
+  } else if (name == "rnglists") {
+    return &debug_rnglists;
   } else {
     return nullptr;
   }
@@ -1332,13 +1446,7 @@
    public:
     FilenameMap(const dwarf::File& file)
         : die_reader_(file),
-          missing_("[DWARF is missing filename]") {
-      attr_reader_.OnAttribute(
-          DW_AT_name, [](string_view* s, dwarf::AttrValue data) {
-            if (!data.IsString()) return;
-            *s = data.GetString();
-          });
-    }
+          missing_("[DWARF is missing filename]") {}
 
     std::string GetFilename(uint64_t compilation_unit_offset) {
       auto& name = map_[compilation_unit_offset];
@@ -1349,13 +1457,32 @@
     }
 
    private:
+    bool ReadName(string_view* name, uint64_t offset) {
+      auto sec = dwarf::DIEReader::Section::kDebugInfo;
+      if (!die_reader_.SeekToCompilationUnit(sec, offset) ||
+          die_reader_.GetTag() != DW_TAG_compile_unit) {
+          return false;
+      }
+
+      absl::optional<dwarf::AttrValue> attr;
+
+      die_reader_.ReadAttributes([&attr](uint16_t tag, dwarf::AttrValue data) {
+        if (tag == DW_AT_name && data.IsString()) {
+          attr = data;
+        }
+      });
+      
+      if (attr && attr->IsString()) {
+        *name = attr->GetString(die_reader_);
+        return true;
+      } else {
+        return false;
+      }
+    }
+
     std::string LookupFilename(uint64_t compilation_unit_offset) {
-      auto section = dwarf::DIEReader::Section::kDebugInfo;
       string_view name;
-      if (die_reader_.SeekToCompilationUnit(section, compilation_unit_offset) &&
-          die_reader_.GetTag() == DW_TAG_compile_unit &&
-          (attr_reader_.ReadAttributes(&die_reader_, &name),
-           !name.empty())) {
+      if (ReadName(&name, compilation_unit_offset)) {
         return std::string(name);
       } else {
         return missing_;
@@ -1363,7 +1490,6 @@
     }
 
     dwarf::DIEReader die_reader_;
-    dwarf::AttrReader<string_view> attr_reader_;
     std::unordered_map<uint64_t, std::string> map_;
     std::string missing_;
   } map(file);
@@ -1384,121 +1510,46 @@
   return true;
 }
 
-// TODO(haberman): make these into real protobufs once proto supports
-// string_view.
-class GeneralDIE {
- public:
-  bool has_name() const { return has_name_; }
-  bool has_linkage_name() const { return has_linkage_name_; }
-  bool has_location_string() const { return has_location_string_; }
-  bool has_low_pc() const { return has_low_pc_; }
-  bool has_high_pc() const { return has_high_pc_; }
-  bool has_location_uint64() const { return has_location_uint64_; }
-  bool has_stmt_list() const { return has_stmt_list_; }
-  bool has_ranges() const { return has_ranges_; }
-  bool has_start_scope() const { return has_start_scope_; }
-
-  std::string DebugString() {
-    std::string ret;
-    if (has_name()) {
-      ret += absl::Substitute("name: $0\n", name());
-    }
-    if (has_linkage_name()) {
-      ret += absl::Substitute("linkage_name: $0\n", linkage_name());
-    }
-    if (has_location_string()) {
-      ret += absl::Substitute("location_string: $0\n", location_string());
-    }
-    if (has_low_pc()) {
-      ret += absl::Substitute("low_pc: $0\n", low_pc());
-    }
-    if (has_high_pc()) {
-      ret += absl::Substitute("high_pc: $0\n", high_pc());
-    }
-    if (has_location_uint64()) {
-      ret += absl::Substitute("location_uint64: $0\n", location_uint64());
-    }
-    if (has_stmt_list()) {
-      ret += absl::Substitute("stmt_list: $0\n", stmt_list());
-    }
-    if (has_ranges()) {
-      ret += absl::Substitute("ranges: $0\n", ranges());
-    }
-    if (has_start_scope()) {
-      ret += absl::Substitute("start_scope: $0\n", start_scope());
-    }
-    return ret;
-  }
-
-  string_view name() const { return name_; }
-  string_view linkage_name() const { return linkage_name_; }
-  string_view location_string() const { return location_string_; }
-  uint64_t low_pc() const { return low_pc_; }
-  uint64_t high_pc() const { return high_pc_; }
-  uint64_t location_uint64() const { return location_uint64_; }
-  uint64_t stmt_list() const { return stmt_list_; }
-  uint64_t ranges() const { return ranges_; }
-  uint64_t start_scope() const { return start_scope_; }
-
-  void set_name(string_view val) {
-    has_name_ = true;
-    name_ = val;
-  }
-  void set_linkage_name(string_view val) {
-    has_linkage_name_ = true;
-    location_string_ = val;
-  }
-  void set_location_string(string_view val) {
-    has_location_string_ = true;
-    location_string_ = val;
-  }
-  void set_low_pc(uint64_t val) {
-    has_low_pc_ = true;
-    low_pc_ = val;
-  }
-  void set_high_pc(uint64_t val) {
-    has_high_pc_ = true;
-    high_pc_ = val;
-  }
-  void set_location_uint64(uint64_t val) {
-    has_location_uint64_ = true;
-    location_uint64_ = val;
-  }
-  void set_stmt_list(uint64_t val) {
-    has_stmt_list_ = true;
-    stmt_list_ = val;
-  }
-  void set_ranges(uint64_t val) {
-    has_ranges_ = true;
-    ranges_ = val;
-  }
-  void set_start_scope(uint64_t val) {
-    has_start_scope_ = true;
-    start_scope_ = val;
-  }
-
- private:
-  bool has_name_ = false;
-  bool has_linkage_name_ = false;
-  bool has_location_string_ = false;
-  bool has_low_pc_ = false;
-  bool has_high_pc_ = false;
-  bool has_location_uint64_ = false;
-  bool has_stmt_list_ = false;
-  bool has_ranges_ = false;
-  bool has_start_scope_ = false;
-
-  string_view name_;
-  string_view linkage_name_;
-  string_view location_string_;
-  uint64_t low_pc_ = 0;
-  uint64_t high_pc_ = 0;
-  uint64_t location_uint64_ = 0;
-  uint64_t stmt_list_ = 0;
-  uint64_t ranges_ = 0;
-  uint64_t start_scope_ = 0;
+struct GeneralDIE {
+  absl::optional<dwarf::AttrValue> name;
+  absl::optional<dwarf::AttrValue> linkage_name;
+  absl::optional<dwarf::AttrValue> location;
+  absl::optional<dwarf::AttrValue> low_pc;
+  absl::optional<dwarf::AttrValue> high_pc;
+  absl::optional<dwarf::AttrValue> stmt_list;
+  absl::optional<dwarf::AttrValue> ranges;
+  absl::optional<dwarf::AttrValue> start_scope;
 };
 
+void ReadGeneralDIEAttr(uint16_t tag, dwarf::AttrValue val, GeneralDIE *die) {
+  switch (tag) {
+    case DW_AT_name:
+      die->name = val;
+      break;
+    case DW_AT_linkage_name:
+      die->linkage_name = val;
+      break;
+    case DW_AT_location:
+      die->location = val;
+      break;
+    case DW_AT_low_pc:
+      die->low_pc = val;
+      break;
+    case DW_AT_high_pc:
+      die->high_pc = val;
+      break;
+    case DW_AT_stmt_list:
+      die->stmt_list = val;
+      break;
+    case DW_AT_ranges:
+      die->ranges = val;
+      break;
+    case DW_AT_start_scope:
+      die->start_scope = val;
+      break;
+  }
+}
+
 class InlinesDIE {
  public:
   bool has_stmt_list() const { return has_stmt_list_; }
@@ -1519,27 +1570,30 @@
 //   readelf --debug-dump=info foo.bin
 void AddDIE(const dwarf::File& file, const std::string& name,
             const GeneralDIE& die, const SymbolTable& symtab,
-            const DualMap& symbol_map, const dwarf::CompilationUnitSizes& sizes,
+            const DualMap& symbol_map, const dwarf::DIEReader& die_reader,
             RangeSink* sink) {
+  uint64_t low_pc = 0;
   // Some DIEs mark address ranges with high_pc/low_pc pairs (especially
   // functions).
-  if (die.has_low_pc() && die.has_high_pc() &&
-      dwarf::IsValidDwarfAddress(die.low_pc(), sizes.address_size())) {
-    uint64_t high_pc = die.high_pc();
+  if (die.low_pc && die.low_pc->IsUint() && die.high_pc &&
+      die.high_pc->IsUint() &&
+      dwarf::IsValidDwarfAddress(die.low_pc->GetUint(die_reader),
+                                 die_reader.unit_sizes().address_size())) {
+    low_pc = die.low_pc->GetUint(die_reader);
+    uint64_t high_pc = die.high_pc->GetUint(die_reader);
 
     // It appears that some compilers make high_pc a size, and others make it an
     // address.
-    if (high_pc >= die.low_pc()) {
-      high_pc -= die.low_pc();
+    if (high_pc >= low_pc) {
+      high_pc -= low_pc;
     }
-    sink->AddVMRangeIgnoreDuplicate("dwarf_pcpair", die.low_pc(), high_pc,
-                                    name);
+    sink->AddVMRangeIgnoreDuplicate("dwarf_pcpair", low_pc, high_pc, name);
   }
 
   // Sometimes a DIE has a linkage_name, which we can look up in the symbol
   // table.
-  if (die.has_linkage_name()) {
-    auto it = symtab.find(die.linkage_name());
+  if (die.linkage_name && die.linkage_name->IsString()) {
+    auto it = symtab.find(die.linkage_name->GetString(die_reader));
     if (it != symtab.end()) {
       sink->AddVMRangeIgnoreDuplicate("dwarf_linkagename", it->second.first,
                                       it->second.second, name);
@@ -1548,16 +1602,16 @@
 
   // Sometimes the DIE has a "location", which gives the location as an address.
   // This parses a very small subset of the overall DWARF expression grammar.
-  if (die.has_location_string()) {
-    string_view location = die.location_string();
-    if (location.size() == sizes.address_size() + 1 &&
+  if (die.location && die.location->IsString()) {
+    string_view location = die.location->GetString(die_reader);
+    if (location.size() == die_reader.unit_sizes().address_size() + 1 &&
         location[0] == DW_OP_addr) {
       location.remove_prefix(1);
       uint64_t addr;
       // TODO(haberman): endian?
-      if (sizes.address_size() == 4) {
+      if (die_reader.unit_sizes().address_size() == 4) {
         addr = ReadFixed<uint32_t>(&location);
-      } else if (sizes.address_size() == 8) {
+      } else if (die_reader.unit_sizes().address_size() == 8) {
         addr = ReadFixed<uint64_t>(&location);
       } else {
         BLOATY_UNREACHABLE();
@@ -1580,39 +1634,99 @@
   }
 
   // Sometimes a location is given as an offset into debug_loc.
-  if (die.has_location_uint64()) {
-    if (die.location_uint64() < file.debug_loc.size()) {
-      absl::string_view loc_range = file.debug_loc.substr(die.location_uint64());
-      loc_range = GetLocationListRange(sizes, loc_range);
+  if (die.location && die.location->IsUint()) {
+    uint64_t location = die.location->GetUint(die_reader);
+    if (location < file.debug_loc.size()) {
+      absl::string_view loc_range = file.debug_loc.substr(location);
+      loc_range = GetLocationListRange(die_reader.unit_sizes(), loc_range);
       sink->AddFileRange("dwarf_locrange", name, loc_range);
     } else if (verbose_level > 0) {
       fprintf(stderr,
               "bloaty: warning: DWARF location out of range, location=%" PRIx64
               "\n",
-              die.location_uint64());
+              location);
     }
   }
 
-  uint64_t ranges_offset = UINT64_MAX;
+  // DWARF 5 range list is the same information as "ranges" but in a different
+  // format.
+  if (die.ranges && die.ranges->form() == DW_FORM_rnglistx && die.ranges->IsUint()) {
+    uint64_t range_list = die.ranges->GetUint(die_reader);
+    const dwarf::CompilationUnitSizes& sizes = die_reader.unit_sizes();
+    string_view offset_data = StrictSubstr(
+        file.debug_rnglists, die_reader.unit_sizes().range_lists_base() + range_list);
+    uint64_t offset = die_reader.unit_sizes().ReadDWARFOffset(&offset_data);
+    string_view data = StrictSubstr(
+        file.debug_rnglists, die_reader.unit_sizes().range_lists_base() + offset);
+    const char* start = data.data();
+    bool done = false;
+    uint64_t base_address = sizes.addr_base();
+    while (!done) {
+      switch (ReadFixed<uint8_t>(&data)) {
+        case DW_RLE_end_of_list:
+          done = true;
+          break;
+        case DW_RLE_base_addressx:
+          base_address = ReadIndirectAddress(
+              die_reader, dwarf::ReadLEB128<uint64_t>(&data));
+          break;
+        case DW_RLE_startx_endx: {
+          uint64_t start = ReadIndirectAddress(
+              die_reader, dwarf::ReadLEB128<uint64_t>(&data));
+          uint64_t end = ReadIndirectAddress(
+              die_reader, dwarf::ReadLEB128<uint64_t>(&data));
+          sink->AddVMRangeIgnoreDuplicate("dwarf_rangelst", start, end - start,
+                                          name);
+          break;
+        }
+        case DW_RLE_startx_length: {
+          uint64_t start = ReadIndirectAddress(
+              die_reader, dwarf::ReadLEB128<uint64_t>(&data));
+          uint64_t length = dwarf::ReadLEB128<uint64_t>(&data);
+          sink->AddVMRangeIgnoreDuplicate("dwarf_rangelst", start, length,
+                                          name);
+          break;
+        }
+        case DW_RLE_offset_pair: {
+          uint64_t start = dwarf::ReadLEB128<uint64_t>(&data) + base_address;
+          uint64_t end = dwarf::ReadLEB128<uint64_t>(&data) + base_address;
+          sink->AddVMRangeIgnoreDuplicate("dwarf_rangelst", start, end - start,
+                                          name);
+          break;
+        }
+        case DW_RLE_base_address:
+        case DW_RLE_start_end:
+        case DW_RLE_start_length:
+          THROW("NYI");
+          break;
+      }
+    }
+    string_view all(start, data.data() - start);
+    sink->AddFileRange("dwarf_rangelst_addrs", name, all);
+  } else {
+    uint64_t ranges_offset = UINT64_MAX;
 
-  // There are two different attributes that sometimes contain an offset into
-  // debug_ranges.
-  if (die.has_ranges()) {
-    ranges_offset = die.ranges();
-  } else if (die.has_start_scope()) {
-    ranges_offset = die.start_scope();
-  }
+    // There are two different attributes that sometimes contain an offset into
+    // debug_ranges.
+    if (die.ranges && die.ranges->IsUint()) {
+      ranges_offset = die.ranges->GetUint(die_reader);
+    } else if (die.start_scope && die.start_scope->IsUint()) {
+      ranges_offset = die.start_scope->GetUint(die_reader);
+    }
 
-  if (ranges_offset != UINT64_MAX) {
-    if (ranges_offset < file.debug_ranges.size()) {
-      absl::string_view ranges_range = file.debug_ranges.substr(ranges_offset);
-      ranges_range = GetRangeListRange(sizes, ranges_range);
-      sink->AddFileRange("dwarf_debugrange", name, ranges_range);
-    } else if (verbose_level > 0) {
-      fprintf(stderr,
-              "bloaty: warning: DWARF debug range out of range, "
-              "ranges_offset=%" PRIx64 "\n",
-              ranges_offset);
+    if (ranges_offset != UINT64_MAX) {
+      if (ranges_offset < file.debug_ranges.size()) {
+        absl::string_view data = file.debug_ranges.substr(ranges_offset);
+        const char* start = data.data();
+        ReadRangeList(die_reader, low_pc, name, sink, &data);
+        string_view all(start, data.data() - start);
+        sink->AddFileRange("dwarf_debugrange", name, all);
+      } else if (verbose_level > 0) {
+        fprintf(stderr,
+                "bloaty: warning: DWARF debug range out of range, "
+                "ranges_offset=%" PRIx64 "\n",
+                ranges_offset);
+      }
     }
   }
 }
@@ -1620,16 +1734,8 @@
 static void ReadDWARFPubNames(const dwarf::File& file, string_view section,
                               RangeSink* sink) {
   dwarf::DIEReader die_reader(file);
-  dwarf::AttrReader<string_view> attr_reader;
   string_view remaining = section;
 
-  attr_reader.OnAttribute(
-      DW_AT_name, [](string_view* s, dwarf::AttrValue data) {
-        if (data.type() == dwarf::AttrValue::Type::kString) {
-          *s = data.GetString();
-        }
-      });
-
   while (remaining.size() > 0) {
     dwarf::CompilationUnitSizes sizes;
     string_view full_unit = remaining;
@@ -1644,7 +1750,12 @@
       THROW("Couldn't seek to debug_info section");
     }
     string_view compileunit_name;
-    attr_reader.ReadAttributes(&die_reader, &compileunit_name);
+    die_reader.ReadAttributes(
+        [&compileunit_name, &die_reader](uint16_t tag, dwarf::AttrValue data) {
+          if (tag == DW_AT_name && data.IsString()) {
+            compileunit_name = data.GetString(die_reader);
+          }
+        });
     if (!compileunit_name.empty()) {
       sink->AddFileRange("dwarf_pubnames", compileunit_name, full_unit);
     }
@@ -1910,56 +2021,6 @@
     std::unordered_map<uint64_t, std::string>* stmt_list_map) {
   dwarf::DIEReader die_reader(file);
   die_reader.set_strp_sink(sink);
-  dwarf::AttrReader<GeneralDIE> attr_reader;
-
-  attr_reader.OnAttribute(DW_AT_name,
-                          [](GeneralDIE* die, dwarf::AttrValue val) {
-                            if (!val.IsString()) return;
-                            die->set_name(val.GetString());
-                          });
-  attr_reader.OnAttribute(DW_AT_linkage_name,
-                          [](GeneralDIE* die, dwarf::AttrValue val) {
-                            if (!val.IsString()) return;
-                            die->set_linkage_name(val.GetString());
-                          });
-  attr_reader.OnAttribute(DW_AT_location,
-                          [](GeneralDIE* die, dwarf::AttrValue val) {
-                            if (val.IsString()) {
-                              die->set_location_string(val.GetString());
-                            } else {
-                              die->set_location_uint64(val.GetUint());
-                            }
-                          });
-  attr_reader.OnAttribute(DW_AT_low_pc,
-                          [](GeneralDIE* die, dwarf::AttrValue val) {
-                            absl::optional<uint64_t> uint = val.ToUint();
-                            if (!uint.has_value()) return;
-                            die->set_low_pc(uint.value());
-                          });
-  attr_reader.OnAttribute(DW_AT_high_pc,
-                          [](GeneralDIE* die, dwarf::AttrValue val) {
-                            absl::optional<uint64_t> uint = val.ToUint();
-                            if (!uint.has_value()) return;
-                            die->set_high_pc(uint.value());
-                          });
-  attr_reader.OnAttribute(DW_AT_stmt_list,
-                          [](GeneralDIE* die, dwarf::AttrValue val) {
-                            absl::optional<uint64_t> uint = val.ToUint();
-                            if (!uint.has_value()) return;
-                            die->set_stmt_list(uint.value());
-                          });
-  attr_reader.OnAttribute(DW_AT_ranges,
-                          [](GeneralDIE* die, dwarf::AttrValue val) {
-                            absl::optional<uint64_t> uint = val.ToUint();
-                            if (!uint.has_value()) return;
-                            die->set_ranges(uint.value());
-                          });
-  attr_reader.OnAttribute(DW_AT_start_scope,
-                          [](GeneralDIE* die, dwarf::AttrValue val) {
-                            absl::optional<uint64_t> uint = val.ToUint();
-                            if (!uint.has_value()) return;
-                            die->set_start_scope(uint.value());
-                          });
 
   if (!die_reader.SeekToStart(section)) {
     return;
@@ -1967,11 +2028,33 @@
 
   do {
     GeneralDIE compileunit_die;
-    attr_reader.ReadAttributes(&die_reader, &compileunit_die);
-    std::string compileunit_name = std::string(compileunit_die.name());
+    die_reader.ReadAttributes(
+        [&die_reader, &compileunit_die](uint16_t tag, dwarf::AttrValue value) {
+          switch (tag) {
+            case DW_AT_addr_base:
+              die_reader.mutable_unit_sizes()->SetAddrBase(value.GetUint(die_reader));
+              break;
+            case DW_AT_str_offsets_base:
+              die_reader.mutable_unit_sizes()->SetStrOffsetsBase(value.GetUint(die_reader));
+              break;
+            case DW_AT_rnglists_base:
+              die_reader.mutable_unit_sizes()->SetRangeListsBase(value.GetUint(die_reader));
+              break;
+            default:
+              ReadGeneralDIEAttr(tag, value, &compileunit_die);
+              break;
+            }
+        });
+    std::string compileunit_name;
+    if (compileunit_die.name && compileunit_die.name->IsString()) {
+      compileunit_name =
+          std::string(compileunit_die.name->GetString(die_reader));
+    }
 
-    if (compileunit_die.has_stmt_list()) {
-      uint64_t stmt_list = compileunit_die.stmt_list();
+    uint64_t stmt_list = UINT64_MAX;
+
+    if (compileunit_die.stmt_list && compileunit_die.stmt_list->IsUint()) {
+      stmt_list = compileunit_die.stmt_list->GetUint(die_reader);
       if (compileunit_name.empty()) {
         auto iter = stmt_list_map->find(stmt_list);
         if (iter != stmt_list_map->end()) {
@@ -1990,11 +2073,10 @@
     sink->AddFileRange("dwarf_debuginfo", compileunit_name,
                        die_reader.unit_range());
     AddDIE(file, compileunit_name, compileunit_die, symtab, symbol_map,
-           die_reader.unit_sizes(), sink);
+           die_reader, sink);
 
-    if (compileunit_die.has_stmt_list()) {
-      uint64_t offset = compileunit_die.stmt_list();
-      ReadDWARFStmtListRange(file, offset, compileunit_name, sink);
+    if (stmt_list != UINT64_MAX) {
+      ReadDWARFStmtListRange(file, stmt_list, compileunit_name, sink);
     }
 
     string_view abbrev_data = file.debug_abbrev;
@@ -2005,15 +2087,18 @@
 
     while (die_reader.NextDIE()) {
       GeneralDIE die;
-      attr_reader.ReadAttributes(&die_reader, &die);
+      die_reader.ReadAttributes([&die](uint16_t tag, dwarf::AttrValue value) {
+        ReadGeneralDIEAttr(tag, value, &die);
+      });
 
       // low_pc == 0 is a signal that this routine was stripped out of the
       // final binary.  Skip this DIE and all of its children.
-      if (die.has_low_pc() && die.low_pc() == 0) {
+      if (die.low_pc && die.low_pc->IsUint() &&
+          die.low_pc->GetUint(die_reader) == 0) {
         die_reader.SkipChildren();
       } else {
-        AddDIE(file, compileunit_name, die, symtab, symbol_map,
-               die_reader.unit_sizes(), sink);
+        AddDIE(file, compileunit_name, die, symtab, symbol_map, die_reader,
+               sink);
       }
     }
   } while (die_reader.NextCompilationUnit());
@@ -2086,14 +2171,6 @@
 
   dwarf::DIEReader die_reader(file);
   dwarf::LineInfoReader line_info_reader(file);
-  dwarf::AttrReader<InlinesDIE> attr_reader;
-
-  attr_reader.OnAttribute(
-      DW_AT_stmt_list, [](InlinesDIE* die, dwarf::AttrValue data) {
-        absl::optional<uint64_t> uint = data.ToUint();
-        if (!uint.has_value()) return;
-        die->set_stmt_list(uint.value());
-      });
 
   if (!die_reader.SeekToStart(dwarf::DIEReader::Section::kDebugInfo)) {
     THROW("debug info is present, but empty");
@@ -2101,7 +2178,15 @@
 
   while (true) {
     InlinesDIE die;
-    attr_reader.ReadAttributes(&die_reader, &die);
+    die_reader.ReadAttributes(
+        [&die, &die_reader](uint16_t tag, dwarf::AttrValue val) {
+          switch (tag) {
+          case DW_AT_stmt_list:
+            if (auto uint = val.ToUint(die_reader))
+              die.set_stmt_list(uint.value());
+            break;
+          }
+        });
 
     if (die.has_stmt_list()) {
       uint64_t offset = die.stmt_list();
@@ -2116,4 +2201,4 @@
   }
 }
 
-}  // namespace bloaty
+} // namespace bloaty
diff --git a/src/dwarf_constants.h b/src/dwarf_constants.h
index ba6eb2c..78891fb 100644
--- a/src/dwarf_constants.h
+++ b/src/dwarf_constants.h
@@ -152,9 +152,27 @@
   DW_FORM_exprloc = 0x18,
   DW_FORM_flag_present = 0x19,
   // DWARF 5.
+  DW_FORM_strx = 0x1a,
+  DW_FORM_addrx = 0x1b,
+  DW_FORM_ref_sup4 = 0x1c,
+  DW_FORM_strp_sup = 0x1d,
+  DW_FORM_data16 = 0x1e,
   DW_FORM_line_strp = 0x1f,
   // DWARF 4.
   DW_FORM_ref_sig8 = 0x20,
+  // DWARF 5.
+  DW_FORM_implicit_const = 0x21,
+  DW_FORM_loclistx = 0x22,
+  DW_FORM_rnglistx = 0x23,
+  DW_FORM_ref_sup8 = 0x24,
+  DW_FORM_strx1 = 0x25,
+  DW_FORM_strx2 = 0x26,
+  DW_FORM_strx3 = 0x27,
+  DW_FORM_strx4 = 0x28,
+  DW_FORM_addrx1 = 0x29,
+  DW_FORM_addrx2 = 0x2a,
+  DW_FORM_addrx3 = 0x2b,
+  DW_FORM_addrx4 = 0x2c,
   // Extensions for Fission.  See http://gcc.gnu.org/wiki/DebugFission.
   DW_FORM_GNU_addr_index = 0x1f01,
   DW_FORM_GNU_str_index = 0x1f02
@@ -261,6 +279,36 @@
   DW_AT_const_expr = 0x6c,
   DW_AT_enum_class = 0x6d,
   DW_AT_linkage_name = 0x6e,
+  // DWARF 5 values.
+  DW_AT_string_length_bit_size = 0x6f,
+  DW_AT_string_length_byte_size = 0x70,
+  DW_AT_rank = 0x71,
+  DW_AT_str_offsets_base = 0x72,
+  DW_AT_addr_base = 0x73,
+  DW_AT_rnglists_base = 0x74,
+  DW_AT_dwo_name = 0x76,
+  DW_AT_reference = 0x77,
+  DW_AT_rvalue_reference = 0x78,
+  DW_AT_macros = 0x79,
+  DW_AT_call_all_calls = 0x7a,
+  DW_AT_call_all_source_calls = 0x7b,
+  DW_AT_call_all_tail_calls = 0x7c,
+  DW_AT_call_return_pc = 0x7d,
+  DW_AT_call_value = 0x7e,
+  DW_AT_call_origin = 0x7f,
+  DW_AT_call_parameter = 0x80,
+  DW_AT_call_pc = 0x81,
+  DW_AT_call_tail_call = 0x82,
+  DW_AT_call_target = 0x83,
+  DW_AT_call_target_clobbered = 0x84,
+  DW_AT_call_data_location = 0x85,
+  DW_AT_call_data_value = 0x86,
+  DW_AT_noreturn = 0x87,
+  DW_AT_alignment = 0x88,
+  DW_AT_export_symbols = 0x89,
+  DW_AT_deleted = 0x8a,
+  DW_AT_defaulted = 0x8b,
+  DW_AT_loclists_base = 0x8c,
   // SGI/MIPS extensions.
   DW_AT_MIPS_fde = 0x2001,
   DW_AT_MIPS_loop_begin = 0x2002,
@@ -645,5 +693,16 @@
   DW_EH_PE_omit        = 0xff
 };
 
+enum RangeListEntry {
+  DW_RLE_end_of_list = 0x00,
+  DW_RLE_base_addressx = 0x01,
+  DW_RLE_startx_endx = 0x02,
+  DW_RLE_startx_length = 0x03,
+  DW_RLE_offset_pair = 0x04,
+  DW_RLE_base_address = 0x05,
+  DW_RLE_start_end = 0x06,
+  DW_RLE_start_length = 0x07,
+};
+
 }  // namespace dwarf2reader
 #endif  // UTIL_DEBUGINFO_DWARF2ENUMS_H__
diff --git a/src/util.h b/src/util.h
index eea7737..1aad13f 100644
--- a/src/util.h
+++ b/src/util.h
@@ -133,13 +133,14 @@
     return _BS<sizeof(T)>(val);
 }
 
-template <class T> T ReadFixed(absl::string_view *data) {
-  T val;
-  if (data->size() < sizeof(T)) {
+template <class T, size_t N = sizeof(T)> T ReadFixed(absl::string_view *data) {
+  static_assert(N <= sizeof(N), "N too big for this data type");
+  T val = 0;
+  if (data->size() < N) {
     THROW("premature EOF reading fixed-length data");
   }
-  memcpy(&val, data->data(), sizeof(T));
-  data->remove_prefix(sizeof(T));
+  memcpy(&val, data->data(), N);
+  data->remove_prefix(N);
   return val;
 }