Fix DWARFDie::getDeclFile(...) to work with DW_AT_specification.

DWARFDie::getDeclFile(...) previously only supported getting the DW_AT_decl_file if the DIE itself contained the DW_AT_decl_file attribute, or if the DIE had a DW_AT_abstract_origin that pointed to another DIE that had a DW_AT_decl_file. This patch allows the function to get the right attribute value if there is a DW_AT_specification that points to another DIE. We also test that if a DW_AT_abtract_origin or DW_AT_specification points to a DIE in another CU with a DW_FORM_ref_addr, that the right line table is used to extract the file index.

Full tests were added for the following cases:
- DIE has a DW_AT_decl_file attribute
- DIE has a DW_AT_abtract_origin that points to another die in the same CU
- DIE has a DW_AT_abtract_origin that points to another die in another CU
- DIE has a DW_AT_specification that points to another die in the same CU
- DIE has a DW_AT_specification that points to another die in another CU

Differential Revision: https://reviews.llvm.org/D108480
This commit is contained in:
Greg Clayton 2021-08-20 12:10:21 -07:00
parent d26000e4cc
commit a58c2e4af0
4 changed files with 585 additions and 13 deletions

View File

@ -119,6 +119,19 @@ public:
Optional<ArrayRef<uint8_t>> getAsBlock() const;
Optional<uint64_t> getAsCStringOffset() const;
Optional<uint64_t> getAsReferenceUVal() const;
/// Correctly extract any file paths from a form value.
///
/// These attributes can be in the from DW_AT_decl_file or DW_AT_call_file
/// attributes. We need to use the file index in the correct DWARFUnit's line
/// table prologue, and each DWARFFormValue has the DWARFUnit the form value
/// was extracted from.
///
/// \param Kind The kind of path to extract.
///
/// \returns A valid string value on success, or llvm::None if the form class
/// is not FC_Constant, or if the file index is not valid.
Optional<std::string>
getAsFile(DILineInfoSpecifier::FileLineInfoKind Kind) const;
/// Skip a form's value in \p DebugInfoData at the offset specified by
/// \p OffsetPtr.

View File

