[DWARFv5] Parse new line-table header format.
authorPaul Robinson <paul.robinson@sony.com>
Tue, 2 May 2017 21:40:47 +0000 (21:40 +0000)
committerPaul Robinson <paul.robinson@sony.com>
Tue, 2 May 2017 21:40:47 +0000 (21:40 +0000)
The directory and file tables now have form-based content descriptors.
Parse these and extract the per-directory/file records based on the
descriptors.  For now we support only DW_FORM_string (inline) for the
path names; follow-up work will add support for indirect forms (i.e.,
DW_FORM_strp, strx<N>, and line_strp).

Differential Revision: http://reviews.llvm.org/D32713

llvm-svn: 301978

llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h
llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp
llvm/test/DebugInfo/Inputs/dwarfdump-header.elf-x86-64
llvm/test/DebugInfo/Inputs/dwarfdump-header.s
llvm/test/DebugInfo/dwarfdump-header.test

index 9a57d2e..5cf3ce4 100644 (file)
@@ -44,6 +44,10 @@ public:
     uint64_t TotalLength;
     /// Version identifier for the statement information format.
     uint16_t Version;
+    /// In v5, size in bytes of an address (or segment offset).
+    uint8_t AddressSize;
+    /// In v5, size in bytes of a segment selector.
+    uint8_t SegSelectorSize;
     /// The number of bytes following the prologue_length field to the beginning
     /// of the first byte of the statement program itself.
     uint64_t PrologueLength;
index 634c431..45e92cf 100644 (file)
@@ -10,6 +10,7 @@
 #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
 #include "llvm/DebugInfo/DWARF/DWARFRelocMap.h"
 #include "llvm/Support/Dwarf.h"
 #include "llvm/Support/Format.h"
@@ -26,11 +27,19 @@ using namespace llvm;
 using namespace dwarf;
 
 typedef DILineInfoSpecifier::FileLineInfoKind FileLineInfoKind;
+namespace {
+struct ContentDescriptor {
+  dwarf::LineNumberEntryFormat Type;
+  dwarf::Form Form;
+};
+typedef SmallVector<ContentDescriptor, 4> ContentDescriptors;
+} // end anonmyous namespace
 
 DWARFDebugLine::Prologue::Prologue() { clear(); }
 
 void DWARFDebugLine::Prologue::clear() {
   TotalLength = Version = PrologueLength = 0;
+  AddressSize = SegSelectorSize = 0;
   MinInstLength = MaxOpsPerInst = DefaultIsStmt = LineBase = LineRange = 0;
   OpcodeBase = 0;
   IsDWARF64 = false;
@@ -43,6 +52,8 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS) const {
   OS << "Line table prologue:\n"
      << format("    total_length: 0x%8.8" PRIx64 "\n", TotalLength)
      << format("         version: %u\n", Version)
+     << format(Version >= 5 ? "    address_size: %u\n" : "", AddressSize)
+     << format(Version >= 5 ? " seg_select_size: %u\n" : "", SegSelectorSize)
      << format(" prologue_length: 0x%8.8" PRIx64 "\n", PrologueLength)
      << format(" min_inst_length: %u\n", MinInstLength)
      << format(Version >= 4 ? "max_ops_per_inst: %u\n" : "", MaxOpsPerInst)
@@ -74,6 +85,125 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS) const {
   }
 }
 
