Implement dwarf5 range lists.

This is a big change. dwarf5 range lists are quite a bit more complicated
than dwarf 4 range lists, both in the contextual information required, and
in their own representation and interpretation.

The big design choice here is how to pass the CU information all the
way down to the reader. I chose a structure, because otherwise the
parameter list gets very long and error prone (and has to be passed
down several levels). This structure could be made a parto of the CU
context itself, or the range handler, so it wouldn't have to be
separately assembled at range-list read time, but both of those
solutions get even more invasive, and harder to follow.

I've tried to figure out how to break this into smaller changes, but it
affects nearly everything that has to do with a compilation unit's
own addresses and when decisions must be made about how to read them.
Dependency injection will do that to you.

It does add tests for range list reading, which did not exist before.

Change-Id: I923b9a2c3379a0f52609bc05310097de5cbb7227
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/2446635
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
diff --git a/src/common/dwarf/dwarf2enums.h b/src/common/dwarf/dwarf2enums.h
index 7bd3979..b93510a 100644
--- a/src/common/dwarf/dwarf2enums.h
+++ b/src/common/dwarf/dwarf2enums.h
@@ -169,6 +169,7 @@
   DW_FORM_ref_sig8 = 0x20,
 
   // Added in DWARF 5:
+  DW_FORM_rnglistx = 0x23,
   DW_FORM_strx1 = 0x25,
   DW_FORM_strx2 = 0x26,
   DW_FORM_strx3 = 0x27,
@@ -264,6 +265,9 @@
   DW_AT_call_line     = 0x59,
   // DWARF 4
   DW_AT_linkage_name  = 0x6e,
+  // DWARF 5
+  DW_AT_addr_base = 0x73,
+  DW_AT_rnglists_base = 0x74,
   // SGI/MIPS extensions.
   DW_AT_MIPS_fde = 0x2001,
   DW_AT_MIPS_loop_begin = 0x2002,
@@ -316,6 +320,18 @@
   DW_AT_PGI_lstride  = 0x3a02
 };
 
+// .debug_rngslist entry types
+enum DwarfRngListEntry {
+  DW_RLE_end_of_list = 0,
+  DW_RLE_base_addressx = 1,
+  DW_RLE_startx_endx = 2,
+  DW_RLE_startx_length = 3,
+  DW_RLE_offset_pair = 4,
+  DW_RLE_base_address = 5,
+  DW_RLE_start_end = 6,
+  DW_RLE_start_length = 7,
+};
+
 // Line number content type codes (DWARF 5).
 enum DwarfLineNumberContentType {
   DW_LNCT_path = 1,
diff --git a/src/common/dwarf/dwarf2reader.cc b/src/common/dwarf/dwarf2reader.cc
index aca8367..f1f4007 100644
--- a/src/common/dwarf/dwarf2reader.cc
+++ b/src/common/dwarf/dwarf2reader.cc
@@ -226,6 +226,7 @@
     case DW_FORM_GNU_str_index:
     case DW_FORM_GNU_addr_index:
     case DW_FORM_addrx:
+    case DW_FORM_rnglistx:
       reader_->ReadUnsignedLEB128(start, &len);
       return start + len;
 
@@ -657,6 +658,10 @@
       ProcessAttributeAddrIndex(
           dieoffset, attr, form, reader_->ReadFourBytes(start));
       return start + 4;
+    case DW_FORM_rnglistx:
+      ProcessAttributeUnsigned(
+          dieoffset, attr, form, reader_->ReadUnsignedLEB128(start, &len));
+      return start + len;
   }
   fprintf(stderr, "Unhandled form type\n");
   return NULL;
@@ -1568,11 +1573,76 @@
   after_header_ = lengthstart + header_.total_length;
 }
 
