
This commit brings support for debug_names in DWARFYAML. It parses YAML and generates emits a DWARF5 Accelerator table with the following limitations: 1. All forms must have a fixed length (zero length is also ok). 2. Hard-coded support for DWARF 5 and DWARF32. 3. The generated table does not contain a hash index All of these limitations can be lifted in the future, but for now this is good enough to enable testing.
303 lines
9.9 KiB
C++
303 lines
9.9 KiB
C++
//===- DWARFAcceleratorTableTest.cpp --------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
|
#include "llvm/ObjectYAML/DWARFEmitter.h"
|
|
#include "llvm/Testing/Support/Error.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
|
|
static Error ExtractDebugNames(StringRef NamesSecData, StringRef StrSecData) {
|
|
DWARFDataExtractor NamesExtractor(NamesSecData,
|
|
/*isLittleEndian=*/true,
|
|
/*AddrSize=*/4);
|
|
DataExtractor StrExtractor(StrSecData,
|
|
/*isLittleEndian=*/true,
|
|
/*AddrSize=*/4);
|
|
DWARFDebugNames Table(NamesExtractor, StrExtractor);
|
|
return Table.extract();
|
|
}
|
|
|
|
namespace {
|
|
|
|
TEST(DWARFDebugNames, ReservedUnitLength) {
|
|
static const char NamesSecData[64] =
|
|
"\xf0\xff\xff\xff"; // Reserved unit length value
|
|
EXPECT_THAT_ERROR(
|
|
ExtractDebugNames(StringRef(NamesSecData, sizeof(NamesSecData)),
|
|
StringRef()),
|
|
FailedWithMessage("parsing .debug_names header at 0x0: unsupported "
|
|
"reserved unit length of value 0xfffffff0"));
|
|
}
|
|
|
|
TEST(DWARFDebugNames, TooSmallForDWARF64) {
|
|
// DWARF64 header takes at least 44 bytes.
|
|
static const char NamesSecData[43] = "\xff\xff\xff\xff"; // DWARF64 mark
|
|
EXPECT_THAT_ERROR(
|
|
ExtractDebugNames(StringRef(NamesSecData, sizeof(NamesSecData)),
|
|
StringRef()),
|
|
FailedWithMessage("parsing .debug_names header at 0x0: unexpected end of "
|
|
"data at offset 0x2b while reading [0x28, 0x2c)"));
|
|
}
|
|
|
|
TEST(DWARFDebugNames, BasicTestEntries) {
|
|
const char *Yamldata = R"(
|
|
--- !ELF
|
|
debug_str:
|
|
- 'NameType1'
|
|
- 'NameType2'
|
|
|
|
debug_names:
|
|
Abbreviations:
|
|
- Code: 0x1
|
|
Tag: DW_TAG_namespace
|
|
Indices:
|
|
- Idx: DW_IDX_compile_unit
|
|
Form: DW_FORM_data4
|
|
- Idx: DW_IDX_die_offset
|
|
Form: DW_FORM_ref4
|
|
Entries:
|
|
- Name: 0x0 # strp to NameType1
|
|
Code: 0x1
|
|
Values:
|
|
- 0x0 # Compile unit
|
|
- 0x0 # DIE Offset
|
|
- Name: 0xa # strp to NameType2
|
|
Code: 0x1
|
|
Values:
|
|
- 0x1 # Compile unit
|
|
- 0x1 # DIE Offset
|
|
- Name: 0x0 # strp to NameType1
|
|
Code: 0x1
|
|
Values:
|
|
- 0x2 # Compile unit
|
|
- 0x2 # DIE Offset
|
|
|
|
)";
|
|
|
|
Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
|
|
DWARFYAML::emitDebugSections(Yamldata,
|
|
/*IsLittleEndian=*/true,
|
|
/*Is64BitAddrSize=*/true);
|
|
ASSERT_THAT_EXPECTED(Sections, Succeeded());
|
|
auto Ctx = DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
|
|
const DWARFDebugNames &DebugNames = Ctx->getDebugNames();
|
|
ASSERT_NE(DebugNames.begin(), DebugNames.end());
|
|
const DWARFDebugNames::NameIndex &NameIndex = *DebugNames.begin();
|
|
|
|
ASSERT_EQ(NameIndex.getNameCount(), 2u);
|
|
ASSERT_EQ(NameIndex.getBucketCount(), 0u);
|
|
ASSERT_EQ(NameIndex.getCUCount(), 1u);
|
|
ASSERT_EQ(NameIndex.getCUOffset(0), 0u);
|
|
ASSERT_EQ(NameIndex.getForeignTUCount(), 0u);
|
|
ASSERT_EQ(NameIndex.getLocalTUCount(), 0u);
|
|
|
|
// Check "NameEntries": there should be one per unique name.
|
|
// These are indexed starting on 1.
|
|
DWARFDebugNames::NameTableEntry FirstEntry = NameIndex.getNameTableEntry(1);
|
|
ASSERT_EQ(FirstEntry.getString(), StringRef("NameType1"));
|
|
DWARFDebugNames::NameTableEntry SecondEntry = NameIndex.getNameTableEntry(2);
|
|
ASSERT_EQ(SecondEntry.getString(), StringRef("NameType2"));
|
|
|
|
SmallVector<DWARFDebugNames::Entry> FirstNameEntries =
|
|
to_vector_of<DWARFDebugNames::Entry>(NameIndex.equal_range("NameType1"));
|
|
ASSERT_EQ(FirstNameEntries.size(), 2u);
|
|
ASSERT_EQ(FirstNameEntries[0].getCUIndex(), 0u);
|
|
ASSERT_EQ(FirstNameEntries[1].getCUIndex(), 0x2);
|
|
ASSERT_EQ(FirstNameEntries[0].getDIEUnitOffset(), 0x0);
|
|
ASSERT_EQ(FirstNameEntries[1].getDIEUnitOffset(), 0x2);
|
|
|
|
SmallVector<DWARFDebugNames::Entry> SecondNameEntries =
|
|
to_vector_of<DWARFDebugNames::Entry>(NameIndex.equal_range("NameType2"));
|
|
ASSERT_EQ(SecondNameEntries.size(), 1u);
|
|
ASSERT_EQ(SecondNameEntries[0].getCUIndex(), 0x1);
|
|
ASSERT_EQ(SecondNameEntries[0].getDIEUnitOffset(), 0x1);
|
|
}
|
|
|
|
TEST(DWARFDebugNames, ParentEntries) {
|
|
const char *Yamldata = R"(
|
|
--- !ELF
|
|
debug_str:
|
|
- 'Name1'
|
|
- 'Name2'
|
|
- 'Name3'
|
|
- 'Name4'
|
|
debug_names:
|
|
Abbreviations:
|
|
- Code: 0x11
|
|
Tag: DW_TAG_namespace
|
|
Indices:
|
|
- Idx: DW_IDX_parent
|
|
Form: DW_FORM_flag_present
|
|
- Idx: DW_IDX_die_offset
|
|
Form: DW_FORM_ref4
|
|
- Code: 0x22
|
|
Tag: DW_TAG_namespace
|
|
Indices:
|
|
- Idx: DW_IDX_parent
|
|
Form: DW_FORM_ref4
|
|
- Idx: DW_IDX_die_offset
|
|
Form: DW_FORM_ref4
|
|
- Code: 0x33
|
|
Tag: DW_TAG_namespace
|
|
Indices:
|
|
- Idx: DW_IDX_die_offset
|
|
Form: DW_FORM_ref4
|
|
Entries:
|
|
- Name: 0x0 # strp to Name1
|
|
Code: 0x11
|
|
Values:
|
|
- 0x0 # Die offset
|
|
- Name: 0x6 # strp to Name2
|
|
Code: 0x22
|
|
Values:
|
|
- 0x0 # Parent = First entry
|
|
- 0x1 # Die offset
|
|
- Name: 0xc # strp to Name3
|
|
Code: 0x22
|
|
Values:
|
|
- 0x6 # Parent = Second entry
|
|
- 0x1 # Die offset
|
|
- Name: 0x12 # strp to Name4
|
|
Code: 0x33
|
|
Values:
|
|
- 0x1 # Die offset
|
|
)";
|
|
|
|
Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
|
|
DWARFYAML::emitDebugSections(Yamldata,
|
|
/*IsLittleEndian=*/true,
|
|
/*Is64BitAddrSize=*/true);
|
|
ASSERT_THAT_EXPECTED(Sections, Succeeded());
|
|
auto Ctx = DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
|
|
const DWARFDebugNames &DebugNames = Ctx->getDebugNames();
|
|
ASSERT_NE(DebugNames.begin(), DebugNames.end());
|
|
const DWARFDebugNames::NameIndex &NameIndex = *DebugNames.begin();
|
|
|
|
SmallVector<DWARFDebugNames::Entry> Name1Entries =
|
|
to_vector_of<DWARFDebugNames::Entry>(NameIndex.equal_range("Name1"));
|
|
ASSERT_EQ(Name1Entries.size(), 1u);
|
|
Expected<std::optional<DWARFDebugNames::Entry>> Name1Parent =
|
|
Name1Entries[0].getParentDIEEntry();
|
|
ASSERT_THAT_EXPECTED(Name1Parent, Succeeded());
|
|
ASSERT_EQ(*Name1Parent, std::nullopt); // Name1 has no parent
|
|
|
|
SmallVector<DWARFDebugNames::Entry> Name2Entries =
|
|
to_vector_of<DWARFDebugNames::Entry>(NameIndex.equal_range("Name2"));
|
|
ASSERT_EQ(Name2Entries.size(), 1u);
|
|
Expected<std::optional<DWARFDebugNames::Entry>> Name2Parent =
|
|
Name2Entries[0].getParentDIEEntry();
|
|
ASSERT_THAT_EXPECTED(Name2Parent, Succeeded());
|
|
ASSERT_EQ((**Name2Parent).getDIEUnitOffset(), 0x0); // Name2 parent == Name1
|
|
|
|
SmallVector<DWARFDebugNames::Entry> Name3Entries =
|
|
to_vector_of<DWARFDebugNames::Entry>(NameIndex.equal_range("Name3"));
|
|
ASSERT_EQ(Name3Entries.size(), 1u);
|
|
Expected<std::optional<DWARFDebugNames::Entry>> Name3Parent =
|
|
Name3Entries[0].getParentDIEEntry();
|
|
ASSERT_THAT_EXPECTED(Name3Parent, Succeeded());
|
|
ASSERT_EQ((**Name3Parent).getDIEUnitOffset(), 0x1); // Name3 parent == Name2
|
|
|
|
SmallVector<DWARFDebugNames::Entry> Name4Entries =
|
|
to_vector_of<DWARFDebugNames::Entry>(NameIndex.equal_range("Name4"));
|
|
ASSERT_EQ(Name4Entries.size(), 1u);
|
|
ASSERT_FALSE(Name4Entries[0].hasParentInformation());
|
|
}
|
|
|
|
TEST(DWARFDebugNames, InvalidAbbrevCode) {
|
|
const char *Yamldata = R"(
|
|
--- !ELF
|
|
debug_str:
|
|
- 'NameType1'
|
|
|
|
debug_names:
|
|
Abbreviations:
|
|
- Code: 0x1
|
|
Tag: DW_TAG_namespace
|
|
Indices:
|
|
- Idx: DW_IDX_compile_unit
|
|
Form: DW_FORM_data4
|
|
Entries:
|
|
- Name: 0x0 # strp to NameType1
|
|
Code: 0x123456
|
|
Values:
|
|
- 0x0 # Compile unit
|
|
)";
|
|
|
|
Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
|
|
DWARFYAML::emitDebugSections(Yamldata,
|
|
/*IsLittleEndian=*/true,
|
|
/*Is64BitAddrSize=*/true);
|
|
ASSERT_THAT_EXPECTED(
|
|
Sections,
|
|
FailedWithMessage("did not find an Abbreviation for this code"));
|
|
}
|
|
|
|
TEST(DWARFDebugNames, InvalidNumOfValues) {
|
|
const char *Yamldata = R"(
|
|
--- !ELF
|
|
debug_str:
|
|
- 'NameType1'
|
|
|
|
debug_names:
|
|
Abbreviations:
|
|
- Code: 0x1
|
|
Tag: DW_TAG_namespace
|
|
Indices:
|
|
- Idx: DW_IDX_compile_unit
|
|
Form: DW_FORM_data4
|
|
Entries:
|
|
- Name: 0x0 # strp to NameType1
|
|
Code: 0x1
|
|
Values:
|
|
- 0x0 # Compile unit
|
|
- 0x0 # Compile unit
|
|
- 0x0 # Compile unit
|
|
)";
|
|
|
|
Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
|
|
DWARFYAML::emitDebugSections(Yamldata,
|
|
/*IsLittleEndian=*/true,
|
|
/*Is64BitAddrSize=*/true);
|
|
ASSERT_THAT_EXPECTED(
|
|
Sections, FailedWithMessage(
|
|
"mismatch between provided and required number of values"));
|
|
}
|
|
|
|
TEST(DWARFDebugNames, UnsupportedForm) {
|
|
const char *Yamldata = R"(
|
|
--- !ELF
|
|
debug_str:
|
|
- 'NameType1'
|
|
|
|
debug_names:
|
|
Abbreviations:
|
|
- Code: 0x1
|
|
Tag: DW_TAG_namespace
|
|
Indices:
|
|
- Idx: DW_IDX_compile_unit
|
|
Form: DW_FORM_strx
|
|
Entries:
|
|
- Name: 0x0 # strp to NameType1
|
|
Code: 0x1
|
|
Values:
|
|
- 0x0 # Compile unit
|
|
)";
|
|
|
|
Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
|
|
DWARFYAML::emitDebugSections(Yamldata,
|
|
/*IsLittleEndian=*/true,
|
|
/*Is64BitAddrSize=*/true);
|
|
ASSERT_THAT_EXPECTED(
|
|
Sections,
|
|
FailedWithMessage("unsupported Form for YAML debug_names emitter"));
|
|
}
|
|
} // end anonymous namespace
|