+// Parse v2-v4 directory and file tables.
+static void
+parseV2DirFileTables(DataExtractor DebugLineData, uint32_t *OffsetPtr,
+                     uint64_t EndPrologueOffset,
+                     std::vector<StringRef> &IncludeDirectories,
+                     std::vector<DWARFDebugLine::FileNameEntry> &FileNames) {
+  while (*OffsetPtr < EndPrologueOffset) {
+    StringRef S = DebugLineData.getCStrRef(OffsetPtr);
+    if (S.empty())
+      break;
+    IncludeDirectories.push_back(S);
+  }
+
+  while (*OffsetPtr < EndPrologueOffset) {
+    StringRef Name = DebugLineData.getCStrRef(OffsetPtr);
+    if (Name.empty())
+      break;
+    DWARFDebugLine::FileNameEntry FileEntry;
+    FileEntry.Name = Name;
+    FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr);
+    FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr);
+    FileEntry.Length = DebugLineData.getULEB128(OffsetPtr);
+    FileNames.push_back(FileEntry);
+  }
+}
+
+// Parse v5 directory/file entry content descriptions.
+// Returns the descriptors, or an empty vector if we did not find a path or
+// ran off the end of the prologue.
+static ContentDescriptors
+parseV5EntryFormat(DataExtractor DebugLineData, uint32_t *OffsetPtr,
+                   uint64_t EndPrologueOffset) {
+  ContentDescriptors Descriptors;
+  int FormatCount = DebugLineData.getU8(OffsetPtr);
+  bool HasPath = false;
+  for (int I = 0; I != FormatCount; ++I) {
+    if (*OffsetPtr >= EndPrologueOffset)
+      return ContentDescriptors();
+    ContentDescriptor Descriptor;
+    Descriptor.Type =
+      dwarf::LineNumberEntryFormat(DebugLineData.getULEB128(OffsetPtr));
+    Descriptor.Form = dwarf::Form(DebugLineData.getULEB128(OffsetPtr));
+    if (Descriptor.Type == dwarf::DW_LNCT_path)
+      HasPath = true;
+    Descriptors.push_back(Descriptor);
+  }
+  return HasPath ? Descriptors : ContentDescriptors();
+}
+
+static bool
+parseV5DirFileTables(DataExtractor DebugLineData, uint32_t *OffsetPtr,
+                     uint64_t EndPrologueOffset,
+                     std::vector<StringRef> &IncludeDirectories,
+                     std::vector<DWARFDebugLine::FileNameEntry> &FileNames) {
+  // Get the directory entry description.
+  ContentDescriptors DirDescriptors =
+    parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset);
+  if (DirDescriptors.empty())
+    return false;
+
+  // Get the directory entries, according to the format described above.
+  int DirEntryCount = DebugLineData.getU8(OffsetPtr);
+  for (int I = 0; I != DirEntryCount; ++I) {
+    if (*OffsetPtr >= EndPrologueOffset)
+      return false;
+    for (auto Descriptor : DirDescriptors) {
+      DWARFFormValue Value(Descriptor.Form);
+      switch (Descriptor.Type) {
+      case DW_LNCT_path:
+        if (!Value.extractValue(DebugLineData, OffsetPtr, nullptr))
+          return false;
+        IncludeDirectories.push_back(Value.getAsCString().getValue());
+        break;
+      default:
+        if (!Value.skipValue(DebugLineData, OffsetPtr, nullptr))
+          return false;
+      }
+    }
+  }
+
+  // Get the file entry description.
+  ContentDescriptors FileDescriptors =
+    parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset);
+  if (FileDescriptors.empty())
+    return false;
+
+  // Get the file entries, according to the format described above.
+  int FileEntryCount = DebugLineData.getU8(OffsetPtr);
+  for (int I = 0; I != FileEntryCount; ++I) {
+    if (*OffsetPtr >= EndPrologueOffset)
+      return false;
+    DWARFDebugLine::FileNameEntry FileEntry;
+    for (auto Descriptor : FileDescriptors) {
+      DWARFFormValue Value(Descriptor.Form);
+      if (!Value.extractValue(DebugLineData, OffsetPtr, nullptr))
+        return false;
+      switch (Descriptor.Type) {
+      case DW_LNCT_path:
+        FileEntry.Name = Value.getAsCString().getValue();
+        break;
+      case DW_LNCT_directory_index:
+        FileEntry.DirIdx = Value.getAsUnsignedConstant().getValue();
+        break;
+      case DW_LNCT_timestamp:
+        FileEntry.ModTime = Value.getAsUnsignedConstant().getValue();
+        break;
+      case DW_LNCT_size:
+        FileEntry.Length = Value.getAsUnsignedConstant().getValue();
+        break;
+      // FIXME: Add MD5
+      default:
+        break;
+      }
+    }
+    FileNames.push_back(FileEntry);
+  }
+  return true;
+}
+
 bool DWARFDebugLine::Prologue::parse(DataExtractor DebugLineData,
                                      uint32_t *OffsetPtr) {
   const uint64_t PrologueOffset = *OffsetPtr;
@@ -90,6 +220,11 @@ bool DWARFDebugLine::Prologue::parse(DataExtractor DebugLineData,
   if (Version < 2)
     return false;
 
+  if (Version >= 5) {
+    AddressSize = DebugLineData.getU8(OffsetPtr);
+    SegSelectorSize = DebugLineData.getU8(OffsetPtr);
+  }
+
   PrologueLength = DebugLineData.getUnsigned(OffsetPtr, sizeofPrologueLength());
   const uint64_t EndPrologueOffset = PrologueLength + *OffsetPtr;
   MinInstLength = DebugLineData.getU8(OffsetPtr);
@@ -106,24 +241,18 @@ bool DWARFDebugLine::Prologue::parse(DataExtractor DebugLineData,
     StandardOpcodeLengths.push_back(OpLen);
   }
 
-  while (*OffsetPtr < EndPrologueOffset) {
-    StringRef S = DebugLineData.getCStrRef(OffsetPtr);
-    if (S.empty())
-      break;
-    IncludeDirectories.push_back(S);
-  }
-
-  while (*OffsetPtr < EndPrologueOffset) {
-    StringRef Name = DebugLineData.getCStrRef(OffsetPtr);
-    if (Name.empty())
-      break;
-    FileNameEntry FileEntry;
-    FileEntry.Name = Name;
-    FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr);
-    FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr);
-    FileEntry.Length = DebugLineData.getULEB128(OffsetPtr);
-    FileNames.push_back(FileEntry);
-  }
+  if (Version >= 5) {
+    if (!parseV5DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset,
+                              IncludeDirectories, FileNames)) {
+      fprintf(stderr,
+              "warning: parsing line table prologue at 0x%8.8" PRIx64
+              " found an invalid directory or file table description at"
+              " 0x%8.8" PRIx64 "\n", PrologueOffset, (uint64_t)*OffsetPtr);
+      return false;
+    }
+  } else
+    parseV2DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset,
+                         IncludeDirectories, FileNames);
 
   if (*OffsetPtr != EndPrologueOffset) {
     fprintf(stderr,
index 4478134..21c1eac 100644 (file)
Binary files a/llvm/test/DebugInfo/Inputs/dwarfdump-header.elf-x86-64 and b/llvm/test/DebugInfo/Inputs/dwarfdump-header.elf-x86-64 differ
index ce51e98..c5cf485 100644 (file)
@@ -1,5 +1,6 @@
-# Test object to verify dwarfdump handles v4 and v5 CU/TU headers.
+# Test object to verify dwarfdump handles v4 and v5 CU/TU/line headers.
 # We have a representative set of units: v4 CU, v5 CU, v4 TU, v5 split TU.
+# We have v4 and v5 line-table headers.
 #
 # To generate the test object:
 # llvm-mc -triple x86_64-unknown-linux dwarfdump-header.s -filetype=obj \
@@ -28,6 +29,8 @@ dwo_TU_5:
         .byte 0x0e  # DW_FORM_strp
         .byte 0x03  # DW_AT_name
         .byte 0x0e  # DW_FORM_strp
+        .byte 0x10  # DW_AT_stmt_list
+        .byte 0x17  # DW_FORM_sec_offset
         .byte 0x00  # EOM(1)
         .byte 0x00  # EOM(2)
         .byte 0x02  # Abbrev code
@@ -81,10 +84,11 @@ CU_4_version:
         .short 4               # DWARF version number
         .long .debug_abbrev    # Offset Into Abbrev. Section
         .byte 8                # Address Size (in bytes)
-# The compile-unit DIE, which has just DW_AT_producer and DW_AT_name.
+# The compile-unit DIE, with DW_AT_producer, DW_AT_name, DW_AT_stmt_list.
         .byte 1
         .long str_producer
         .long str_CU_4
+        .long LH_4_start
         .byte 0 # NULL
 CU_4_end:
 
@@ -95,10 +99,11 @@ CU_5_version:
         .byte 1                # DWARF Unit Type
         .byte 8                # Address Size (in bytes)
         .long .debug_abbrev    # Offset Into Abbrev. Section
-# The compile-unit DIE, which has just DW_AT_producer and DW_AT_name.
+# The compile-unit DIE, with DW_AT_producer, DW_AT_name, DW_AT_stmt_list.
         .byte 1
         .long str_producer
         .long str_CU_5
+        .long LH_5_start
         .byte 0 # NULL
 CU_5_end:
 
@@ -147,3 +152,106 @@ TU_split_5_type:
         .byte 0 # NULL
         .byte 0 # NULL
 TU_split_5_end:
+
+        .section .debug_line,"",@progbits
+# DWARF v4 line-table header.
+LH_4_start:
+        .long   LH_4_end-LH_4_version   # Length of Unit
+LH_4_version:
+        .short  4               # DWARF version number
+        .long   LH_4_header_end-LH_4_params     # Length of Prologue
+LH_4_params:
+        .byte   1               # Minimum Instruction Length
+        .byte   1               # Maximum Operations per Instruction
+        .byte   1               # Default is_stmt
+        .byte   -5              # Line Base
+        .byte   14              # Line Range
+        .byte   13              # Opcode Base
+        .byte   0               # Standard Opcode Lengths
+        .byte   1
+        .byte   1
+        .byte   1
+        .byte   1
+        .byte   0
+        .byte   0
+        .byte   0
+        .byte   1
+        .byte   0
+        .byte   0
+        .byte   1
+        # Directory table
+        .asciz  "Directory4a"
+        .asciz  "Directory4b"
+        .byte   0
+        # File table
+        .asciz  "File4a"        # File name 1
+        .byte   1               # Directory index 1
+        .byte   0x41            # Timestamp 1
+        .byte   0x42            # File Size 1
+        .asciz  "File4b"        # File name 2
+        .byte   0               # Directory index 2
+        .byte   0x43            # Timestamp 2
+        .byte   0x44            # File Size 2
+        .byte   0               # End of list
+LH_4_header_end:
+        # Line number program, which is empty.
+LH_4_end:
+
+# DWARF v5 line-table header.
+LH_5_start:
+        .long   LH_5_end-LH_5_version   # Length of Unit
+LH_5_version:
+        .short  5               # DWARF version number
+        .byte   8               # Address Size
+        .byte   0               # Segment Selector Size
+        .long   LH_5_header_end-LH_5_params     # Length of Prologue
+LH_5_params:
+        .byte   1               # Minimum Instruction Length
+        .byte   1               # Maximum Operations per Instruction
+        .byte   1               # Default is_stmt
+        .byte   -5              # Line Base
+        .byte   14              # Line Range
+        .byte   13              # Opcode Base
+        .byte   0               # Standard Opcode Lengths
+        .byte   1
+        .byte   1
+        .byte   1
+        .byte   1
+        .byte   0
+        .byte   0
+        .byte   0
+        .byte   1
+        .byte   0
+        .byte   0
+        .byte   1
+        # Directory table format
+        .byte   1               # One element per directory entry
+        .byte   1               # DW_LNCT_path
+        .byte   0x08            # DW_FORM_string
+        # Directory table entries
+        .byte   2               # Two directories
+        .asciz "Directory5a"
+        .asciz "Directory5b"
+        # File table format
+        .byte   4               # Four elements per file entry
+        .byte   1               # DW_LNCT_path
+        .byte   0x08            # DW_FORM_string
+        .byte   2               # DW_LNCT_directory_index
+        .byte   0x0b            # DW_FORM_data1
+        .byte   3               # DW_LNCT_timestamp
+        .byte   0x0f            # DW_FORM_udata
+        .byte   4               # DW_LNCT_size
+        .byte   0x0f            # DW_FORM_udata
+        # File table entries
+        .byte   2               # Two files
+        .asciz "File5a"
+        .byte   1
+        .byte   0x51
+        .byte   0x52
+        .asciz "File5b"
+        .byte   2
+        .byte   0x53
+        .byte   0x54
+LH_5_header_end:
+        # Line number program, which is empty.
+LH_5_end:
index 3947c8b..222e506 100644 (file)
@@ -7,13 +7,13 @@ CHECK-LABEL: .debug_info contents:
 
 The v4 CU header.
 
-CHECK: 0x00000000: Compile Unit: length = 0x00000011 version = 0x0004 abbr_offset = 0x0000 addr_size = 0x08 (next unit at 0x00000015)
+CHECK: 0x00000000: Compile Unit: length = 0x00000015 version = 0x0004 abbr_offset = 0x0000 addr_size = 0x08 (next unit at 0x00000019)
 CHECK: 0x0000000b: DW_TAG_compile_unit
 
 The v5 normal CU header.
 
-CHECK: 0x00000015: Compile Unit: length = 0x00000012 version = 0x0005 unit_type = DW_UT_compile abbr_offset = 0x0000 addr_size = 0x08 (next unit at 0x0000002b)
-CHECK: 0x00000021: DW_TAG_compile_unit
+CHECK: 0x00000019: Compile Unit: length = 0x00000016 version = 0x0005 unit_type = DW_UT_compile abbr_offset = 0x0000 addr_size = 0x08 (next unit at 0x00000033)
+CHECK: 0x00000025: DW_TAG_compile_unit
 
 CHECK-LABEL: .debug_types contents:
 
@@ -27,3 +27,33 @@ CHECK: .debug_types.dwo contents:
 
 CHECK: 0x00000000: Type Unit: length = 0x00000020 version = 0x0005 unit_type = DW_UT_split_type abbr_offset = 0x0000 addr_size = 0x08 name = 'V5_split_type_unit' type_signature = 0x8899aabbccddeeff type_offset = 0x001d (next unit at 0x00000024)
 CHECK: 0x00000018: DW_TAG_type_unit
+
+CHECK-LABEL: .debug_line contents:
+
+The v4 line table header.
+
+CHECK: Line table prologue:
+CHECK: version: 4
+CHECK-NOT: address_size
+CHECK-NOT: seg_select_size
+CHECK: max_ops_per_inst: 1
+CHECK: include_directories[  1] = 'Directory4a'
+CHECK: include_directories[  2] = 'Directory4b'
+CHECK-NOT: include_directories
+CHECK: file_names[  1]    1 0x00000041 0x00000042 File4a{{$}}
+CHECK: file_names[  2]    0 0x00000043 0x00000044 File4b{{$}}
+CHECK-NOT: file_names
+
+The v5 line table header.
+
+CHECK: Line table prologue:
+CHECK: version: 5
+CHECK: address_size: 8
+CHECK: seg_select_size: 0
+CHECK: max_ops_per_inst: 1
+CHECK: include_directories[  1] = 'Directory5a'
+CHECK: include_directories[  2] = 'Directory5b'
+CHECK-NOT: include_directories
+CHECK: file_names[  1]    1 0x00000051 0x00000052 File5a{{$}}
+CHECK: file_names[  2]    2 0x00000053 0x00000054 File5b{{$}}
+CHECK-NOT: file_names