-RangeListReader::RangeListReader(const uint8_t* buffer, uint64_t size,
-                                 ByteReader* reader, RangeListHandler* handler)
-    : buffer_(buffer), size_(size), reader_(reader), handler_(handler) { }
+bool RangeListReader::SetRangesBase(uint64_t offset) {
+  // Versions less than 5 don't use ranges base.
+  if (cu_info_->version_ < 5) {
+    return true;
+  }
+  // Length may not be 12 bytes, but if 12 bytes aren't available
+  // at this point, then the header is too short.
+  if (offset + 12 >= cu_info_->size_) {
+    return false;
+  }
+  // The length of this CU's contribution.
+  uint64_t cu_length = reader_->ReadFourBytes(cu_info_->buffer_ + offset);
+  offset += 4;
+  if (cu_length == 0xffffffffUL) {
+    cu_length = reader_->ReadEightBytes(cu_info_->buffer_ + offset);
+    offset += 8;
+  }
 
-bool RangeListReader::ReadRangeList(uint64_t offset) {
+  // Truncating size here results in correctly ignoring everything not from
+  // this cu from here on out.
+  cu_info_->size_ = offset + cu_length;
+
+  // Check for the rest of the header in advance.
+  if (offset + 8 >= cu_info_->size_) {
+    return false;
+  }
+  // Version. Can only read version 5.
+  if (reader_->ReadTwoBytes(cu_info_->buffer_ + offset) != 5) {
+    return false;
+  }
+  offset += 2;
+  // Address size
+  if (reader_->ReadOneByte(cu_info_->buffer_ + offset) !=
+      reader_->AddressSize()) {
+    return false;
+  }
+  offset += 1;
+  // Segment selectors are unsupported
+  if (reader_->ReadOneByte(cu_info_->buffer_ + offset) != 0) {
+    return false;
+  }
+  offset += 1;
+  offset_entry_count_ = reader_->ReadFourBytes(cu_info_->buffer_ + offset);
+  offset += 4;
+  offset_array_ = offset;
+  return true;
+}
+
+bool RangeListReader::ReadRanges(enum DwarfForm form, uint64_t data) {
+  if (form == DW_FORM_sec_offset) {
+    if (cu_info_->version_ <= 4) {
+      return ReadDebugRanges(data);
+    } else {
+      return ReadDebugRngList(data);
+    }
+  } else if (form == DW_FORM_rnglistx) {
+    SetRangesBase(cu_info_->ranges_base_);
+    if (data >= offset_entry_count_) {
+      return false;
+    }
+    uint64_t index_offset = reader_->AddressSize() * data;
+    uint64_t range_list_offset =
+        reader_->ReadAddress(cu_info_->buffer_ + offset_array_ + index_offset);
+
+    return ReadDebugRngList(range_list_offset);
+  }
+  return false;
+}
+
+bool RangeListReader::ReadDebugRanges(uint64_t offset) {
   const uint64_t max_address =
     (reader_->AddressSize() == 4) ? 0xffffffffUL
                                   : 0xffffffffffffffffULL;
@@ -1580,21 +1650,22 @@
   bool list_end = false;
 
   do {
-    if (offset > size_ - entry_size) {
+    if (offset > cu_info_->size_ - entry_size) {
       return false; // Invalid range detected
     }
 
-    uint64_t start_address = reader_->ReadAddress(buffer_ + offset);
-    uint64_t end_address =
-      reader_->ReadAddress(buffer_ + offset + reader_->AddressSize());
+    uint64_t start_address = reader_->ReadAddress(cu_info_->buffer_ + offset);
+    uint64_t end_address = reader_->ReadAddress(
+        cu_info_->buffer_ + offset + reader_->AddressSize());
 
     if (start_address == max_address) { // Base address selection
-      handler_->SetBaseAddress(end_address);
+      cu_info_->base_address_ = end_address;
     } else if (start_address == 0 && end_address == 0) { // End-of-list
       handler_->Finish();
       list_end = true;
     } else { // Add a range entry
-      handler_->AddRange(start_address, end_address);
+      handler_->AddRange(start_address + cu_info_->base_address_,
+                         end_address + cu_info_->base_address_);
     }
 
     offset += entry_size;
@@ -1603,6 +1674,62 @@
   return true;
 }
 
+bool RangeListReader::ReadDebugRngList(uint64_t offset) {
+  uint64_t start = 0;
+  uint64_t end = 0;
+  uint64_t range_len = 0;
+  uint64_t index = 0;
+  // A uleb128's length isn't known until after it has been read, so overruns
+  // are only caught after an entire entry.
+  while (offset < cu_info_->size_) {
+    uint8_t entry_type = reader_->ReadOneByte(cu_info_->buffer_ + offset);
+    offset += 1;
+    // Handle each entry type per Dwarf 5 Standard, section 2.17.3.
+    switch (entry_type) {
+      case DW_RLE_end_of_list:
+        handler_->Finish();
+        return true;
+      case DW_RLE_base_addressx:
+        offset += ReadULEB(offset, &index);
+        cu_info_->base_address_ = GetAddressAtIndex(index);
+        break;
+      case DW_RLE_startx_endx:
+        offset += ReadULEB(offset, &index);
+        start = GetAddressAtIndex(index);
+        offset += ReadULEB(offset, &index);
+        end = GetAddressAtIndex(index);
+        handler_->AddRange(start, end);
+        break;
+      case DW_RLE_startx_length:
+        offset += ReadULEB(offset, &index);
+        start = GetAddressAtIndex(index);
+        offset += ReadULEB(offset, &range_len);
+        handler_->AddRange(start, start + range_len);
+        break;
+      case DW_RLE_offset_pair:
+        offset += ReadULEB(offset, &start);
+        offset += ReadULEB(offset, &end);
+        handler_->AddRange(start + cu_info_->base_address_,
+                           end + cu_info_->base_address_);
+        break;
+      case DW_RLE_base_address:
+        offset += ReadAddress(offset, &cu_info_->base_address_);
+        break;
+      case DW_RLE_start_end:
+        offset += ReadAddress(offset, &start);
+        offset += ReadAddress(offset, &end);
+        handler_->AddRange(start, end);
+        break;
+      case DW_RLE_start_length:
+        offset += ReadAddress(offset, &start);
+        offset += ReadULEB(offset, &end);
+        handler_->AddRange(start, start + end);
+        break;
+    }
+  }
+  return false;
+}
+
 // A DWARF rule for recovering the address or value of a register, or
 // computing the canonical frame address. There is one subclass of this for
 // each '*Rule' member function in CallFrameInfo::Handler.
diff --git a/src/common/dwarf/dwarf2reader.h b/src/common/dwarf/dwarf2reader.h
index e405e3a..d041dd8 100644
--- a/src/common/dwarf/dwarf2reader.h
+++ b/src/common/dwarf/dwarf2reader.h
@@ -234,25 +234,78 @@
   // Add a range.
   virtual void AddRange(uint64_t begin, uint64_t end) { };
 
-  // A new base address must be set for computing the ranges' addresses.
-  virtual void SetBaseAddress(uint64_t base_address) { };
-
   // Finish processing the range list.
   virtual void Finish() { };
 };
 
 class RangeListReader {
  public:
-  RangeListReader(const uint8_t* buffer, uint64_t size, ByteReader* reader,
-                  RangeListHandler* handler);
+  // Reading a range list requires quite a bit of information
+  // from the compilation unit. Package it conveniently.
+  struct CURangesInfo {
+    CURangesInfo() :
+        version_(0), base_address_(0), ranges_base_(0),
+        buffer_(nullptr), size_(0), addr_buffer_(nullptr),
+        addr_buffer_size_(0), addr_base_(0) { }
 
-  bool ReadRangeList(uint64_t offset);
+    uint16_t version_;
+    // Ranges base address. Ordinarily the CU's low_pc.
+    uint64_t base_address_;
+    // Offset into .debug_rnglists for this CU's rangelists.
+    uint64_t ranges_base_;
+    // Contents of either .debug_ranges or .debug_rnglists.
+    const uint8_t* buffer_;
+    uint64_t size_;
+    // Contents of .debug_addr. This cu's contribution starts at
+    // addr_base_
+    const uint8_t* addr_buffer_;
+    uint64_t addr_buffer_size_;
+    uint64_t addr_base_;
+  };
+
+  RangeListReader(ByteReader* reader, CURangesInfo* cu_info,
+                  RangeListHandler* handler) :
+      reader_(reader), cu_info_(cu_info), handler_(handler),
+      offset_array_(0), offset_entry_count_(0) { }
+
+  // Read ranges from cu_info as specified by form and data.
+  bool ReadRanges(enum DwarfForm form, uint64_t data);
 
  private:
-  const uint8_t* buffer_;
-  uint64_t size_;
+  bool SetRangesBase(uint64_t base);
+
+  // Read dwarf4 .debug_ranges at offset.
+  bool ReadDebugRanges(uint64_t offset);
+  // Read dwarf5 .debug_rngslist at offset.
+  bool ReadDebugRngList(uint64_t offset);
+
+  // Convenience functions to handle the mechanics of reading entries in the
+  // ranges section.
+  uint64_t ReadULEB(uint64_t offset, uint64_t* value) {
+    uint64_t len;
+    *value = reader_->ReadUnsignedLEB128(cu_info_->buffer_ + offset, &len);
+    return len;
+  }
+
+  uint64_t ReadAddress(uint64_t offset, uint64_t* value) {
+    *value = reader_->ReadAddress(cu_info_->buffer_ + offset);
+    return reader_->AddressSize();
+  }
+
+  // Read the address at this CU's addr_index in the .debug_addr section.
+  uint64_t GetAddressAtIndex(uint64_t addr_index) {
+    assert(cu_info_->addr_buffer_ != nullptr);
+    uint64_t offset =
+        cu_info_->addr_base_ + addr_index * reader_->AddressSize();
+    assert(offset < cu_info_->addr_buffer_size_);
+    return reader_->ReadAddress(cu_info_->addr_buffer_ + offset);
+  }
+
   ByteReader* reader_;
+  CURangesInfo* cu_info_;
   RangeListHandler* handler_;
+  uint64_t offset_array_;
+  uint64_t offset_entry_count_;
 };
 
 // This class is the main interface between the reader and the
@@ -492,7 +545,7 @@
     else if (attr == DW_AT_GNU_addr_base) {
       addr_base_ = data;
     }
-    else if (attr == DW_AT_GNU_ranges_base) {
+    else if (attr == DW_AT_GNU_ranges_base || attr == DW_AT_rnglists_base) {
       ranges_base_ = data;
     }
     // TODO(yunlian): When we add DW_AT_ranges_base from DWARF-5,
@@ -654,7 +707,8 @@
   // from the skeleton CU.
   uint64_t skeleton_dwo_id_;
 
-  // The value of the DW_AT_GNU_ranges_base attribute, if any.
+  // The value of the DW_AT_GNU_ranges_base or DW_AT_rnglists_base attribute,
+  // if any.
   uint64_t ranges_base_;
 
   // The value of the DW_AT_GNU_addr_base attribute, if any.
diff --git a/src/common/dwarf/dwarf2reader_die_unittest.cc b/src/common/dwarf/dwarf2reader_die_unittest.cc
index 8781932..0d8811b 100644
--- a/src/common/dwarf/dwarf2reader_die_unittest.cc
+++ b/src/common/dwarf/dwarf2reader_die_unittest.cc
@@ -491,3 +491,142 @@
                       DwarfHeaderParams(kBigEndian,    8, 3, 8),
                       DwarfHeaderParams(kBigEndian,    8, 4, 4),
                       DwarfHeaderParams(kBigEndian,    8, 4, 8)));
+
+class MockRangeListHandler: public dwarf2reader::RangeListHandler {
+ public:
+  MOCK_METHOD(void, AddRange, (uint64_t begin, uint64_t end));
+  MOCK_METHOD(void, Finish, ());
+};
+
+TEST(RangeList, Dwarf4ReadRangeList) {
+  using dwarf2reader::RangeListReader;
+  using dwarf2reader::DW_FORM_sec_offset;
+
+  // Create a dwarf4 .debug_ranges section.
+  google_breakpad::test_assembler::Section ranges(kBigEndian);
+  std::string padding_offset = "padding offset";
+  ranges.Append(padding_offset);
+  const uint64_t section_offset = ranges.Size();
+  ranges.D32(1).D32(2);          // (2, 3)
+  ranges.D32(0xFFFFFFFF).D32(3); // base_address = 3.
+  ranges.D32(1).D32(2);          // (4, 5)
+  ranges.D32(0).D32(1);          // (3, 4) An out of order entry is legal.
+  ranges.D32(0).D32(0);          // End of range.
+
+  std::string section_contents;
+  ranges.GetContents(&section_contents);
+
+  ByteReader byte_reader(ENDIANNESS_BIG);
+  byte_reader.SetAddressSize(4);
+
+  RangeListReader::CURangesInfo cu_info;
+  // Only set the fields that matter for dwarf 4.
+  cu_info.version_ = 4;
+  cu_info.base_address_ = 1;
+  cu_info.buffer_ = reinterpret_cast<const uint8_t*>(section_contents.data());
+  cu_info.size_ = section_contents.size();
+
+  MockRangeListHandler handler;
+  dwarf2reader::RangeListReader range_list_reader(&byte_reader, &cu_info,
+                                                  &handler);
+  EXPECT_CALL(handler, AddRange(2, 3));
+  EXPECT_CALL(handler, AddRange(4, 5));
+  EXPECT_CALL(handler, AddRange(3, 4));
+  EXPECT_CALL(handler, Finish());
+  EXPECT_TRUE(range_list_reader.ReadRanges(DW_FORM_sec_offset,
+                                           section_offset));
+}
+
+TEST(RangeList, Dwarf5ReadRangeList) {
+  using dwarf2reader::RangeListReader;
+  using dwarf2reader::DW_RLE_base_addressx;
+  using dwarf2reader::DW_RLE_startx_endx;
+  using dwarf2reader::DW_RLE_startx_length;
+  using dwarf2reader::DW_RLE_offset_pair;
+  using dwarf2reader::DW_RLE_end_of_list;
+  using dwarf2reader::DW_RLE_base_address;
+  using dwarf2reader::DW_RLE_offset_pair;
+  using dwarf2reader::DW_RLE_start_end;
+  using dwarf2reader::DW_RLE_start_length;
+  using dwarf2reader::DW_RLE_end_of_list;
+  using dwarf2reader::DW_FORM_sec_offset;
+  using dwarf2reader::DW_FORM_rnglistx;
+
+  // .debug_addr for the indexed entries like startx.
+  Section addr;
+  addr.set_endianness(kBigEndian);
+  // Test addr_base handling with a padding address at 0.
+  addr.D32(0).D32(1).D32(2).D32(3).D32(4);
+  std::string addr_contents;
+  assert(addr.GetContents(&addr_contents));
+
+  // .debug_rnglists is the dwarf 5 section.
+  Section rnglists;
+  rnglists.set_endianness(kBigEndian);
+  std::string padding_offset = "padding offset";
+  rnglists.Append(padding_offset);
+  const uint64_t ranges_base = rnglists.Size();
+
+  // Header
+  Label section_size;
+  rnglists.Append(kBigEndian, 4, section_size);
+  rnglists.D16(5); // Version
+  rnglists.D8(4);  // Address size
+  rnglists.D8(0);  // Segment selector size
+  rnglists.D32(2); // Offset entry count
+  // Offset entries.
+  Label range0;
+  rnglists.Append(kBigEndian, 4, range0);
+  Label range1;
+  rnglists.Append(kBigEndian, 4, range1);
+
+  // Range 0 (will be read via DW_AT_ranges, DW_FORM_sec_offset).
+  range0 = rnglists.Size();
+  rnglists.D8(DW_RLE_base_addressx).ULEB128(0); // base_addr = 1
+  rnglists.D8(DW_RLE_startx_endx).ULEB128(1).ULEB128(2); // (2, 3)
+  rnglists.D8(DW_RLE_startx_length).ULEB128(3).ULEB128(1); // (4, 5)
+  rnglists.D8(DW_RLE_offset_pair).ULEB128(5).ULEB128(6); // (6, 7)
+  rnglists.D8(DW_RLE_end_of_list);
+
+  // Range 1 (will be read via DW_AT_ranges, DW_FORM_rnglistx).
+  range1 = rnglists.Size();
+  rnglists.D8(DW_RLE_base_address).D32(8); // base_addr = 8
+  rnglists.D8(DW_RLE_offset_pair).ULEB128(1).ULEB128(2); // (9, 10)
+  rnglists.D8(DW_RLE_start_end).D32(10).D32(11); // (10, 11)
+  rnglists.D8(DW_RLE_start_length).D32(12).ULEB128(1); // (12, 13)
+  rnglists.D8(DW_RLE_end_of_list);
+  section_size = rnglists.Size();
+  std::string rnglists_contents;
+  assert(rnglists.GetContents(&rnglists_contents));
+
+  RangeListReader::CURangesInfo cu_info;
+  // Only set the fields that matter for dwarf 4.
+  cu_info.version_ = 5;
+  cu_info.base_address_ = 1;
+  cu_info.ranges_base_ = ranges_base;
+  cu_info.buffer_ =
+      reinterpret_cast<const uint8_t*>(rnglists_contents.data());
+  cu_info.size_ = rnglists_contents.size();
+  cu_info.addr_buffer_ =
+      reinterpret_cast<const uint8_t*>(addr_contents.data());
+  cu_info.addr_buffer_size_ = addr_contents.size();
+  cu_info.addr_base_ = 4;
+
+  ByteReader byte_reader(ENDIANNESS_BIG);
+  byte_reader.SetAddressSize(4);
+  MockRangeListHandler handler;
+  dwarf2reader::RangeListReader range_list_reader(&byte_reader, &cu_info,
+                                                  &handler);
+  EXPECT_CALL(handler, AddRange(2, 3));
+  EXPECT_CALL(handler, AddRange(4, 5));
+  EXPECT_CALL(handler, AddRange(6, 7));
+  EXPECT_CALL(handler, AddRange(9, 10));
+  EXPECT_CALL(handler, AddRange(10, 11));
+  EXPECT_CALL(handler, AddRange(12, 13));
+  EXPECT_CALL(handler, Finish()).Times(2);
+  EXPECT_TRUE(range_list_reader.ReadRanges(DW_FORM_rnglistx, 1));
+  EXPECT_TRUE(range_list_reader.ReadRanges(DW_FORM_sec_offset,
+                                           range0.Value()));
+  // Out of range index, should result in no calls.
+  EXPECT_FALSE(range_list_reader.ReadRanges(DW_FORM_rnglistx, 2));
+}
diff --git a/src/common/dwarf_cu_to_module.cc b/src/common/dwarf_cu_to_module.cc
index a5bc7d6..168c466 100644
--- a/src/common/dwarf_cu_to_module.cc
+++ b/src/common/dwarf_cu_to_module.cc
@@ -172,13 +172,16 @@
 struct DwarfCUToModule::CUContext {
   CUContext(FileContext* file_context_arg, WarningReporter* reporter_arg,
             RangesHandler* ranges_handler_arg)
-      : file_context(file_context_arg),
+      : version(0),
+        file_context(file_context_arg),
         reporter(reporter_arg),
         ranges_handler(ranges_handler_arg),
         language(Language::CPlusPlus),
         low_pc(0),
         high_pc(0),
-        ranges(0) {}
+        ranges_form(dwarf2reader::DW_FORM_sec_offset),
+        ranges_data(0),
+        ranges_base(0) { }
 
   ~CUContext() {
     for (vector<Module::Function*>::iterator it = functions.begin();
@@ -187,6 +190,9 @@
     }
   };
 
+  // Dwarf version of the source CU.
+  uint8_t version;
+
   // The DWARF-bearing file into which this CU was incorporated.
   FileContext* file_context;
 
@@ -200,11 +206,54 @@
   const Language* language;
 
   // Addresses covered by this CU. If high_pc_ is non-zero then the CU covers
-  // low_pc to high_pc, otherwise ranges is non-zero and low_pc represents
-  // the base address of the ranges covered by the CU.
+  // low_pc to high_pc, otherwise ranges_data is non-zero and low_pc represents
+  // the base address of the ranges covered by the CU. ranges_data will define
+  // the CU's actual ranges.
   uint64_t low_pc;
   uint64_t high_pc;
-  uint64_t ranges;
+
+  // Ranges for this CU are read according to this form.
+  enum dwarf2reader::DwarfForm ranges_form;
+  uint64_t ranges_data;
+
+  // Offset into .debug_rngslists where this CU's ranges are stored.
+  // Data in DW_FORM_rnglistx is relative to this offset.
+  uint64_t ranges_base;
+
+  // Offset into .debug_addr where this CU's addresses are stored. Data in
+  // form DW_FORM_addrxX is relative to this offset.
+  uint64_t addr_base;
+
+  // Collect all the data from the CU that a RangeListReader needs to read a
+  // range.
+  bool AssembleRangeListInfo(
+      dwarf2reader::RangeListReader::CURangesInfo* info) {
+    const dwarf2reader::SectionMap& section_map
+        = file_context->section_map();
+    info->version_ = version;
+    info->base_address_ = low_pc;
+    info->ranges_base_ = ranges_base;
+    const char* section_name = (version <= 4 ?
+                                ".debug_ranges" : ".debug_rnglists");
+    dwarf2reader::SectionMap::const_iterator map_entry
+        = dwarf2reader::GetSectionByName(section_map, section_name);
+    if (map_entry == section_map.end()) {
+      return false;
+    }
+    info->buffer_ = map_entry->second.first;
+    info->size_ = map_entry->second.second;
+    if (version > 4) {
+      dwarf2reader::SectionMap::const_iterator map_entry
+          = dwarf2reader::GetSectionByName(section_map, ".debug_addr");
+      if (map_entry == section_map.end()) {
+        return false;
+      }
+      info->addr_buffer_ = map_entry->second.first;
+      info->addr_buffer_size_ = map_entry->second.second;
+      info->addr_base_ = addr_base;
+    }
+    return true;
+  }
 
   // The functions defined in this compilation unit. We accumulate
   // them here during parsing. Then, in DwarfCUToModule::Finish, we
@@ -470,7 +519,9 @@
               uint64_t offset)
       : GenericDIEHandler(cu_context, parent_context, offset),
         low_pc_(0), high_pc_(0), high_pc_form_(dwarf2reader::DW_FORM_addr),
-        ranges_(0), abstract_origin_(NULL), inline_(false) { }
+        ranges_form_(dwarf2reader::DW_FORM_sec_offset), ranges_data_(0),
+        abstract_origin_(NULL), inline_(false) { }
+
   void ProcessAttributeUnsigned(enum DwarfAttribute attr,
                                 enum DwarfForm form,
                                 uint64_t data);
@@ -490,7 +541,8 @@
   string name_;
   uint64_t low_pc_, high_pc_; // DW_AT_low_pc, DW_AT_high_pc
   DwarfForm high_pc_form_; // DW_AT_high_pc can be length or address.
-  uint64_t ranges_; // DW_AT_ranges
+  DwarfForm ranges_form_; // DW_FORM_sec_offset or DW_FORM_rnglistx
+  uint64_t ranges_data_; // DW_AT_ranges
   const AbstractOrigin* abstract_origin_;
   bool inline_;
 };
