Add and handle various dwarf5 addrx forms.

Given the almost nonexistent direct dwarfreader tests, I think
the best way to test these dwarf5 additions will be to add a full
dwarf5 compilation unit similar to the ones used incidentally in
the other tests. But I can't do that until enough dwarf5 is
correctly implemented.

Change-Id: I3418bda7212ae85c4b67232a2ab8fea9b9ca5d42
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/2258838
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/src/common/dwarf/bytereader-inl.h b/src/common/dwarf/bytereader-inl.h
index f4c068a..235d75e 100644
--- a/src/common/dwarf/bytereader-inl.h
+++ b/src/common/dwarf/bytereader-inl.h
@@ -50,6 +50,17 @@
   }
 }
 
+inline uint64_t ByteReader::ReadThreeBytes(const uint8_t* buffer) const {
+  const uint32_t buffer0 = buffer[0];
+  const uint32_t buffer1 = buffer[1];
+  const uint32_t buffer2 = buffer[2];
+  if (endian_ == ENDIANNESS_LITTLE) {
+    return buffer0 | buffer1 << 8 | buffer2 << 16;
+  } else {
+    return buffer2 | buffer1 << 8 | buffer0 << 16;
+  }
+}
+
 inline uint64_t ByteReader::ReadFourBytes(const uint8_t *buffer) const {
   const uint32_t buffer0 = buffer[0];
   const uint32_t buffer1 = buffer[1];
diff --git a/src/common/dwarf/bytereader.h b/src/common/dwarf/bytereader.h
index 2b37a12..d0c3b96 100644
--- a/src/common/dwarf/bytereader.h
+++ b/src/common/dwarf/bytereader.h
@@ -68,6 +68,11 @@
   // number, using this ByteReader's endianness.
   uint16_t ReadTwoBytes(const uint8_t *buffer) const;
 
+  // Read three bytes from BUFFER and return them as an unsigned 64 bit
+  // number, using this ByteReader's endianness. DWARF 5 uses this encoding
+  // for various index-related DW_FORMs.
+  uint64_t ReadThreeBytes(const uint8_t* buffer) const;
+
   // Read four bytes from BUFFER and return them as an unsigned 32 bit
   // number, using this ByteReader's endianness. This function returns
   // a uint64_t so that it is compatible with ReadAddress and
diff --git a/src/common/dwarf/dwarf2enums.h b/src/common/dwarf/dwarf2enums.h
index f5ccce9..bed7032 100644
--- a/src/common/dwarf/dwarf2enums.h
+++ b/src/common/dwarf/dwarf2enums.h
@@ -164,6 +164,12 @@
   DW_FORM_strx3 = 0x27,
   DW_FORM_strx4 = 0x28,
 
+  DW_FORM_addrx = 0x1b,
+  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
diff --git a/src/common/dwarf/dwarf2reader.cc b/src/common/dwarf/dwarf2reader.cc
index 4f0c31d..fbfff27 100644
--- a/src/common/dwarf/dwarf2reader.cc
+++ b/src/common/dwarf/dwarf2reader.cc
@@ -182,17 +182,21 @@
 
     case DW_FORM_flag_present:
       return start;
+    case DW_FORM_addrx1:
     case DW_FORM_data1:
     case DW_FORM_flag:
     case DW_FORM_ref1:
     case DW_FORM_strx1:
       return start + 1;
+    case DW_FORM_addrx2:
     case DW_FORM_ref2:
     case DW_FORM_data2:
     case DW_FORM_strx2:
       return start + 2;
+    case DW_FORM_addrx3:
     case DW_FORM_strx3:
       return start + 3;
+    case DW_FORM_addrx4:
     case DW_FORM_ref4:
     case DW_FORM_data4:
     case DW_FORM_strx4:
@@ -208,6 +212,7 @@
     case DW_FORM_strx:
     case DW_FORM_GNU_str_index:
     case DW_FORM_GNU_addr_index:
+    case DW_FORM_addrx:
       reader_->ReadUnsignedLEB128(start, &len);
       return start + len;
 
@@ -549,8 +554,7 @@
       return start + 2;
     }
     case DW_FORM_strx3: {
-      uint64_t str_index = reader_->ReadTwoBytes(start);
-      str_index *= reader_->ReadOneByte(start + 2);
+      uint64_t str_index = reader_->ReadThreeBytes(start);
       ProcessFormStringIndex(dieoffset, attr, form, str_index);
       return start + 3;
     }
