#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"
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;
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)
}
}
+// 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;
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);
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,
-# 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 \
.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
.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:
.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:
.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:
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:
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