@@ -511,7 +563,8 @@
       high_pc_ = data;
       break;
     case dwarf2reader::DW_AT_ranges:
-      ranges_ = data;
+      ranges_data_ = data;
+      ranges_form_ = form;
       break;
 
     default:
@@ -590,7 +643,7 @@
       iter->second->name = name_;
   }
 
-  if (!ranges_) {
+  if (!ranges_data_) {
     // Make high_pc_ an address, if it isn't already.
     if (high_pc_form_ != dwarf2reader::DW_FORM_addr &&
         high_pc_form_ != dwarf2reader::DW_FORM_GNU_addr_index &&
@@ -606,14 +659,17 @@
     ranges.push_back(range);
   } else {
     RangesHandler* ranges_handler = cu_context_->ranges_handler;
-
     if (ranges_handler) {
-      if (!ranges_handler->ReadRanges(ranges_, cu_context_->low_pc, &ranges)) {
-        ranges.clear();
-        cu_context_->reporter->MalformedRangeList(ranges_);
+      dwarf2reader::RangeListReader::CURangesInfo cu_info;
+      if (cu_context_->AssembleRangeListInfo(&cu_info)) {
+        if (!ranges_handler->ReadRanges(ranges_form_, ranges_data_,
+                                        &cu_info, &ranges)) {
+          ranges.clear();
+          cu_context_->reporter->MalformedRangeList(ranges_data_);
+        }
+      } else {
+        cu_context_->reporter->MissingRanges();
       }
-    } else {
-      cu_context_->reporter->MissingRanges();
     }
   }
 
@@ -843,7 +899,15 @@
       cu_context_->high_pc  = data;
       break;
     case dwarf2reader::DW_AT_ranges:
-      cu_context_->ranges = data;
+      cu_context_->ranges_data = data;
+      cu_context_->ranges_form = form;
+      break;
+    case dwarf2reader::DW_AT_rnglists_base:
+      cu_context_->ranges_base = data;
+      break;
+    case dwarf2reader::DW_AT_addr_base:
+    case dwarf2reader::DW_AT_GNU_addr_base:
+      cu_context_->addr_base = data;
       break;
 
     default:
@@ -1262,6 +1326,7 @@
                                            uint8_t offset_size,
                                            uint64_t cu_length,
                                            uint8_t dwarf_version) {
+  cu_context_->version = dwarf_version;
   return dwarf_version >= 2;
 }
 
diff --git a/src/common/dwarf_cu_to_module.h b/src/common/dwarf_cu_to_module.h
index 3e15b66..386d628 100644
--- a/src/common/dwarf_cu_to_module.h
+++ b/src/common/dwarf_cu_to_module.h
@@ -45,7 +45,6 @@
 
 #include "common/language.h"
 #include "common/module.h"
-#include "common/dwarf/bytereader.h"
 #include "common/dwarf/dwarf2diehandler.h"
 #include "common/dwarf/dwarf2reader.h"
 #include "common/scoped_ptr.h"
@@ -131,12 +130,11 @@
     virtual ~RangesHandler() { }
 
     // Called when finishing a function to populate the function's ranges.
-    // The ranges' entries are read starting from offset in the .debug_ranges
-    // section, base_address holds the base PC the range list values are
-    // offsets off. Return false if the rangelist falls out of the
-    // .debug_ranges section.
-    virtual bool ReadRanges(uint64_t offset, Module::Address base_address,
-                            vector<Module::Range>* ranges) = 0;
+    // The entries are read according to the form and data.
+    virtual bool ReadRanges(
+        enum dwarf2reader::DwarfForm form, uint64_t data,
+        dwarf2reader::RangeListReader::CURangesInfo* cu_info,
+        vector<Module::Range>* ranges) = 0;
   };
 
   // An abstract base class for handlers that handle DWARF line data
diff --git a/src/common/dwarf_range_list_handler.cc b/src/common/dwarf_range_list_handler.cc
index 58982aa..3fecb56 100644
--- a/src/common/dwarf_range_list_handler.cc
+++ b/src/common/dwarf_range_list_handler.cc
@@ -40,15 +40,11 @@
 namespace google_breakpad {
 
 void DwarfRangeListHandler::AddRange(uint64_t begin, uint64_t end) {
-  Module::Range r(begin + base_address_, end - begin);
+  Module::Range r(begin, end - begin);
 
   ranges_->push_back(r);
 }
 
-void DwarfRangeListHandler::SetBaseAddress(uint64_t base_address) {
-  base_address_ = base_address;
-}
-
 void DwarfRangeListHandler::Finish() {
   std::sort(ranges_->begin(), ranges_->end(),
     [](const Module::Range& a, const Module::Range& b) {
diff --git a/src/common/dwarf_range_list_handler.h b/src/common/dwarf_range_list_handler.h
index 2adb2f9..5db5d1e 100644
--- a/src/common/dwarf_range_list_handler.h
+++ b/src/common/dwarf_range_list_handler.h
@@ -51,25 +51,18 @@
 
 class DwarfRangeListHandler: public dwarf2reader::RangeListHandler {
  public:
-  DwarfRangeListHandler(uint64_t base_address, vector<Module::Range>* ranges)
-      : base_address_(base_address), ranges_(ranges) { }
+  DwarfRangeListHandler(vector<Module::Range>* ranges)
+      : ranges_(ranges) { }
 
   ~DwarfRangeListHandler() { }
 
   // Add a range to the list
   void AddRange(uint64_t begin, uint64_t end);
 
-  // Record the new base address and use it for the following entries
-  void SetBaseAddress(uint64_t base_address);
-
   // Sort the ranges so that they are in ascending order of starting address
   void Finish();
 
  private:
-  // The current PC to add to every entry, this can be overridden by a special
-  // list entry
-  uint64_t base_address_;
-
   // The list of ranges to be populated
   vector<Module::Range>* ranges_;
 };
diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc
index 8ecf0bc..0514f5a 100644
--- a/src/common/linux/dump_symbols.cc
+++ b/src/common/linux/dump_symbols.cc
@@ -232,22 +232,20 @@
 // owned by a function) with the results.
 class DumperRangesHandler : public DwarfCUToModule::RangesHandler {
  public:
-  DumperRangesHandler(const uint8_t* buffer, uint64_t size,
-                      dwarf2reader::ByteReader* reader)
-      : buffer_(buffer), size_(size), reader_(reader) { }
+  DumperRangesHandler(dwarf2reader::ByteReader* reader) :
+      reader_(reader) { }
 
-  bool ReadRanges(uint64_t offset, Module::Address base_address,
-                  vector<Module::Range>* ranges) {
-    DwarfRangeListHandler handler(base_address, ranges);
-    dwarf2reader::RangeListReader rangelist_reader(buffer_, size_, reader_,
-                                                   &handler);
-
-    return rangelist_reader.ReadRangeList(offset);
+  bool ReadRanges(
+      enum dwarf2reader::DwarfForm form, uint64_t data,
+      dwarf2reader::RangeListReader::CURangesInfo* cu_info,
+      vector<Module::Range>* ranges) {
+    DwarfRangeListHandler handler(ranges);
+    dwarf2reader::RangeListReader range_list_reader(reader_, cu_info,
+                                                    &handler);
+    return range_list_reader.ReadRanges(form, data);
   }
 
  private:
-  const uint8_t* buffer_;
-  uint64_t size_;
   dwarf2reader::ByteReader* reader_;
 };
 
@@ -313,17 +311,8 @@
     file_context.AddSectionToSectionMap(name, contents, section->sh_size);
   }
 
-  // Optional .debug_ranges reader
-  scoped_ptr<DumperRangesHandler> ranges_handler;
-  dwarf2reader::SectionMap::const_iterator ranges_entry =
-      file_context.section_map().find(".debug_ranges");
-  if (ranges_entry != file_context.section_map().end()) {
-    const std::pair<const uint8_t*, uint64_t>& ranges_section =
-      ranges_entry->second;
-    ranges_handler.reset(
-      new DumperRangesHandler(ranges_section.first, ranges_section.second,
-                              &byte_reader));
-  }
+  // .debug_ranges and .debug_rnglists reader
+  DumperRangesHandler ranges_handler(&byte_reader);
 
   // Parse all the compilation units in the .debug_info section.
   DumperLineToModule line_to_module(&byte_reader);
@@ -341,7 +330,7 @@
     // data that was found.
     DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset);
     DwarfCUToModule root_handler(&file_context, &line_to_module,
-                                 ranges_handler.get(), &reporter);
+                                 &ranges_handler, &reporter);
     // Make a Dwarf2Handler that drives the DIEHandler.
     dwarf2reader::DIEDispatcher die_dispatcher(&root_handler);
     // Make a DWARF parser for the compilation unit at OFFSET.
diff --git a/src/common/mac/dump_syms.cc b/src/common/mac/dump_syms.cc
index 24bcd65..672323c 100644
--- a/src/common/mac/dump_syms.cc
+++ b/src/common/mac/dump_syms.cc
@@ -311,22 +311,20 @@
 class DumpSymbols::DumperRangesHandler:
       public DwarfCUToModule::RangesHandler {
  public:
-  DumperRangesHandler(const uint8_t* buffer, uint64_t size,
-                      dwarf2reader::ByteReader* reader)
-      : buffer_(buffer), size_(size), reader_(reader) { }
+  DumperRangesHandler(dwarf2reader::ByteReader* reader) :
+      reader_(reader) { }
 
-  bool ReadRanges(uint64_t offset, Module::Address base_address,
-                  vector<Module::Range>* ranges) {
-    DwarfRangeListHandler handler(base_address, ranges);
-    dwarf2reader::RangeListReader rangelist_reader(buffer_, size_, reader_,
-                                                   &handler);
-
-    return rangelist_reader.ReadRangeList(offset);
+  bool ReadRanges(
+      enum dwarf2reader::DwarfForm form, uint64_t data,
+      dwarf2reader::RangeListReader::CURangesInfo* cu_info,
+      vector<Module::Range>* ranges) {
+    DwarfRangeListHandler handler(ranges);
+    dwarf2reader::RangeListReader range_list_reader(reader_, cu_info,
+                                                    &handler);
+    return range_list_reader.ReadRanges(form, data);
   }
 
  private:
-  const uint8_t* buffer_;
-  uint64_t size_;
   dwarf2reader::ByteReader* reader_;
 };
 
@@ -456,17 +454,8 @@
   // Build a line-to-module loader for the root handler to use.
   DumperLineToModule line_to_module(&byte_reader);
 
-  // Optional .debug_ranges reader
-  scoped_ptr<DumperRangesHandler> ranges_handler;
-  dwarf2reader::SectionMap::const_iterator ranges_entry =
-      file_context.section_map().find("__debug_ranges");
-  if (ranges_entry != file_context.section_map().end()) {
-    const std::pair<const uint8_t*, uint64_t>& ranges_section =
-      ranges_entry->second;
-    ranges_handler.reset(
-      new DumperRangesHandler(ranges_section.first, ranges_section.second,
-                              &byte_reader));
-  }
+  // .debug_ranges and .debug_rngslists reader
+  DumperRangesHandler ranges_handler(&byte_reader);
 
   // Walk the __debug_info section, one compilation unit at a time.
   uint64_t debug_info_length = debug_info_section.second;
@@ -476,7 +465,7 @@
     DwarfCUToModule::WarningReporter reporter(selected_object_name_,
                                               offset);
     DwarfCUToModule root_handler(&file_context, &line_to_module,
-                                 ranges_handler.get(), &reporter);
+                                 &ranges_handler, &reporter);
     // Make a Dwarf2Handler that drives our DIEHandler.
     dwarf2reader::DIEDispatcher die_dispatcher(&root_handler);
     // Make a DWARF parser for the compilation unit at OFFSET.