@ -566,18 +566,10 @@ uint64_t DWARFDie::getDeclLine() const {
std::string
DWARFDie::getDeclFile(DILineInfoSpecifier::FileLineInfoKind Kind) const {
auto D = getAttributeValueAsReferencedDie(DW_AT_abstract_origin);
if (!D)
D = *this;
std::string FileName;
if (auto DeclFile = toUnsigned(D.find(DW_AT_decl_file))) {
if (const auto *LineTable =
getDwarfUnit()->getContext().getLineTableForUnit(
D.getDwarfUnit()->getLinkedUnit()))
LineTable->getFileNameByIndex(
*DeclFile, D.getDwarfUnit()->getCompilationDir(), Kind, FileName);
}
return FileName;
if (auto FormValue = findRecursively(DW_AT_decl_file))
if (auto OptString = FormValue->getAsFile(Kind))
return *OptString;
return {};
}
void DWARFDie::getCallerFrame(uint32_t &CallFile, uint32_t &CallLine,

View File

@ -690,7 +690,7 @@ Optional<uint64_t> DWARFFormValue::getAsReference() const {
return R->Unit ? R->Unit->getOffset() + R->Offset : R->Offset;
return None;
}
Optional<DWARFFormValue::UnitOffset> DWARFFormValue::getAsRelativeReference() const {
if (!isFormClass(FC_Reference))
return None;
@ -762,3 +762,17 @@ Optional<uint64_t> DWARFFormValue::getAsReferenceUVal() const {
return None;
return Value.uval;
}
Optional<std::string>
DWARFFormValue::getAsFile(DILineInfoSpecifier::FileLineInfoKind Kind) const {
if (U == nullptr || !isFormClass(FC_Constant))
return None;
DWARFUnit *DU = const_cast<DWARFUnit *>(U);
if (auto *LT = U->getContext().getLineTableForUnit(DU->getLinkedUnit())) {
std::string FileName;
if (LT->getFileNameByIndex(Value.uval, DU->getCompilationDir(), Kind,
FileName))
return FileName;
}
return None;
}

View File

@ -100,4 +100,557 @@ TEST(DWARFDie, getLocations) {
"No DW_AT_call_data_value")));
}
TEST(DWARFDie, getDeclFile) {
const char *yamldata = R"(
debug_str:
- ''
debug_abbrev:
- ID: 0
Table:
- Code: 0x1
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_stmt_list
Form: DW_FORM_sec_offset
- Code: 0x2
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_decl_file
Form: DW_FORM_data1
debug_info:
- Length: 0xF
Version: 4
AbbrevTableID: 0
AbbrOffset: 0x0
AddrSize: 8
Entries:
- AbbrCode: 0x1
Values:
- Value: 0x0
- AbbrCode: 0x2
Values:
- Value: 0x1
- AbbrCode: 0x0
debug_line:
- Length: 42
Version: 2
PrologueLength: 36
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.cpp
DirIdx: 1
ModTime: 0
Length: 0
)";
// Given DWARF like this:
//
// 0x0000000b: DW_TAG_compile_unit
// DW_AT_stmt_list (0x00000000)
//
// 0x00000010: DW_TAG_subprogram
// DW_AT_decl_file ("/tmp/main.cpp")
//
// 0x00000012: NULL
//
// This tests that we can extract the right DW_AT_decl_file from a DIE that
// has a DW_AT_decl_file attribute.
Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
DWARFYAML::emitDebugSections(StringRef(yamldata),
/*IsLittleEndian=*/true,
/*Is64BitAddrSize=*/true);
ASSERT_THAT_EXPECTED(Sections, Succeeded());
std::unique_ptr<DWARFContext> Ctx =
DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);
ASSERT_NE(nullptr, CU);
DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false);
ASSERT_TRUE(Die.isValid());
DWARFDie MainDie = Die.getFirstChild();
ASSERT_TRUE(MainDie.isValid());
std::string DeclFile = MainDie.getDeclFile(
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);
#if defined(_WIN32)
EXPECT_EQ(DeclFile, "/tmp\\main.cpp");
#else
EXPECT_EQ(DeclFile, "/tmp/main.cpp");
#endif
}
TEST(DWARFDie, getDeclFileAbstractOrigin) {
const char *yamldata = R"(
debug_str:
- ''
debug_abbrev:
- ID: 0
Table:
- Code: 0x1
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_stmt_list
Form: DW_FORM_sec_offset
- Code: 0x2
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_abstract_origin
Form: DW_FORM_ref_addr
- Code: 0x3
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_decl_file
Form: DW_FORM_data1
debug_info:
- Length: 0x14
Version: 4
AbbrevTableID: 0
AbbrOffset: 0x0
AddrSize: 8
Entries:
- AbbrCode: 0x1
Values:
- Value: 0x0
- AbbrCode: 0x2
Values:
- Value: 0x15
- AbbrCode: 0x3
Values:
- Value: 0x1
- AbbrCode: 0x0
debug_line:
- Length: 42
Version: 2
PrologueLength: 36
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.cpp
DirIdx: 1
ModTime: 0
Length: 0
)";
// Given DWARF like this:
//
// 0x0000000b: DW_TAG_compile_unit
// DW_AT_stmt_list (0x00000000)
//
// 0x00000010: DW_TAG_subprogram
// DW_AT_abstract_origin (0x0000000000000015)
//
// 0x00000015: DW_TAG_subprogram
// DW_AT_decl_file ("/tmp/main.cpp")
//
// 0x00000017: NULL
//
//
// The DIE at 0x00000010 uses a DW_AT_abstract_origin to point to the DIE at
// 0x00000015, make sure that DWARFDie::getDeclFile() succeeds by extracting
// the right file name of "/tmp/main.cpp".
//
// This tests that when we have a DW_AT_abstract_origin with a compile unit
// relative form (DW_FORM_ref4) to another DIE that we get the right
// DW_AT_decl_file value.
Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
DWARFYAML::emitDebugSections(StringRef(yamldata),
/*IsLittleEndian=*/true,
/*Is64BitAddrSize=*/true);
ASSERT_THAT_EXPECTED(Sections, Succeeded());
std::unique_ptr<DWARFContext> Ctx =
DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);
ASSERT_NE(nullptr, CU);
DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false);
ASSERT_TRUE(Die.isValid());
DWARFDie MainDie = Die.getFirstChild();
ASSERT_TRUE(MainDie.isValid());
std::string DeclFile = MainDie.getDeclFile(
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);
#if defined(_WIN32)
EXPECT_EQ(DeclFile, "/tmp\\main.cpp");
#else
EXPECT_EQ(DeclFile, "/tmp/main.cpp");
#endif
}
TEST(DWARFDie, getDeclFileSpecification) {
const char *yamldata = R"(
debug_str:
- ''
debug_abbrev:
- ID: 0
Table:
- Code: 0x1
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_stmt_list
Form: DW_FORM_sec_offset
- Code: 0x2
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_specification
Form: DW_FORM_ref_addr
- Code: 0x3
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_decl_file
Form: DW_FORM_data1
debug_info:
- Length: 0x14
Version: 4
AbbrevTableID: 0
AbbrOffset: 0x0
AddrSize: 8
Entries:
- AbbrCode: 0x1
Values:
- Value: 0x0
- AbbrCode: 0x2
Values:
- Value: 0x15
- AbbrCode: 0x3
Values:
- Value: 0x1
- AbbrCode: 0x0
debug_line:
- Length: 42
Version: 2
PrologueLength: 36
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.cpp
DirIdx: 1
ModTime: 0
Length: 0
)";
// Given DWARF like this:
//
// 0x0000000b: DW_TAG_compile_unit
// DW_AT_stmt_list (0x00000000)
//
// 0x00000010: DW_TAG_subprogram
// DW_AT_specification (0x0000000000000015)
//
// 0x00000015: DW_TAG_subprogram
// DW_AT_decl_file ("/tmp/main.cpp")
//
// 0x00000017: NULL
//
// The DIE at 0x00000010 uses a DW_AT_specification to point to the DIE at
// 0x00000015, make sure that DWARFDie::getDeclFile() succeeds by extracting
// the right file name of "/tmp/main.cpp".
//
// This tests that when we have a DW_AT_specification with a compile unit
// relative form (DW_FORM_ref4) to another DIE that we get the right
// DW_AT_decl_file value.
Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
DWARFYAML::emitDebugSections(StringRef(yamldata),
/*IsLittleEndian=*/true,
/*Is64BitAddrSize=*/true);
ASSERT_THAT_EXPECTED(Sections, Succeeded());
std::unique_ptr<DWARFContext> Ctx =
DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);
ASSERT_NE(nullptr, CU);
DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false);
ASSERT_TRUE(Die.isValid());
DWARFDie MainDie = Die.getFirstChild();
ASSERT_TRUE(MainDie.isValid());
std::string DeclFile = MainDie.getDeclFile(
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);
#if defined(_WIN32)
EXPECT_EQ(DeclFile, "/tmp\\main.cpp");
#else
EXPECT_EQ(DeclFile, "/tmp/main.cpp");
#endif
}
TEST(DWARFDie, getDeclFileAbstractOriginAcrossCUBoundary) {
const char *yamldata = R"(
debug_str:
- ''
debug_abbrev:
- ID: 0
Table:
- Code: 0x1
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
- Code: 0x2
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_abstract_origin
Form: DW_FORM_ref_addr
- Code: 0x3
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_stmt_list
Form: DW_FORM_sec_offset
- Code: 0x4
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_decl_file
Form: DW_FORM_data1
debug_info:
- Length: 0xE
Version: 4
AbbrevTableID: 0
AbbrOffset: 0x0
AddrSize: 8
Entries:
- AbbrCode: 0x1
- AbbrCode: 0x2
Values:
- Value: 0x22
- AbbrCode: 0x0
- Length: 0xF
Version: 4
AbbrevTableID: 0
AbbrOffset: 0x0
AddrSize: 8
Entries:
- AbbrCode: 0x3
Values:
- Value: 0x0
- AbbrCode: 0x4
Values:
- Value: 0x1
- AbbrCode: 0x0
debug_line:
- Length: 42
Version: 2
PrologueLength: 36
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.cpp
DirIdx: 1
ModTime: 0
Length: 0
)";
// Given DWARF like this:
//
// 0x0000000b: DW_TAG_compile_unit
//
// 0x0000000c: DW_TAG_subprogram
// DW_AT_abstract_origin (0x0000000000000022)
//
// 0x00000011: NULL
//
// 0x0000001d: DW_TAG_compile_unit
// DW_AT_stmt_list (0x00000000)
//
// 0x00000022: DW_TAG_subprogram
// DW_AT_decl_file ("/tmp/main.cpp")
//
// 0x00000024: NULL
//
// This tests that when we have a DW_AT_abstract_origin with a
// DW_FORM_ref_addr to another DIE in another compile unit that we use the
// right file table when converting the file index of the DW_AT_decl_file.
//
// The DIE at 0x0000000c uses a DW_AT_abstract_origin to point to the DIE at
// 0x00000022, make sure that DWARFDie::getDeclFile() succeeds by extracting
// the right file name of "/tmp/main.cpp". The DW_AT_decl_file must grab the
// file from the line table prologue of the compile unit at offset
// 0x0000001d.
Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
DWARFYAML::emitDebugSections(StringRef(yamldata),
/*IsLittleEndian=*/true,
/*Is64BitAddrSize=*/true);
ASSERT_THAT_EXPECTED(Sections, Succeeded());
std::unique_ptr<DWARFContext> Ctx =
DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);
ASSERT_NE(nullptr, CU);
DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false);
ASSERT_TRUE(Die.isValid());
DWARFDie MainDie = Die.getFirstChild();
ASSERT_TRUE(MainDie.isValid());
std::string DeclFile = MainDie.getDeclFile(
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);
#if defined(_WIN32)
EXPECT_EQ(DeclFile, "/tmp\\main.cpp");
#else
EXPECT_EQ(DeclFile, "/tmp/main.cpp");
#endif
}
TEST(DWARFDie, getDeclFileSpecificationAcrossCUBoundary) {
const char *yamldata = R"(
debug_str:
- ''
debug_abbrev:
- ID: 0
Table:
- Code: 0x1
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
- Code: 0x2
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_specification
Form: DW_FORM_ref_addr
- Code: 0x3
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_stmt_list
Form: DW_FORM_sec_offset
- Code: 0x4
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_decl_file
Form: DW_FORM_data1
debug_info:
- Length: 0xE
Version: 4
AbbrevTableID: 0
AbbrOffset: 0x0
AddrSize: 8
Entries:
- AbbrCode: 0x1
- AbbrCode: 0x2
Values:
- Value: 0x22
- AbbrCode: 0x0
- Length: 0xF
Version: 4
AbbrevTableID: 0
AbbrOffset: 0x0
AddrSize: 8
Entries:
- AbbrCode: 0x3
Values:
- Value: 0x0
- AbbrCode: 0x4
Values:
- Value: 0x1
- AbbrCode: 0x0
debug_line:
- Length: 42
Version: 2
PrologueLength: 36
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.cpp
DirIdx: 1
ModTime: 0
Length: 0
)";
// Given DWARF like this:
//
// 0x0000000b: DW_TAG_compile_unit
//
// 0x0000000c: DW_TAG_subprogram
// DW_AT_specification (0x0000000000000022)
//
// 0x00000011: NULL
//
// 0x0000001d: DW_TAG_compile_unit
// DW_AT_stmt_list (0x00000000)
//
// 0x00000022: DW_TAG_subprogram
// DW_AT_decl_file ("/tmp/main.cpp")
//
// 0x00000024: NULL
//
// This tests that when we have a DW_AT_specification with a
// DW_FORM_ref_addr to another DIE in another compile unit that we use the
// right file table when converting the file index of the DW_AT_decl_file.
//
// The DIE at 0x0000000c uses a DW_AT_specification to point to the DIE at
// 0x00000022, make sure that DWARFDie::getDeclFile() succeeds by extracting
// the right file name of "/tmp/main.cpp". The DW_AT_decl_file must grab the
// file from the line table prologue of the compile unit at offset
// 0x0000001d.
Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
DWARFYAML::emitDebugSections(StringRef(yamldata),
/*IsLittleEndian=*/true,
/*Is64BitAddrSize=*/true);
ASSERT_THAT_EXPECTED(Sections, Succeeded());
std::unique_ptr<DWARFContext> Ctx =
DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);
ASSERT_NE(nullptr, CU);
DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false);
ASSERT_TRUE(Die.isValid());
DWARFDie MainDie = Die.getFirstChild();
ASSERT_TRUE(MainDie.isValid());
std::string DeclFile = MainDie.getDeclFile(
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);
#if defined(_WIN32)
EXPECT_EQ(DeclFile, "/tmp\\main.cpp");
#else
EXPECT_EQ(DeclFile, "/tmp/main.cpp");
#endif
}
} // end anonymous namespace