Add support for dwarf5 line tables.

Change-Id: I2c0cd0e7163502e52fbf0745b611befb2e219071
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/2276193
Reviewed-by: Sterling Augustine <saugustine@google.com>
diff --git a/src/common/dwarf/dwarf2enums.h b/src/common/dwarf/dwarf2enums.h
index 2b93aba..7bd3979 100644
--- a/src/common/dwarf/dwarf2enums.h
+++ b/src/common/dwarf/dwarf2enums.h
@@ -316,6 +316,14 @@
   DW_AT_PGI_lstride  = 0x3a02
 };
 
+// Line number content type codes (DWARF 5).
+enum DwarfLineNumberContentType {
+  DW_LNCT_path = 1,
+  DW_LNCT_directory_index = 2,
+  DW_LNCT_timestamp = 3,
+  DW_LNCT_size = 4,
+  DW_LNCT_MD5 = 5,
+};
 
 // Line number opcodes.
 enum DwarfLineNumberOps {
diff --git a/src/common/dwarf/dwarf2reader.cc b/src/common/dwarf/dwarf2reader.cc
index a7eedaa..ad82a5f 100644
--- a/src/common/dwarf/dwarf2reader.cc
+++ b/src/common/dwarf/dwarf2reader.cc
@@ -1039,10 +1039,17 @@
 }
 
 LineInfo::LineInfo(const uint8_t *buffer, uint64_t buffer_length,
-                   ByteReader* reader, LineInfoHandler* handler):
-    handler_(handler), reader_(reader), buffer_(buffer) {
+                   ByteReader* reader, const uint8_t* string_buffer,
+                   size_t string_buffer_length,
+                   const uint8_t* line_string_buffer,
+                   size_t line_string_buffer_length, LineInfoHandler* handler):
+    handler_(handler), reader_(reader), buffer_(buffer),
+    string_buffer_(string_buffer),
+    line_string_buffer_(line_string_buffer) {
 #ifndef NDEBUG
   buffer_length_ = buffer_length;
+  string_buffer_length_ = string_buffer_length;
+  line_string_buffer_length_ = line_string_buffer_length;
 #endif
   header_.std_opcode_lengths = NULL;
 }
@@ -1053,6 +1060,128 @@
   return after_header_ - buffer_;
 }
 
