LTO builds have been creating invalid DWARF and one of the errors was a file index that was out of bounds. "llvm-dwarfdump --verify" will check all file indexes for line tables already, but there are no checks for the validity of file indexes in attributes.
The verification will verify if there is a DW_AT_decl_file/DW_AT_call_file that:
- there is a line table for the compile unit
- the file index is valid
- the encoding is appropriate
Tests are added that test all of the above conditions.
Differential Revision: https://reviews.llvm.org/D84817
bool hasFileAtIndex(uint64_t FileIndex) const;
+ Optional<uint64_t> getLastValidFileIndex() const;
+
bool
getFileNameByIndex(uint64_t FileIndex, StringRef CompDir,
DILineInfoSpecifier::FileLineInfoKind Kind,
return Prologue.hasFileAtIndex(FileIndex);
}
+ Optional<uint64_t> getLastValidFileIndex() const {
+ return Prologue.getLastValidFileIndex();
+ }
+
/// Extracts filename by its index in filename table in prologue.
/// In Dwarf 4, the files are 1-indexed and the current compilation file
/// name is not represented in the list. In DWARF v5, the files are
return FileIndex != 0 && FileIndex <= FileNames.size();
}
+Optional<uint64_t> DWARFDebugLine::Prologue::getLastValidFileIndex() const {
+ if (FileNames.empty())
+ return None;
+ uint16_t DwarfVersion = getVersion();
+ assert(DwarfVersion != 0 &&
+ "line table prologue has no dwarf version information");
+ // In DWARF v5 the file names are 0-indexed.
+ if (DwarfVersion >= 5)
+ return FileNames.size() - 1;
+ return FileNames.size();
+}
+
const llvm::DWARFDebugLine::FileNameEntry &
DWARFDebugLine::Prologue::getFileNameEntry(uint64_t Index) const {
uint16_t DwarfVersion = getVersion();
}
break;
}
+ case DW_AT_call_file:
+ case DW_AT_decl_file: {
+ if (auto FileIdx = AttrValue.Value.getAsUnsignedConstant()) {
+ DWARFUnit *U = Die.getDwarfUnit();
+ const auto *LT = U->getContext().getLineTableForUnit(U);
+ if (LT) {
+ if (!LT->hasFileAtIndex(*FileIdx)) {
+ bool IsZeroIndexed = LT->Prologue.getVersion() >= 5;
+ if (Optional<uint64_t> LastFileIdx = LT->getLastValidFileIndex()) {
+ ReportError("DIE has " + AttributeString(Attr) +
+ " with an invalid file index " +
+ llvm::formatv("{0}", *FileIdx) +
+ " (valid values are [" + (IsZeroIndexed ? "0-" : "1-") +
+ llvm::formatv("{0}", *LastFileIdx) + "])");
+ } else {
+ ReportError("DIE has " + AttributeString(Attr) +
+ " with an invalid file index " +
+ llvm::formatv("{0}", *FileIdx) +
+ " (the file table in the prologue is empty)");
+ }
+ }
+ } else {
+ ReportError("DIE has " + AttributeString(Attr) +
+ " that references a file with index " +
+ llvm::formatv("{0}", *FileIdx) +
+ " and the compile unit has no line table");
+ }
+ } else {
+ ReportError("DIE has " + AttributeString(Attr) +
+ " with invalid encoding");
+ }
+ break;
+ }
default:
break;
}
--- /dev/null
+# RUN: yaml2obj %s | not llvm-dwarfdump --verify - | FileCheck %s --implicit-check-not=error:
+
+# CHECK: error: DIE has DW_AT_decl_file with an invalid file index 2 (valid values are [1-1]){{[[:space:]]}}
+# CHECK-NEXT: 0x0000001e: DW_TAG_subprogram
+# CHECK-NEXT: DW_AT_name ("main")
+# CHECK-NEXT: DW_AT_low_pc (0x0000000000001000)
+# CHECK-NEXT: DW_AT_high_pc (0x0000000000002000)
+# CHECK-NEXT: DW_AT_decl_file (0x02)
+# CHECK-NEXT: DW_AT_call_line (5){{[[:space:]]}}
+# CHECK-NEXT: error: DIE has DW_AT_call_file with an invalid file index 3 (valid values are [1-1]){{[[:space:]]}}
+# CHECK-NEXT: 0x00000035: DW_TAG_inlined_subroutine
+# CHECK-NEXT: DW_AT_name ("inline1")
+# CHECK-NEXT: DW_AT_low_pc (0x0000000000001100)
+# CHECK-NEXT: DW_AT_high_pc (0x0000000000001200)
+# CHECK-NEXT: DW_AT_call_file (0x03)
+# CHECK-NEXT: DW_AT_call_line (10){{[[:space:]]}}
+
+# CHECK: Errors detected.
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_X86_64
+DWARF:
+ debug_str:
+ - ''
+ - '/tmp/main.c'
+ - main
+ - inline1
+ debug_abbrev:
+ - Code: 0x0000000000000001
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_stmt_list
+ Form: DW_FORM_sec_offset
+ - Code: 0x0000000000000002
+ Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_decl_file
+ Form: DW_FORM_data1
+ - Attribute: DW_AT_call_line
+ Form: DW_FORM_data1
+ - Code: 0x0000000000000003
+ Tag: DW_TAG_inlined_subroutine
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data4
+ - Attribute: DW_AT_call_file
+ Form: DW_FORM_data1
+ - Attribute: DW_AT_call_line
+ Form: DW_FORM_data1
+ debug_info:
+ - Length: 0x0000000000000046
+ Version: 4
+ AbbrOffset: 0x0000000000000000
+ AddrSize: 8
+ Entries:
+ - AbbrCode: 0x00000001
+ Values:
+ - Value: 0x0000000000000001
+ - Value: 0x0000000000000002
+ - Value: 0x0000000000000000
+ - Value: 0x0000000000000000
+ - AbbrCode: 0x00000002
+ Values:
+ - Value: 0x000000000000000D
+ - Value: 0x0000000000001000
+ - Value: 0x0000000000002000
+ - Value: 0x0000000000000002
+ - Value: 0x0000000000000005
+ - AbbrCode: 0x00000003
+ Values:
+ - Value: 0x0000000000000012
+ - Value: 0x0000000000001100
+ - Value: 0x0000000000000100
+ - Value: 0x0000000000000003
+ - Value: 0x000000000000000A
+ - AbbrCode: 0x00000000
+ Values: []
+ - AbbrCode: 0x00000000
+ Values: []
+ debug_line:
+ - Length: 40
+ Version: 2
+ PrologueLength: 34
+ MinInstLength: 1
+ DefaultIsStmt: 1
+ LineBase: 251
+ LineRange: 14
+ OpcodeBase: 13
+ StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]
+ IncludeDirs:
+ - '/tmp'
+ Files:
+ - Name: main.c
+ DirIdx: 1
+ ModTime: 0
+ Length: 0
+ Opcodes: []
+...
--- /dev/null
+# RUN: yaml2obj %s | not llvm-dwarfdump --verify - | FileCheck %s --implicit-check-not=error:
+
+# CHECK: error: DIE has DW_AT_decl_file with an invalid file index 2 (the file table in the prologue is empty){{[[:space:]]}}
+# CHECK-NEXT: 0x0000001e: DW_TAG_subprogram
+# CHECK-NEXT: DW_AT_name ("main")
+# CHECK-NEXT: DW_AT_low_pc (0x0000000000001000)
+# CHECK-NEXT: DW_AT_high_pc (0x0000000000002000)
+# CHECK-NEXT: DW_AT_decl_file (0x02)
+# CHECK-NEXT: DW_AT_call_line (5){{[[:space:]]}}
+# CHECK-NEXT: error: DIE has DW_AT_call_file with an invalid file index 3 (the file table in the prologue is empty){{[[:space:]]}}
+# CHECK-NEXT: 0x00000035: DW_TAG_inlined_subroutine
+# CHECK-NEXT: DW_AT_name ("inline1")
+# CHECK-NEXT: DW_AT_low_pc (0x0000000000001100)
+# CHECK-NEXT: DW_AT_high_pc (0x0000000000001200)
+# CHECK-NEXT: DW_AT_call_file (0x03)
+# CHECK-NEXT: DW_AT_call_line (10){{[[:space:]]}}
+
+# CHECK: Errors detected.
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_X86_64
+DWARF:
+ debug_str:
+ - ''
+ - '/tmp/main.c'
+ - main
+ - inline1
+ debug_abbrev:
+ - Code: 0x0000000000000001
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_stmt_list
+ Form: DW_FORM_sec_offset
+ - Code: 0x0000000000000002
+ Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_decl_file
+ Form: DW_FORM_data1
+ - Attribute: DW_AT_call_line
+ Form: DW_FORM_data1
+ - Code: 0x0000000000000003
+ Tag: DW_TAG_inlined_subroutine
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data4
+ - Attribute: DW_AT_call_file
+ Form: DW_FORM_data1
+ - Attribute: DW_AT_call_line
+ Form: DW_FORM_data1
+ debug_info:
+ - Length: 0x0000000000000046
+ Version: 4
+ AbbrOffset: 0x0000000000000000
+ AddrSize: 8
+ Entries:
+ - AbbrCode: 0x00000001
+ Values:
+ - Value: 0x0000000000000001
+ - Value: 0x0000000000000002
+ - Value: 0x0000000000000000
+ - Value: 0x0000000000000000
+ - AbbrCode: 0x00000002
+ Values:
+ - Value: 0x000000000000000D
+ - Value: 0x0000000000001000
+ - Value: 0x0000000000002000
+ - Value: 0x0000000000000002
+ - Value: 0x0000000000000005
+ - AbbrCode: 0x00000003
+ Values:
+ - Value: 0x0000000000000012
+ - Value: 0x0000000000001100
+ - Value: 0x0000000000000100
+ - Value: 0x0000000000000003
+ - Value: 0x000000000000000A
+ - AbbrCode: 0x00000000
+ Values: []
+ - AbbrCode: 0x00000000
+ Values: []
+ debug_line:
+ - Length: 30
+ Version: 2
+ PrologueLength: 24
+ MinInstLength: 1
+ DefaultIsStmt: 1
+ LineBase: 251
+ LineRange: 14
+ OpcodeBase: 13
+ StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]
+ IncludeDirs:
+ - '/tmp'
+ Files:
+ Opcodes: []
+...
# CHECK-NEXT: DW_AT_comp_dir [DW_FORM_strp] ( .debug_str[0x0000003f] = "/Users/sgravani/Development/tests")
# CHECK-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
# CHECK-NEXT: DW_AT_high_pc [DW_FORM_data4] (0x00000016){{[[:space:]]}}
+# CHECK-NEXT: error: DIE has DW_AT_decl_file that references a file with index 1 and the compile unit has no line table{{[[:space:]]}}
+# CHECK-NEXT: 0x0000002b: DW_TAG_subprogram [2] *
+# CHECK-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
+# CHECK-NEXT: DW_AT_high_pc [DW_FORM_data4] (0x00000016)
+# CHECK-NEXT: DW_AT_frame_base [DW_FORM_exprloc] (DW_OP_reg6)
+# CHECK-NEXT: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000061] = "main")
+# CHECK-NEXT: DW_AT_decl_file [DW_FORM_data1] (0x01)
+# CHECK-NEXT: DW_AT_decl_line [DW_FORM_data1] (1)
+# CHECK-NEXT: DW_AT_prototyped [DW_FORM_flag_present] (true)
+# CHECK-NEXT: DW_AT_type [DW_FORM_ref4] (cu + 0x0052 => {0x00000052} "")
+# CHECK-NEXT: DW_AT_external [DW_FORM_flag_present] (true){{[[:space:]]}}
# CHECK-NEXT: error: DIE has DW_AT_type with incompatible tag DW_TAG_null{{[[:space:]]}}
# CHECK-NEXT: 0x0000002b: DW_TAG_subprogram [2] *
# CHECK-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
# CHECK-NEXT: DW_AT_prototyped [DW_FORM_flag_present] (true)
# CHECK-NEXT: DW_AT_type [DW_FORM_ref4] (cu + 0x0052 => {0x00000052} "")
# CHECK-NEXT: DW_AT_external [DW_FORM_flag_present] (true){{[[:space:]]}}
+# CHECK-NEXT: error: DIE has DW_AT_decl_file that references a file with index 1 and the compile unit has no line table{{[[:space:]]}}
+# CHECK-NEXT: 0x00000044: DW_TAG_variable [3]
+# CHECK-NEXT: DW_AT_location [DW_FORM_exprloc] (DW_OP_fbreg -8)
+# CHECK-NEXT: DW_AT_name [DW_FORM_strp] ( .debug_str[0x0000006a] = "a")
+# CHECK-NEXT: DW_AT_decl_file [DW_FORM_data1] (0x01)
+# CHECK-NEXT: DW_AT_decl_line [DW_FORM_data1] (2)
+# CHECK-NEXT: DW_AT_use_location [DW_FORM_ref4] (cu + 0x0053 => {0x00000053}){{[[:space:]]}}
# CHECK-NEXT: error: Compilation unit root DIE is not a unit DIE: DW_TAG_null.
# CHECK-NEXT: error: Compilation unit type (DW_UT_compile) and root DIE (DW_TAG_null) do not match.
# CHECK-NEXT: error: Units[2] - start offset: 0x00000068
--- /dev/null
+# RUN: yaml2obj %s | not llvm-dwarfdump --verify - | FileCheck %s --implicit-check-not=error:
+
+# CHECK: error: DIE has DW_AT_decl_file with invalid encoding{{[[:space:]]}}
+# CHECK-NEXT: 0x0000001a: DW_TAG_subprogram
+# CHECK-NEXT: DW_AT_name ("main")
+# CHECK-NEXT: DW_AT_low_pc (0x0000000000001000)
+# CHECK-NEXT: DW_AT_high_pc (0x0000000000002000)
+# CHECK-NEXT: DW_AT_decl_file ("")
+# CHECK-NEXT: DW_AT_call_line (5){{[[:space:]]}}
+# CHECK-NEXT: error: DIE has DW_AT_call_file with invalid encoding{{[[:space:]]}}
+# CHECK-NEXT: 0x00000034: DW_TAG_inlined_subroutine
+# CHECK-NEXT: DW_AT_name ("inline1")
+# CHECK-NEXT: DW_AT_low_pc (0x0000000000001100)
+# CHECK-NEXT: DW_AT_high_pc (0x0000000000001200)
+# CHECK-NEXT: DW_AT_call_file ("")
+# CHECK-NEXT: DW_AT_call_line (10){{[[:space:]]}}
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_X86_64
+DWARF:
+ debug_str:
+ - ''
+ - '/tmp/main.c'
+ - main
+ - ''
+ - inline1
+ debug_abbrev:
+ - Code: 0x0000000000000001
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Code: 0x0000000000000002
+ Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_decl_file
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_call_line
+ Form: DW_FORM_data1
+ - Code: 0x0000000000000003
+ Tag: DW_TAG_inlined_subroutine
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data4
+ - Attribute: DW_AT_call_file
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_call_line
+ Form: DW_FORM_data1
+ debug_info:
+ - Length: 0x0000000000000048
+ Version: 4
+ AbbrOffset: 0x0000000000000000
+ AddrSize: 8
+ Entries:
+ - AbbrCode: 0x00000001
+ Values:
+ - Value: 0x0000000000000001
+ - Value: 0x0000000000000002
+ - Value: 0x0000000000000000
+ - AbbrCode: 0x00000002
+ Values:
+ - Value: 0x000000000000000D
+ - Value: 0x0000000000001000
+ - Value: 0x0000000000002000
+ - Value: 0x0000000000000012
+ - Value: 0x0000000000000005
+ - AbbrCode: 0x00000003
+ Values:
+ - Value: 0x0000000000000013
+ - Value: 0x0000000000001100
+ - Value: 0x0000000000000100
+ - Value: 0x0000000000000012
+ - Value: 0x000000000000000A
+ - AbbrCode: 0x00000000
+ Values: []
+ - AbbrCode: 0x00000000
+ Values: []
+...