@@ -560,14 +564,27 @@
       return start + 4;
     }
 
-    case DW_FORM_GNU_addr_index: {
-      uint64_t addr_index = reader_->ReadUnsignedLEB128(start, &len);
-      const uint8_t* addr_ptr =
-          addr_buffer_ + addr_base_ + addr_index * reader_->AddressSize();
-      ProcessAttributeUnsigned(dieoffset, attr, form,
-                               reader_->ReadAddress(addr_ptr));
+    case DW_FORM_addrx:
+    case DW_FORM_GNU_addr_index:
+      ProcessAttributeAddrIndex(
+          dieoffset, attr, form, reader_->ReadUnsignedLEB128(start, &len));
       return start + len;
-    }
+    case DW_FORM_addrx1:
+      ProcessAttributeAddrIndex(
+          dieoffset, attr, form, reader_->ReadOneByte(start));
+      return start + 1;
+    case DW_FORM_addrx2:
+      ProcessAttributeAddrIndex(
+          dieoffset, attr, form, reader_->ReadTwoBytes(start));
+      return start + 2;
+    case DW_FORM_addrx3:
+      ProcessAttributeAddrIndex(
+          dieoffset, attr, form, reader_->ReadThreeBytes(start));
+      return start + 3;
+    case DW_FORM_addrx4:
+      ProcessAttributeAddrIndex(
+          dieoffset, attr, form, reader_->ReadFourBytes(start));
+      return start + 4;
   }
   fprintf(stderr, "Unhandled form type\n");
   return NULL;
diff --git a/src/common/dwarf/dwarf2reader.h b/src/common/dwarf/dwarf2reader.h
index 51d9228..593f5c0 100644
--- a/src/common/dwarf/dwarf2reader.h
+++ b/src/common/dwarf/dwarf2reader.h
@@ -499,6 +499,18 @@
     handler_->ProcessAttributeString(offset, attr, form, data);
   }
 
+  // Called to handle common portions of DW_FORM_addrx and variations, as well
+  // as DW_FORM_GNU_addr_index.
+  void ProcessAttributeAddrIndex(uint64_t offset,
+                                 enum DwarfAttribute attr,
+                                 enum DwarfForm form,
+                                 uint64_t addr_index) {
+    const uint8_t* addr_ptr =
+        addr_buffer_ + addr_base_ + addr_index * reader_->AddressSize();
+    ProcessAttributeUnsigned(
+        offset, attr, form, reader_->ReadAddress(addr_ptr));
+  }
+
   // Processes all DIEs for this compilation unit
   void ProcessDIEs();
 
diff --git a/src/common/dwarf_cu_to_module.cc b/src/common/dwarf_cu_to_module.cc
index d6e7976..24bbf83 100644
--- a/src/common/dwarf_cu_to_module.cc
+++ b/src/common/dwarf_cu_to_module.cc
@@ -593,7 +593,12 @@
   if (!ranges_) {
     // 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) {
+        high_pc_form_ != dwarf2reader::DW_FORM_GNU_addr_index &&
+        high_pc_form_ != dwarf2reader::DW_FORM_addrx &&
+        high_pc_form_ != dwarf2reader::DW_FORM_addrx1 &&
+        high_pc_form_ != dwarf2reader::DW_FORM_addrx2 &&
+        high_pc_form_ != dwarf2reader::DW_FORM_addrx3 &&
+        high_pc_form_ != dwarf2reader::DW_FORM_addrx4) {
       high_pc_ += low_pc_;
     }