+void LineInfo::ReadTypesAndForms(const uint8_t** lineptr,
+                                 uint32_t* content_types,
+                                 uint32_t* content_forms,
+                                 uint32_t max_types,
+                                 uint32_t* format_count) {
+  size_t len;
+
+  uint32_t count = reader_->ReadUnsignedLEB128(*lineptr, &len);
+  *lineptr += len;
+  if (count < 1 || count > max_types) {
+    return;
+  }
+  for (uint32_t col = 0; col < count; ++col) {
+    content_types[col] = reader_->ReadUnsignedLEB128(*lineptr, &len);
+    *lineptr += len;
+    content_forms[col] = reader_->ReadUnsignedLEB128(*lineptr, &len);
+    *lineptr += len;
+  }
+  *format_count = count;
+}
+
+const char* LineInfo::ReadStringForm(uint32_t form, const uint8_t** lineptr) {
+  const char* name = nullptr;
+  if (form == DW_FORM_string) {
+    name = reinterpret_cast<const char*>(*lineptr);
+    *lineptr += strlen(name) + 1;
+    return name;
+  } else if (form == DW_FORM_strp) {
+    uint64_t offset = reader_->ReadOffset(*lineptr);
+    assert(offset < string_buffer_length_);
+    *lineptr += reader_->OffsetSize();
+    if (string_buffer_ != nullptr) {
+      name = reinterpret_cast<const char*>(string_buffer_) + offset;
+      return name;
+    }
+  } else if (form == DW_FORM_line_strp) {
+    uint64_t offset = reader_->ReadOffset(*lineptr);
+    assert(offset < line_string_buffer_length_);
+    *lineptr += reader_->OffsetSize();
+    if (line_string_buffer_ != nullptr) {
+      name = reinterpret_cast<const char*>(line_string_buffer_) + offset;
+      return name;
+    }
+  }
+  // Shouldn't be called with a non-string-form, and
+  // if there is a string form but no string buffer,
+  // that is a problem too.
+  assert(0);
+  return nullptr;
+}
+
+uint64_t LineInfo::ReadUnsignedData(uint32_t form, const uint8_t** lineptr) {
+  size_t len;
+  uint64_t value;
+
+  switch (form) {
+    case DW_FORM_data1:
+      value = reader_->ReadOneByte(*lineptr);
+      *lineptr += 1;
+      return value;
+    case DW_FORM_data2:
+      value = reader_->ReadTwoBytes(*lineptr);
+      *lineptr += 2;
+      return value;
+    case DW_FORM_data4:
+      value = reader_->ReadFourBytes(*lineptr);
+      *lineptr += 4;
+      return value;
+    case DW_FORM_data8:
+      value = reader_->ReadEightBytes(*lineptr);
+      *lineptr += 8;
+      return value;
+    case DW_FORM_udata:
+      value = reader_->ReadUnsignedLEB128(*lineptr, &len);
+      *lineptr += len;
+      return value;
+    default:
+      fprintf(stderr, "Unrecognized data form.");
+      return 0;
+  }
+}
+
+void LineInfo::ReadFileRow(const uint8_t** lineptr,
+                           const uint32_t* content_types,
+                           const uint32_t* content_forms, uint32_t row,
+                           uint32_t format_count) {
+  const char* filename = nullptr;
+  uint64_t dirindex = 0;
+  uint64_t mod_time = 0;
+  uint64_t filelength = 0;
+
+  for (uint32_t col = 0; col < format_count; ++col) {
+    switch (content_types[col]) {
+      case DW_LNCT_path:
+        filename = ReadStringForm(content_forms[col], lineptr);
+        break;
+      case DW_LNCT_directory_index:
+        dirindex = ReadUnsignedData(content_forms[col], lineptr);
+        break;
+      case DW_LNCT_timestamp:
+        mod_time = ReadUnsignedData(content_forms[col], lineptr);
+        break;
+      case DW_LNCT_size:
+        filelength = ReadUnsignedData(content_forms[col], lineptr);
+        break;
+      case DW_LNCT_MD5:
+        // MD5 entries help a debugger sort different versions of files with
+        // the same name.  It is always paired with a DW_FORM_data16 and is
+        // unused in this case.
+        lineptr += 16;
+        break;
+      default:
+        fprintf(stderr, "Unrecognized form in line table header. %d\n",
+                content_types[col]);
+        assert(false);
+        break;
+    }
+  }
+  assert(filename != nullptr);
+  handler_->DefineFile(filename, row, dirindex, mod_time, filelength);
+}
+
 // The header for a debug_line section is mildly complicated, because
 // the line info is very tightly encoded.
 void LineInfo::ReadHeader() {
@@ -1067,12 +1196,24 @@
   assert(buffer_ + initial_length_size + header_.total_length <=
         buffer_ + buffer_length_);
 
-  // Address size *must* be set by CU ahead of time.
-  assert(reader_->AddressSize() != 0);
 
   header_.version = reader_->ReadTwoBytes(lineptr);
   lineptr += 2;
 
+  if (header_.version >= 5) {
+    uint8_t address_size = reader_->ReadOneByte(lineptr);
+    reader_->SetAddressSize(address_size);
+    lineptr += 1;
+    uint8_t segment_selector_size = reader_->ReadOneByte(lineptr);
+    if (segment_selector_size != 0) {
+      fprintf(stderr,"No support for segmented memory.");
+    }
+    lineptr += 1;
+  } else {
+    // Address size *must* be set by CU ahead of time.
+    assert(reader_->AddressSize() != 0);
+  }
+
   header_.prologue_length = reader_->ReadOffset(lineptr);
   lineptr += reader_->OffsetSize();
 
@@ -1106,41 +1247,84 @@
     lineptr += 1;
   }
 
