Index: include/llvm/DebugInfo/DWARF/DWARFDebugLine.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFDebugLine.h +++ include/llvm/DebugInfo/DWARF/DWARFDebugLine.h @@ -43,6 +43,10 @@ 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: lib/DebugInfo/DWARF/DWARFDebugLine.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -7,9 +7,10 @@ // //===----------------------------------------------------------------------===// +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/ADT/SmallString.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" -#include "llvm/DebugInfo/DWARF/DWARFDebugLine.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,13 @@ using namespace dwarf; typedef DILineInfoSpecifier::FileLineInfoKind FileLineInfoKind; +typedef std::pair ContentDescriptor; 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 +46,8 @@ 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 +79,130 @@ } } +// Parse v2-v4 directory and file tables. +static void +parseOldDirFileTables(DataExtractor debug_line_data, uint32_t *offset_ptr, + uint64_t end_prologue_offset, + std::vector &IncludeDirectories, + std::vector &FileNames) { + while (*offset_ptr < end_prologue_offset) { + const char *s = debug_line_data.getCStr(offset_ptr); + if (s && s[0]) + IncludeDirectories.push_back(s); + else + break; + } + + while (*offset_ptr < end_prologue_offset) { + const char *name = debug_line_data.getCStr(offset_ptr); + if (name && name[0]) { + DWARFDebugLine::FileNameEntry fileEntry; + fileEntry.Name = name; + fileEntry.DirIdx = debug_line_data.getULEB128(offset_ptr); + fileEntry.ModTime = debug_line_data.getULEB128(offset_ptr); + fileEntry.Length = debug_line_data.getULEB128(offset_ptr); + FileNames.push_back(fileEntry); + } else { + break; + } + } +} + +// Parse v5 directory/file entry content descriptions. +static bool +parseV5EntryFormat(DataExtractor debug_line_data, uint32_t *offset_ptr, + uint64_t end_prologue_offset, + SmallVectorImpl &Descriptors) { + unsigned FormatCount = debug_line_data.getU8(offset_ptr); + for (unsigned i = 0; i < FormatCount; ++i) { + if (*offset_ptr >= end_prologue_offset) + return false; + unsigned ContentType = debug_line_data.getULEB128(offset_ptr); + unsigned ContentForm = debug_line_data.getULEB128(offset_ptr); + Descriptors.push_back(std::make_pair(ContentType, ContentForm)); + } + return true; +} + +static bool +parseV5DirFileTables(DataExtractor debug_line_data, uint32_t *offset_ptr, + uint64_t end_prologue_offset, + std::vector &IncludeDirectories, + std::vector &FileNames) { + // Get the directory entry description. + + SmallVector Descriptors; + if (!parseV5EntryFormat(debug_line_data, offset_ptr, end_prologue_offset, + Descriptors)) + return false; + + // Get the directory entries, according to the format described above. + + unsigned EntryCount = debug_line_data.getU8(offset_ptr); + for (unsigned i = 0; i < EntryCount; ++i) { + if (*offset_ptr >= end_prologue_offset) + return false; + for (auto Descriptor : Descriptors) { + DWARFFormValue Value(dwarf::Form(Descriptor.second)); + switch (dwarf::LineNumberEntryFormat(Descriptor.first)) { + case DW_LNCT_path: + if (!Value.extractValue(debug_line_data, offset_ptr, nullptr)) + return false; + IncludeDirectories.push_back(Value.getAsCString().getValue()); + break; + default: + if (!Value.skipValue(debug_line_data, offset_ptr, nullptr)) + return false; + } + } + } + if (IncludeDirectories.size() != EntryCount) + return false; + + // Get the file entry description. + + Descriptors.clear(); + if (!parseV5EntryFormat(debug_line_data, offset_ptr, end_prologue_offset, + Descriptors)) + return false; + + // Get the file entries, according to the format described above. + + EntryCount = debug_line_data.getU8(offset_ptr); + for (unsigned i = 0; i < EntryCount; ++i) { + if (*offset_ptr >= end_prologue_offset) + return false; + DWARFDebugLine::FileNameEntry fileEntry; + for (auto Descriptor : Descriptors) { + DWARFFormValue Value(dwarf::Form(Descriptor.second)); + if (!Value.extractValue(debug_line_data, offset_ptr, nullptr)) + return false; + switch (dwarf::LineNumberEntryFormat(Descriptor.first)) { + 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); + } + if (FileNames.size() != EntryCount) + return false; + + return true; +} + bool DWARFDebugLine::Prologue::parse(DataExtractor debug_line_data, uint32_t *offset_ptr) { const uint64_t prologue_offset = *offset_ptr; @@ -90,6 +219,11 @@ if (Version < 2) return false; + if (Version >= 5) { + AddressSize = debug_line_data.getU8(offset_ptr); + SegSelectorSize = debug_line_data.getU8(offset_ptr); + } + PrologueLength = debug_line_data.getUnsigned(offset_ptr, sizeofPrologueLength()); const uint64_t end_prologue_offset = PrologueLength + *offset_ptr; @@ -107,27 +241,13 @@ StandardOpcodeLengths.push_back(op_len); } - while (*offset_ptr < end_prologue_offset) { - const char *s = debug_line_data.getCStr(offset_ptr); - if (s && s[0]) - IncludeDirectories.push_back(s); - else - break; - } - - while (*offset_ptr < end_prologue_offset) { - const char *name = debug_line_data.getCStr(offset_ptr); - if (name && name[0]) { - FileNameEntry fileEntry; - fileEntry.Name = name; - fileEntry.DirIdx = debug_line_data.getULEB128(offset_ptr); - fileEntry.ModTime = debug_line_data.getULEB128(offset_ptr); - fileEntry.Length = debug_line_data.getULEB128(offset_ptr); - FileNames.push_back(fileEntry); - } else { - break; - } - } + if (Version >= 5) { + if (!parseV5DirFileTables(debug_line_data, offset_ptr, end_prologue_offset, + IncludeDirectories, FileNames)) + return false; + } else + parseOldDirFileTables(debug_line_data, offset_ptr, end_prologue_offset, + IncludeDirectories, FileNames); if (*offset_ptr != end_prologue_offset) { fprintf(stderr, "warning: parsing line table prologue at 0x%8.8" PRIx64 Index: test/DebugInfo/Inputs/dwarfdump-header.s =================================================================== --- test/DebugInfo/Inputs/dwarfdump-header.s +++ test/DebugInfo/Inputs/dwarfdump-header.s @@ -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 @@ .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 @@ .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 @@ .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 @@ .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: test/DebugInfo/dwarfdump-header.test =================================================================== --- test/DebugInfo/dwarfdump-header.test +++ test/DebugInfo/dwarfdump-header.test @@ -7,13 +7,13 @@ 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: 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