#include "llvm/Support/Path.h"
#include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h"
+#include "Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDataExtractor.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h"
#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
#include "Plugins/SymbolFile/PDB/SymbolFilePDB.h"
#include "TestingSupport/TestUtilities.h"
#include "lldb/Symbol/LineTable.h"
#include "lldb/Symbol/SymbolVendor.h"
#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataEncoder.h"
#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/StreamString.h"
+
+
+using namespace lldb;
using namespace lldb_private;
class SymbolFileDWARFTests : public testing::Test {
uint32_t expected_abilities = SymbolFile::kAllAbilities;
EXPECT_EQ(expected_abilities, symfile->CalculateAbilities());
}
+
+TEST_F(SymbolFileDWARFTests, TestAbbrevOrder1Start1) {
+ // Test that if we have a .debug_abbrev that contains ordered abbreviation
+ // codes that start at 1, that we get O(1) access.
+
+ const auto byte_order = eByteOrderLittle;
+ const uint8_t addr_size = 4;
+ StreamString encoder(Stream::eBinary, addr_size, byte_order);
+ encoder.PutULEB128(1); // Abbrev code 1
+ encoder.PutULEB128(DW_TAG_compile_unit);
+ encoder.PutHex8(DW_CHILDREN_yes);
+ encoder.PutULEB128(DW_AT_name);
+ encoder.PutULEB128(DW_FORM_strp);
+ encoder.PutULEB128(0);
+ encoder.PutULEB128(0);
+
+ encoder.PutULEB128(2); // Abbrev code 2
+ encoder.PutULEB128(DW_TAG_subprogram);
+ encoder.PutHex8(DW_CHILDREN_no);
+ encoder.PutULEB128(DW_AT_name);
+ encoder.PutULEB128(DW_FORM_strp);
+ encoder.PutULEB128(0);
+ encoder.PutULEB128(0);
+
+ encoder.PutULEB128(0); // Abbrev code 0 (termination)
+
+ DWARFDataExtractor data;
+ data.SetData(encoder.GetData(), encoder.GetSize(), byte_order);
+ DWARFAbbreviationDeclarationSet abbrev_set;
+ lldb::offset_t data_offset = 0;
+ llvm::Error error = abbrev_set.extract(data, &data_offset);
+ EXPECT_FALSE(bool(error));
+ // Make sure we have O(1) access to each abbreviation by making sure the
+ // index offset is 1 and not UINT32_MAX
+ EXPECT_EQ(abbrev_set.GetIndexOffset(), 1);
+
+ auto abbrev1 = abbrev_set.GetAbbreviationDeclaration(1);
+ EXPECT_EQ(abbrev1->Tag(), DW_TAG_compile_unit);
+ EXPECT_TRUE(abbrev1->HasChildren());
+ EXPECT_EQ(abbrev1->NumAttributes(), 1);
+ auto abbrev2 = abbrev_set.GetAbbreviationDeclaration(2);
+ EXPECT_EQ(abbrev2->Tag(), DW_TAG_subprogram);
+ EXPECT_FALSE(abbrev2->HasChildren());
+ EXPECT_EQ(abbrev2->NumAttributes(), 1);
+}
+
+TEST_F(SymbolFileDWARFTests, TestAbbrevOrder1Start5) {
+ // Test that if we have a .debug_abbrev that contains ordered abbreviation
+ // codes that start at 5, that we get O(1) access.
+
+ const auto byte_order = eByteOrderLittle;
+ const uint8_t addr_size = 4;
+ StreamString encoder(Stream::eBinary, addr_size, byte_order);
+ encoder.PutULEB128(5); // Abbrev code 5
+ encoder.PutULEB128(DW_TAG_compile_unit);
+ encoder.PutHex8(DW_CHILDREN_yes);
+ encoder.PutULEB128(DW_AT_name);
+ encoder.PutULEB128(DW_FORM_strp);
+ encoder.PutULEB128(0);
+ encoder.PutULEB128(0);
+
+ encoder.PutULEB128(6); // Abbrev code 6
+ encoder.PutULEB128(DW_TAG_subprogram);
+ encoder.PutHex8(DW_CHILDREN_no);
+ encoder.PutULEB128(DW_AT_name);
+ encoder.PutULEB128(DW_FORM_strp);
+ encoder.PutULEB128(0);
+ encoder.PutULEB128(0);
+
+ encoder.PutULEB128(0); // Abbrev code 0 (termination)
+
+ DWARFDataExtractor data;
+ data.SetData(encoder.GetData(), encoder.GetSize(), byte_order);
+ DWARFAbbreviationDeclarationSet abbrev_set;
+ lldb::offset_t data_offset = 0;
+ llvm::Error error = abbrev_set.extract(data, &data_offset);
+ EXPECT_FALSE(bool(error));
+ // Make sure we have O(1) access to each abbreviation by making sure the
+ // index offset is 5 and not UINT32_MAX
+ EXPECT_EQ(abbrev_set.GetIndexOffset(), 5);
+
+ auto abbrev1 = abbrev_set.GetAbbreviationDeclaration(5);
+ EXPECT_EQ(abbrev1->Tag(), DW_TAG_compile_unit);
+ EXPECT_TRUE(abbrev1->HasChildren());
+ EXPECT_EQ(abbrev1->NumAttributes(), 1);
+ auto abbrev2 = abbrev_set.GetAbbreviationDeclaration(6);
+ EXPECT_EQ(abbrev2->Tag(), DW_TAG_subprogram);
+ EXPECT_FALSE(abbrev2->HasChildren());
+ EXPECT_EQ(abbrev2->NumAttributes(), 1);
+}
+
+TEST_F(SymbolFileDWARFTests, TestAbbrevOutOfOrder) {
+ // Test that if we have a .debug_abbrev that contains unordered abbreviation
+ // codes, that we can access the information correctly.
+
+ const auto byte_order = eByteOrderLittle;
+ const uint8_t addr_size = 4;
+ StreamString encoder(Stream::eBinary, addr_size, byte_order);
+ encoder.PutULEB128(2); // Abbrev code 2
+ encoder.PutULEB128(DW_TAG_compile_unit);
+ encoder.PutHex8(DW_CHILDREN_yes);
+ encoder.PutULEB128(DW_AT_name);
+ encoder.PutULEB128(DW_FORM_strp);
+ encoder.PutULEB128(0);
+ encoder.PutULEB128(0);
+
+ encoder.PutULEB128(1); // Abbrev code 1
+ encoder.PutULEB128(DW_TAG_subprogram);
+ encoder.PutHex8(DW_CHILDREN_no);
+ encoder.PutULEB128(DW_AT_name);
+ encoder.PutULEB128(DW_FORM_strp);
+ encoder.PutULEB128(0);
+ encoder.PutULEB128(0);
+
+ encoder.PutULEB128(0); // Abbrev code 0 (termination)
+
+ DWARFDataExtractor data;
+ data.SetData(encoder.GetData(), encoder.GetSize(), byte_order);
+ DWARFAbbreviationDeclarationSet abbrev_set;
+ lldb::offset_t data_offset = 0;
+ llvm::Error error = abbrev_set.extract(data, &data_offset);
+ EXPECT_FALSE(bool(error));
+ // Make sure we don't have O(1) access to each abbreviation by making sure
+ // the index offset is UINT32_MAX
+ EXPECT_EQ(abbrev_set.GetIndexOffset(), UINT32_MAX);
+
+ auto abbrev1 = abbrev_set.GetAbbreviationDeclaration(2);
+ EXPECT_EQ(abbrev1->Tag(), DW_TAG_compile_unit);
+ EXPECT_TRUE(abbrev1->HasChildren());
+ EXPECT_EQ(abbrev1->NumAttributes(), 1);
+ auto abbrev2 = abbrev_set.GetAbbreviationDeclaration(1);
+ EXPECT_EQ(abbrev2->Tag(), DW_TAG_subprogram);
+ EXPECT_FALSE(abbrev2->HasChildren());
+ EXPECT_EQ(abbrev2->NumAttributes(), 1);
+}
+
+TEST_F(SymbolFileDWARFTests, TestAbbrevInvalidNULLTag) {
+ // Test that we detect when an abbreviation has a NULL tag and that we get
+ // an error when decoding.
+
+ const auto byte_order = eByteOrderLittle;
+ const uint8_t addr_size = 4;
+ StreamString encoder(Stream::eBinary, addr_size, byte_order);
+ encoder.PutULEB128(1); // Abbrev code 1
+ encoder.PutULEB128(0); // Invalid NULL tag here!
+ encoder.PutHex8(DW_CHILDREN_no);
+ encoder.PutULEB128(0);
+ encoder.PutULEB128(0);
+
+ encoder.PutULEB128(0); // Abbrev code 0 (termination)
+
+ DWARFDataExtractor data;
+ data.SetData(encoder.GetData(), encoder.GetSize(), byte_order);
+ DWARFAbbreviationDeclarationSet abbrev_set;
+ lldb::offset_t data_offset = 0;
+ llvm::Error error = abbrev_set.extract(data, &data_offset);
+ // Verify we get an error
+ EXPECT_TRUE(bool(error));
+ EXPECT_EQ("abbrev decl requires non-null tag.",
+ llvm::toString(std::move(error)));
+
+}
+
+TEST_F(SymbolFileDWARFTests, TestAbbrevNullAttrValidForm) {
+ // Test that we detect when an abbreviation has a NULL attribute and a non
+ // NULL form and that we get an error when decoding.
+
+ const auto byte_order = eByteOrderLittle;
+ const uint8_t addr_size = 4;
+ StreamString encoder(Stream::eBinary, addr_size, byte_order);
+ encoder.PutULEB128(1); // Abbrev code 1
+ encoder.PutULEB128(DW_TAG_compile_unit);
+ encoder.PutHex8(DW_CHILDREN_no);
+ encoder.PutULEB128(0); // Invalid NULL DW_AT
+ encoder.PutULEB128(DW_FORM_strp); // With a valid form
+ encoder.PutULEB128(0);
+ encoder.PutULEB128(0);
+
+ encoder.PutULEB128(0); // Abbrev code 0 (termination)
+
+ DWARFDataExtractor data;
+ data.SetData(encoder.GetData(), encoder.GetSize(), byte_order);
+ DWARFAbbreviationDeclarationSet abbrev_set;
+ lldb::offset_t data_offset = 0;
+ llvm::Error error = abbrev_set.extract(data, &data_offset);
+ // Verify we get an error
+ EXPECT_TRUE(bool(error));
+ EXPECT_EQ("malformed abbreviation declaration attribute",
+ llvm::toString(std::move(error)));
+
+}
+
+TEST_F(SymbolFileDWARFTests, TestAbbrevValidAttrNullForm) {
+ // Test that we detect when an abbreviation has a valid attribute and a
+ // NULL form and that we get an error when decoding.
+
+ const auto byte_order = eByteOrderLittle;
+ const uint8_t addr_size = 4;
+ StreamString encoder(Stream::eBinary, addr_size, byte_order);
+ encoder.PutULEB128(1); // Abbrev code 1
+ encoder.PutULEB128(DW_TAG_compile_unit);
+ encoder.PutHex8(DW_CHILDREN_no);
+ encoder.PutULEB128(DW_AT_name); // Valid attribute
+ encoder.PutULEB128(0); // NULL form
+ encoder.PutULEB128(0);
+ encoder.PutULEB128(0);
+
+ encoder.PutULEB128(0); // Abbrev code 0 (termination)
+
+ DWARFDataExtractor data;
+ data.SetData(encoder.GetData(), encoder.GetSize(), byte_order);
+ DWARFAbbreviationDeclarationSet abbrev_set;
+ lldb::offset_t data_offset = 0;
+ llvm::Error error = abbrev_set.extract(data, &data_offset);
+ // Verify we get an error
+ EXPECT_TRUE(bool(error));
+ EXPECT_EQ("malformed abbreviation declaration attribute",
+ llvm::toString(std::move(error)));
+}
+
+TEST_F(SymbolFileDWARFTests, TestAbbrevMissingTerminator) {
+ // Test that we detect when an abbreviation has a valid attribute and a
+ // form, but is missing the NULL attribute and form that terminates an
+ // abbreviation
+
+ const auto byte_order = eByteOrderLittle;
+ const uint8_t addr_size = 4;
+ StreamString encoder(Stream::eBinary, addr_size, byte_order);
+ encoder.PutULEB128(1); // Abbrev code 1
+ encoder.PutULEB128(DW_TAG_compile_unit);
+ encoder.PutHex8(DW_CHILDREN_no);
+ encoder.PutULEB128(DW_AT_name);
+ encoder.PutULEB128(DW_FORM_strp);
+ // Don't add the NULL DW_AT and NULL DW_FORM terminator
+
+ DWARFDataExtractor data;
+ data.SetData(encoder.GetData(), encoder.GetSize(), byte_order);
+ DWARFAbbreviationDeclarationSet abbrev_set;
+ lldb::offset_t data_offset = 0;
+ llvm::Error error = abbrev_set.extract(data, &data_offset);
+ // Verify we get an error
+ EXPECT_TRUE(bool(error));
+ EXPECT_EQ("abbreviation declaration attribute list not terminated with a "
+ "null entry", llvm::toString(std::move(error)));
+}