-  // It is legal for the directory entry table to be empty.
-  if (*lineptr) {
-    uint32_t dirindex = 1;
-    while (*lineptr) {
-      const char *dirname = reinterpret_cast<const char *>(lineptr);
-      handler_->DefineDir(dirname, dirindex);
-      lineptr += strlen(dirname) + 1;
-      dirindex++;
+  if (header_.version <= 4) {
+    // Directory zero is assumed to be the compilation directory and special
+    // cased where used. It is not actually stored in the dwarf data. But an
+    // empty entry here avoids off-by-one errors elsewhere in the code.
+    handler_->DefineDir("", 0);
+    // It is legal for the directory entry table to be empty.
+    if (*lineptr) {
+      uint32_t dirindex = 1;
+      while (*lineptr) {
+        const char* dirname = reinterpret_cast<const char*>(lineptr);
+        handler_->DefineDir(dirname, dirindex);
+        lineptr += strlen(dirname) + 1;
+        dirindex++;
+      }
     }
-  }
-  lineptr++;
+    lineptr++;
+    // It is also legal for the file entry table to be empty.
 
-  // It is also legal for the file entry table to be empty.
-  if (*lineptr) {
-    uint32_t fileindex = 1;
+    // Similarly for file zero.
+    handler_->DefineFile("", 0, 0, 0, 0);
+    if (*lineptr) {
+      uint32_t fileindex = 1;
+      size_t len;
+      while (*lineptr) {
+        const char* filename = ReadStringForm(DW_FORM_string, &lineptr);
+
+        uint64_t dirindex = reader_->ReadUnsignedLEB128(lineptr, &len);
+        lineptr += len;
+
+        uint64_t mod_time = reader_->ReadUnsignedLEB128(lineptr, &len);
+        lineptr += len;
+
+        uint64_t filelength = reader_->ReadUnsignedLEB128(lineptr, &len);
+        lineptr += len;
+        handler_->DefineFile(filename, fileindex,
+                             static_cast<uint32_t>(dirindex), mod_time,
+                             filelength);
+        fileindex++;
+      }
+    }
+    lineptr++;
+  } else {
+    // Read the DWARF-5 directory table.
+
+    // Dwarf5 supports five different types and forms per directory- and
+    // file-table entry. Theoretically, there could be duplicate entries
+    // in this table, but that would be quite unusual.
+    static const uint32_t kMaxTypesAndForms = 5;
+    uint32_t content_types[kMaxTypesAndForms];
+    uint32_t content_forms[kMaxTypesAndForms];
+    uint32_t format_count;
     size_t len;
-    while (*lineptr) {
-      const char *filename = reinterpret_cast<const char *>(lineptr);
-      lineptr += strlen(filename) + 1;
 
-      uint64_t dirindex = reader_->ReadUnsignedLEB128(lineptr, &len);
-      lineptr += len;
+    ReadTypesAndForms(&lineptr, content_types, content_forms, kMaxTypesAndForms,
+                      &format_count);
+    uint32_t entry_count = reader_->ReadUnsignedLEB128(lineptr, &len);
+    lineptr += len;
+    for (uint32_t row = 0; row < entry_count; ++row) {
+      const char* dirname = nullptr;
+      for (uint32_t col = 0; col < format_count; ++col) {
+        // The path is the only relevant content type for this implementation.
+        if (content_types[col] == DW_LNCT_path) {
+          dirname = ReadStringForm(content_forms[col], &lineptr);
+        }
+      }
+      handler_->DefineDir(dirname, row);
+    }
 
-      uint64_t mod_time = reader_->ReadUnsignedLEB128(lineptr, &len);
-      lineptr += len;
+    // Read the DWARF-5 filename table.
+    ReadTypesAndForms(&lineptr, content_types, content_forms, kMaxTypesAndForms,
+                      &format_count);
+    entry_count = reader_->ReadUnsignedLEB128(lineptr, &len);
+    lineptr += len;
 
-      uint64_t filelength = reader_->ReadUnsignedLEB128(lineptr, &len);
-      lineptr += len;
-      handler_->DefineFile(filename, fileindex, static_cast<uint32_t>(dirindex), 
-                           mod_time, filelength);
-      fileindex++;
+    for (uint32_t row = 0; row < entry_count; ++row) {
+      ReadFileRow(&lineptr, content_types, content_forms, row, format_count);
     }
   }
-  lineptr++;
-
   after_header_ = lineptr;
 }
 
diff --git a/src/common/dwarf/dwarf2reader.h b/src/common/dwarf/dwarf2reader.h
index 228c867..e52f74c 100644
--- a/src/common/dwarf/dwarf2reader.h
+++ b/src/common/dwarf/dwarf2reader.h
@@ -98,8 +98,10 @@
   // to the beginning and length of the line information to read.
   // Reader is a ByteReader class that has the endianness set
   // properly.
-  LineInfo(const uint8_t *buffer_, uint64_t buffer_length,
-           ByteReader* reader, LineInfoHandler* handler);
+  LineInfo(const uint8_t* buffer, uint64_t buffer_length,
+           ByteReader* reader, const uint8_t* string_buffer,
+           size_t string_buffer_length, const uint8_t* line_string_buffer,
+           size_t line_string_buffer_length, LineInfoHandler* handler);
 
   virtual ~LineInfo() {
     if (header_.std_opcode_lengths) {
@@ -137,15 +139,32 @@
   // Reads the DWARF2/3 line information
   void ReadLines();
 
+  // Read the DWARF5 types and forms for the file and directory tables.
+  void ReadTypesAndForms(const uint8_t** lineptr, uint32_t* content_types,
+                         uint32_t* content_forms, uint32_t max_types,
+                         uint32_t* format_count);
+
+  // Read a row from the dwarf5 LineInfo file table.
+  void ReadFileRow(const uint8_t** lineptr, const uint32_t* content_types,
+                   const uint32_t* content_forms, uint32_t row,
+                   uint32_t format_count);
+
+  // Read and return the data at *lineptr according to form. Advance
+  // *lineptr appropriately.
+  uint64_t ReadUnsignedData(uint32_t form, const uint8_t** lineptr);
+
+  // Read and return the data at *lineptr according to form. Advance
+  // *lineptr appropriately.
+  const char* ReadStringForm(uint32_t form, const uint8_t** lineptr);
+
   // The associated handler to call processing functions in
   LineInfoHandler* handler_;
 
   // The associated ByteReader that handles endianness issues for us
   ByteReader* reader_;
 
-  // A DWARF2/3 line info header.  This is not the same size as
-  // in the actual file, as the one in the file may have a 32 bit or
-  // 64 bit lengths
+  // A DWARF line info header.  This is not the same size as in the actual file,
+  // as the one in the file may have a 32 bit or 64 bit lengths
 
   struct LineInfoHeader header_;
 
@@ -156,6 +175,13 @@
 #ifndef NDEBUG
   uint64_t buffer_length_;
 #endif
+  // Convenience pointers into .debug_str and .debug_line_str. These exactly
+  // correspond to those in the compilation unit.
+  const uint8_t* string_buffer_;
+  uint64_t string_buffer_length_;
+  const uint8_t* line_string_buffer_;
+  uint64_t line_string_buffer_length_;
+
   const uint8_t *after_header_;
 };
 
diff --git a/src/common/dwarf/dwarf2reader_lineinfo_unittest.cc b/src/common/dwarf/dwarf2reader_lineinfo_unittest.cc
new file mode 100644
index 0000000..99fc1a0
--- /dev/null
+++ b/src/common/dwarf/dwarf2reader_lineinfo_unittest.cc
@@ -0,0 +1,187 @@
+// Copyright (c) 2020, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Sterling Augustine <saugustine@google.com>
+
+// dwarf2reader_lineinfo_unittest.cc: Unit tests for dwarf2reader::LineInfo
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "common/dwarf/bytereader.h"
+#include "common/dwarf/dwarf2reader.h"
+#include "google_breakpad/common/breakpad_types.h"
+
+using std::vector;
+using testing::InSequence;
+using testing::Return;
+using testing::Sequence;
+using testing::Test;
+using testing::_;
+
+using namespace dwarf2reader;
+
+namespace {
+
+const uint8_t dwarf5_line_program[] = {
+  0x40, 0x0, 0x0, 0x0,  // unit_length (end - begin)
+  // begin
+  0x05, 0x0,  // version
+  0x8,  // address_size
+  0x0,  // segment_selector_size
+  0x26, 0x0, 0x0, 0x0, // header_length (end_header_end - begin_header)
+  // begin_header:
+  0x1,  // minimum_instruction_length
+  0x1,  // maximum_operations_per_instruction
+  0x1,  // default_is_stmt
+  0xfb, // line_base
+  0xe,  // line_range
+  0xd,  // opcode_base and lengths
+  0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1,
+  0x1,  // directory entry format count
+  DW_LNCT_path, DW_FORM_strp,
+  0x1,  // directories count
+  0x1, 0x0, 0x0, 0x0, // offset into .debug_line_str
+  0x2,  // file_name_entry_format_count
+  DW_LNCT_directory_index, DW_FORM_data1,
+  DW_LNCT_path, DW_FORM_line_strp,
+  0x1,  // filename count
+  0x0,  // directory index
+  0x1, 0x0, 0x0, 0x0, // offset into .debug_str
+  // end_header
+  DW_LNS_set_file, 0x0,
+  //  set address to 0x0
+  0x0, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  // Advance Address by 0 and line by 3
+  0x15,
+  // Advance PC by 1
+  0x2, 0x1,
+  0x0,
+  DW_LNE_end_sequence,
+  DW_LNE_end_sequence,
+  // end
+};
+
+const uint8_t dwarf4_line_program[] = {
+  0x37, 0x0, 0x0, 0x0,  // unit_length (end - begin)
+  // begin
+  0x04, 0x0,  // version
+  0x1d, 0x0, 0x0, 0x0, // header_length (end_header - begin_header)
+  // begin_header:
+  0x1,  // minimum_instruction_length
+  0x1,  // maximum_operations_per_instruction
+  0x1,  // default_is_stmt
+  0xfb, // line_base
+  0xe,  // line_range
+  0xd,  // opcode_base and lengths
+  0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1,
+  '/', 'a', '\0',  // directory entry 1 (zeroth entry implied)
+  '\0', // end of directory table
+  'b', '/', 'c', '\0',  // file entry 1 (zeroth entry implied)
+  0, // file 1 directory
+  0, // file 1 modification time
+  0, // file 1 length
+  '\0', // end of file table
+  // end_header
+  DW_LNS_set_file, 0x0,
+  //  set address to 0x0
+  0x0, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  // Advance Address by 0 and line by 3
+  0x15,
+  // Advance PC by 1
+  0x2, 0x1,
+  0x0,
+  DW_LNE_end_sequence,
+  DW_LNE_end_sequence,
+  // end
+};
+
+class MockLineInfoHandler: public LineInfoHandler {
+ public:
+  MOCK_METHOD(void, DefineDir, (const string&, uint32_t dir_num), (override));
+  MOCK_METHOD(void, DefineFile, (const string& name, int32_t file_num,
+                                 uint32_t dir_num, uint64_t mod_time,
+                                 uint64_t length), (override));
+  MOCK_METHOD(void, AddLine, (uint64_t address, uint64_t length,
+                              uint32_t file_num, uint32_t line_num,
+                              uint32_t column_num), (override));
+};
+
+const uint8_t string_section[] = {'x', '/', 'a', '\0'};
+const uint8_t line_string_section[] = {'x', 'b', '/', 'c', '\0' };
+
+struct LineProgram: public Test {
+  MockLineInfoHandler handler_;
+};
+
+TEST_F(LineProgram, ReadLinesDwarf5) {
+  ByteReader byte_reader(ENDIANNESS_LITTLE);
+  // LineTables don't specify the offset size like Compilation Units do.
+  byte_reader.SetOffsetSize(4);
+  LineInfo line_reader(dwarf5_line_program,
+                       sizeof(dwarf5_line_program),
+                       &byte_reader,
+                       string_section,
+                       sizeof(string_section),
+                       line_string_section,
+                       sizeof(line_string_section),
+                       &handler_);
+  EXPECT_CALL(handler_, DefineDir("/a", 0)).Times(1);
+  EXPECT_CALL(handler_, DefineFile("b/c", 0, 0, 0, 0)).Times(1);
+  EXPECT_CALL(handler_, AddLine(0, 1, 0, 4, 0)).Times(1);
+  EXPECT_EQ(line_reader.Start(), sizeof(dwarf5_line_program));
+}
+
+TEST_F(LineProgram, ReadLinesDwarf4) {
+  ByteReader byte_reader(ENDIANNESS_LITTLE);
+  // LineTables don't specify the offset size like Compilation Units do.
+  byte_reader.SetOffsetSize(4);
+  // dwarf4 line info headers don't encode the address size.
+  byte_reader.SetAddressSize(8);
+  LineInfo line_reader(dwarf4_line_program,
+                       sizeof(dwarf5_line_program),
+                       &byte_reader,
+                       // dwarf4 line tables can't access the string sections
+                       // so pass values likely to make assertions fail if
+                       // the code uses them improperly.
+                       nullptr, 0, nullptr, 0,
+                       &handler_);
+  EXPECT_CALL(handler_, DefineDir("", 0)).Times(1);
+  EXPECT_CALL(handler_, DefineDir("/a", 1)).Times(1);
+  EXPECT_CALL(handler_, DefineFile("", 0, 0, 0, 0)).Times(1);
+  EXPECT_CALL(handler_, DefineFile("b/c", 1, 0, 0, 0)).Times(1);
+  EXPECT_CALL(handler_, AddLine(0, 1, 0, 4, 0)).Times(1);
+  EXPECT_EQ(line_reader.Start(), sizeof(dwarf4_line_program));
+}
+
+}  // anonymous namespace
diff --git a/src/common/dwarf/functioninfo.cc b/src/common/dwarf/functioninfo.cc
index edd1bb7..28c4f93 100644
--- a/src/common/dwarf/functioninfo.cc
+++ b/src/common/dwarf/functioninfo.cc
@@ -51,15 +51,9 @@
                                      LineMap* linemap):linemap_(linemap),
                                                        files_(files),
                                                        dirs_(dirs) {
-  // The dirs and files are 1 indexed, so just make sure we put
-  // nothing in the 0 vector.
-  assert(dirs->size() == 0);
-  assert(files->size() == 0);
-  dirs->push_back("");
-  SourceFileInfo s;
-  s.name = "";
-  s.lowpc = ULLONG_MAX;
-  files->push_back(s);
+  // In dwarf4, the dirs and files are 1 indexed, and in dwarf5 they are zero
+  // indexed. This is handled in the LineInfo reader, so empty files are not
+  // needed here.
 }
 
 void CULineInfoHandler::DefineDir(const string& name, uint32_t dir_num) {
diff --git a/src/common/dwarf_cu_to_module.cc b/src/common/dwarf_cu_to_module.cc
index be044ce..ec2badc 100644
--- a/src/common/dwarf_cu_to_module.cc
+++ b/src/common/dwarf_cu_to_module.cc
@@ -940,14 +940,35 @@
     cu_context_->reporter->MissingSection(".debug_line");
     return;
   }
-  const uint8_t *section_start = map_entry->second.first;
-  uint64_t section_length = map_entry->second.second;
-  if (offset >= section_length) {
+  const uint8_t* line_section_start = map_entry->second.first + offset;
+  uint64_t line_section_length = map_entry->second.second;
+  if (offset >= line_section_length) {
     cu_context_->reporter->BadLineInfoOffset(offset);
     return;
   }
-  line_reader_->ReadProgram(section_start + offset, section_length - offset,
-                            cu_context_->file_context->module_, &lines_);
+  line_section_length -= offset;
+  // When reading line tables, string sections are never needed for dwarf4, and
+  // may or may not be needed by dwarf5, so no error if they are missing.
+  const uint8_t* string_section_start = nullptr;
+  uint64_t string_section_length = 0;
+  map_entry = dwarf2reader::GetSectionByName(section_map, ".debug_str");
+  if (map_entry != section_map.end()) {
+    string_section_start = map_entry->second.first + offset;
+    string_section_length = map_entry->second.second - offset;
+  }
+  const uint8_t* line_string_section_start = nullptr;
+  uint64_t line_string_section_length = 0;
+  map_entry = dwarf2reader::GetSectionByName(section_map, ".debug_line_str");
+  if (map_entry != section_map.end()) {
+    line_string_section_start = map_entry->second.first + offset;
+    line_string_section_length = map_entry->second.second - offset;
+    return;
+  }
+  line_reader_->ReadProgram(
+      line_section_start, line_section_length,
+      string_section_start, string_section_length,
+      line_string_section_start, line_string_section_length,
+      cu_context_->file_context->module_, &lines_);
 }
 
 namespace {
diff --git a/src/common/dwarf_cu_to_module.h b/src/common/dwarf_cu_to_module.h
index 2153bd9..b948aec 100644
--- a/src/common/dwarf_cu_to_module.h
+++ b/src/common/dwarf_cu_to_module.h
@@ -158,7 +158,11 @@
     // mappings, given a pointer to some DWARF line number data
     // PROGRAM, and an overestimate of its size. Add no zero-length
     // lines to LINES.
-    virtual void ReadProgram(const uint8_t *program, uint64_t length,
+    virtual void ReadProgram(const uint8_t* program, uint64_t length,
+                             const uint8_t* string_section,
+                             uint64_t string_section_length,
+                             const uint8_t* line_string_section,
+                             uint64_t line_string_length,
                              Module *module, vector<Module::Line> *lines) = 0;
   };
 
diff --git a/src/common/dwarf_cu_to_module_unittest.cc b/src/common/dwarf_cu_to_module_unittest.cc
index ed1d7c9..1b7e2bf 100644
--- a/src/common/dwarf_cu_to_module_unittest.cc
+++ b/src/common/dwarf_cu_to_module_unittest.cc
@@ -67,8 +67,12 @@
 class MockLineToModuleHandler: public DwarfCUToModule::LineToModuleHandler {
  public:
   MOCK_METHOD1(StartCompilationUnit, void(const string& compilation_dir));
-  MOCK_METHOD4(ReadProgram, void(const uint8_t *program, uint64_t length,
-                                 Module *module, vector<Module::Line> *lines));
+  MOCK_METHOD8(ReadProgram, void(const uint8_t* program, uint64_t length,
+                                 const uint8_t* string_section,
+                                 uint64_t string_section_length,
+                                 const uint8_t* line_string_section,
+                                 uint64_t line_string_section_length,
+                                 Module* module, vector<Module::Line>* lines));
 };
 
 class MockWarningReporter: public DwarfCUToModule::WarningReporter {
@@ -113,8 +117,12 @@
    public:
     explicit AppendLinesFunctor(
         const vector<Module::Line> *lines) : lines_(lines) { }
-    void operator()(const uint8_t *program, uint64_t length,
-                    Module *module, vector<Module::Line> *lines) {
+    void operator()(const uint8_t* program, uint64_t length,
+                    const uint8_t* string_section,
+                    uint64_t string_section_length,
+                    const uint8_t* line_string_section,
+                    uint64_t line_string_section_length,
+                    Module *module, vector<Module::Line>* lines) {
       lines->insert(lines->end(), lines_->begin(), lines_->end());
     }
    private:
@@ -147,7 +155,7 @@
     // By default, expect the line program reader not to be invoked. We
     // may override this in StartCU.
     EXPECT_CALL(line_reader_, StartCompilationUnit(_)).Times(0);
-    EXPECT_CALL(line_reader_, ReadProgram(_,_,_,_)).Times(0);
+    EXPECT_CALL(line_reader_, ReadProgram(_,_,_,_,_,_,_,_)).Times(0);
 
     // The handler will consult this section map to decide what to
     // pass to our line reader.
@@ -332,10 +340,10 @@
   if (!lines_.empty())
     EXPECT_CALL(line_reader_,
                 ReadProgram(&dummy_line_program_[0], dummy_line_size_,
+                            _,_,_,_,
                             &module_, _))
         .Times(AtMost(1))
         .WillOnce(DoAll(Invoke(appender_), Return()));
-
   ASSERT_TRUE(root_handler_
               .StartCompilationUnit(0x51182ec307610b51ULL, 0x81, 0x44,
                                     0x4241b4f33720dd5cULL, 3));
@@ -1509,7 +1517,7 @@
   DwarfCUToModule::FileContext fc("dwarf-filename", &m, true);
   EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
   MockLineToModuleHandler lr;
-  EXPECT_CALL(lr, ReadProgram(_,_,_,_)).Times(0);
+  EXPECT_CALL(lr, ReadProgram(_,_,_,_,_,_,_,_)).Times(0);
 
   // Kludge: satisfy reporter_'s expectation.
   reporter_.SetCUName("compilation-unit-name");
@@ -1568,7 +1576,7 @@
   DwarfCUToModule::FileContext fc("dwarf-filename", &m, false);
   EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
   MockLineToModuleHandler lr;
-  EXPECT_CALL(lr, ReadProgram(_,_,_,_)).Times(0);
+  EXPECT_CALL(lr, ReadProgram(_,_,_,_,_,_,_,_)).Times(0);
 
   // Kludge: satisfy reporter_'s expectation.
   reporter_.SetCUName("compilation-unit-name");
diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc
index b7e77ab..997977b 100644
--- a/src/common/linux/dump_symbols.cc
+++ b/src/common/linux/dump_symbols.cc
@@ -262,10 +262,18 @@
   void StartCompilationUnit(const string& compilation_dir) {
     compilation_dir_ = compilation_dir;
   }
-  void ReadProgram(const uint8_t *program, uint64_t length,
+  void ReadProgram(const uint8_t* program, uint64_t length,
+                   const uint8_t* string_section,
+                   uint64_t string_section_length,
+                   const uint8_t* line_string_section,
+                   uint64_t line_string_section_length,
                    Module* module, std::vector<Module::Line>* lines) {
     DwarfLineToModule handler(module, compilation_dir_, lines);
-    dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
+    dwarf2reader::LineInfo parser(program, length, byte_reader_,
+                                  string_section, string_section_length,
+                                  line_string_section,
+                                  line_string_section_length,
+                                  &handler);
     parser.Start();
   }
  private:
diff --git a/src/common/mac/dump_syms.cc b/src/common/mac/dump_syms.cc
index 3fbedd9..2734ff6 100644
--- a/src/common/mac/dump_syms.cc
+++ b/src/common/mac/dump_syms.cc
@@ -344,10 +344,15 @@
     compilation_dir_ = compilation_dir;
   }
 
-  void ReadProgram(const uint8_t *program, uint64_t length,
-                   Module *module, vector<Module::Line> *lines) {
+  void ReadProgram(const uint8_t* program, uint64_t length,
+                   const uint8_t* string_section,
+                   uint64_t string_section_length,
+                   const uint8_t* line_string_section,
+                   uint64_t line_string_section_length,
+                   Module* module, vector<Module::Line>* lines) {
     DwarfLineToModule handler(module, compilation_dir_, lines);
-    dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
+    dwarf2reader::LineInfo parser(program, length, byte_reader_,
+                                  nullptr, 0, nullptr, 0, &handler);
     parser.Start();
   }
  private: