| 1 | //===-- SymbolFileDWARFTests.cpp ------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "gtest/gtest.h" |
| 10 | |
| 11 | #include "llvm/ADT/STLExtras.h" |
| 12 | #include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" |
| 13 | #include "llvm/DebugInfo/PDB/PDBSymbolData.h" |
| 14 | #include "llvm/DebugInfo/PDB/PDBSymbolExe.h" |
| 15 | #include "llvm/Support/FileSystem.h" |
| 16 | #include "llvm/Support/Path.h" |
| 17 | |
| 18 | #include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" |
| 19 | #include "Plugins/SymbolFile/DWARF/DWARFDataExtractor.h" |
| 20 | #include "Plugins/SymbolFile/DWARF/DWARFDebugAranges.h" |
| 21 | #include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" |
| 22 | #include "Plugins/SymbolFile/PDB/SymbolFilePDB.h" |
| 23 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
| 24 | #include "TestingSupport/SubsystemRAII.h" |
| 25 | #include "TestingSupport/TestUtilities.h" |
| 26 | #include "lldb/Core/Address.h" |
| 27 | #include "lldb/Core/Module.h" |
| 28 | #include "lldb/Core/ModuleSpec.h" |
| 29 | #include "lldb/Host/FileSystem.h" |
| 30 | #include "lldb/Host/HostInfo.h" |
| 31 | #include "lldb/Symbol/CompileUnit.h" |
| 32 | #include "lldb/Symbol/LineTable.h" |
| 33 | #include "lldb/Utility/ArchSpec.h" |
| 34 | #include "lldb/Utility/DataEncoder.h" |
| 35 | #include "lldb/Utility/FileSpec.h" |
| 36 | #include "lldb/Utility/StreamString.h" |
| 37 | |
| 38 | using namespace lldb; |
| 39 | using namespace lldb_private; |
| 40 | using namespace lldb_private::dwarf; |
| 41 | using namespace lldb_private::plugin::dwarf; |
| 42 | using llvm::DWARFDebugArangeSet; |
| 43 | |
| 44 | class SymbolFileDWARFTests : public testing::Test { |
| 45 | SubsystemRAII<FileSystem, HostInfo, ObjectFilePECOFF, SymbolFileDWARF, |
| 46 | TypeSystemClang, SymbolFilePDB> |
| 47 | subsystems; |
| 48 | |
| 49 | public: |
| 50 | void SetUp() override { |
| 51 | m_dwarf_test_exe = GetInputFilePath(name: "test-dwarf.exe" ); |
| 52 | } |
| 53 | |
| 54 | protected: |
| 55 | std::string m_dwarf_test_exe; |
| 56 | }; |
| 57 | |
| 58 | TEST_F(SymbolFileDWARFTests, TestAbilitiesForDWARF) { |
| 59 | // Test that when we have Dwarf debug info, SymbolFileDWARF is used. |
| 60 | FileSpec fspec(m_dwarf_test_exe); |
| 61 | ArchSpec aspec("i686-pc-windows" ); |
| 62 | lldb::ModuleSP module = std::make_shared<Module>(args&: fspec, args&: aspec); |
| 63 | |
| 64 | SymbolFile *symfile = module->GetSymbolFile(); |
| 65 | ASSERT_NE(nullptr, symfile); |
| 66 | EXPECT_EQ(symfile->GetPluginName(), SymbolFileDWARF::GetPluginNameStatic()); |
| 67 | |
| 68 | uint32_t expected_abilities = SymbolFile::kAllAbilities; |
| 69 | EXPECT_EQ(expected_abilities, symfile->CalculateAbilities()); |
| 70 | } |
| 71 | |
| 72 | TEST_F(SymbolFileDWARFTests, ParseArangesNonzeroSegmentSize) { |
| 73 | // This `.debug_aranges` table header is a valid 32bit big-endian section |
| 74 | // according to the DWARFv5 spec:6.2.1, but contains segment selectors which |
| 75 | // are not supported by lldb, and should be gracefully rejected |
| 76 | const unsigned char binary_data[] = { |
| 77 | 0, 0, 0, 41, // unit_length (length field not including this field itself) |
| 78 | 0, 2, // DWARF version number (half) |
| 79 | 0, 0, 0, 0, // offset into the .debug_info_table (ignored for the purposes |
| 80 | // of this test |
| 81 | 4, // address size |
| 82 | 1, // segment size |
| 83 | // alignment for the first tuple which "begins at an offset that is a |
| 84 | // multiple of the size of a single tuple". Tuples are nine bytes in this |
| 85 | // example. |
| 86 | 0, 0, 0, 0, 0, 0, |
| 87 | // BEGIN TUPLES |
| 88 | 1, 0, 0, 0, 4, 0, 0, 0, |
| 89 | 1, // a 1byte object starting at address 4 in segment 1 |
| 90 | 0, 0, 0, 0, 4, 0, 0, 0, |
| 91 | 1, // a 1byte object starting at address 4 in segment 0 |
| 92 | // END TUPLES |
| 93 | 0, 0, 0, 0, 0, 0, 0, 0, 0 // terminator |
| 94 | }; |
| 95 | llvm::DWARFDataExtractor data(llvm::ArrayRef<unsigned char>(binary_data), |
| 96 | /*isLittleEndian=*/false, /*AddrSize=*/4); |
| 97 | |
| 98 | DWARFDebugArangeSet debug_aranges; |
| 99 | offset_t off = 0; |
| 100 | llvm::Error error = debug_aranges.extract(data, offset_ptr: &off); |
| 101 | EXPECT_TRUE(bool(error)); |
| 102 | EXPECT_EQ("non-zero segment selector size in address range table at offset " |
| 103 | "0x0 is not supported" , |
| 104 | llvm::toString(std::move(error))); |
| 105 | EXPECT_EQ(off, 12U); // Parser should read no further than the segment size |
| 106 | } |
| 107 | |
| 108 | TEST_F(SymbolFileDWARFTests, ParseArangesWithMultipleTerminators) { |
| 109 | // This .debug_aranges set has multiple terminator entries which appear in |
| 110 | // binaries produced by popular linux compilers and linker combinations. We |
| 111 | // must be able to parse all the way through the data for each |
| 112 | // DWARFDebugArangeSet. Previously the DWARFDebugArangeSet::extract() |
| 113 | // function would stop parsing as soon as we ran into a terminator even |
| 114 | // though the length field stated that there was more data that follows. This |
| 115 | // would cause the next DWARFDebugArangeSet to be parsed immediately |
| 116 | // following the first terminator and it would attempt to decode the |
| 117 | // DWARFDebugArangeSet header using the remaining segment + address pairs |
| 118 | // from the remaining bytes. |
| 119 | unsigned char binary_data[] = { |
| 120 | 0, 0, 0, 0, // unit_length that will be set correctly after this |
| 121 | 0, 2, // DWARF version number (uint16_t) |
| 122 | 0, 0, 0, 255, // CU offset |
| 123 | 4, // address size |
| 124 | 0, // segment size |
| 125 | 0, 0, 0, 0, // alignment for the first tuple |
| 126 | // BEGIN TUPLES |
| 127 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // premature terminator |
| 128 | 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, // [0x1000-0x1100) |
| 129 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // premature terminator |
| 130 | 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, // [0x2000-0x2010) |
| 131 | // END TUPLES |
| 132 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // terminator |
| 133 | }; |
| 134 | // Set the big endian length correctly. |
| 135 | const offset_t binary_data_size = sizeof(binary_data); |
| 136 | binary_data[3] = (uint8_t)binary_data_size - 4; |
| 137 | DWARFDataExtractor data; |
| 138 | data.SetData(bytes: static_cast<const void *>(binary_data), length: sizeof binary_data, |
| 139 | byte_order: lldb::ByteOrder::eByteOrderBig); |
| 140 | DWARFDebugAranges debug_aranges; |
| 141 | debug_aranges.extract(debug_aranges_data: data); |
| 142 | // Parser should read all terminators to the end of the length specified. |
| 143 | ASSERT_EQ(debug_aranges.GetNumRanges(), 2U); |
| 144 | EXPECT_EQ(debug_aranges.FindAddress(0x0fff), DW_INVALID_OFFSET); |
| 145 | EXPECT_EQ(debug_aranges.FindAddress(0x1000), 255u); |
| 146 | EXPECT_EQ(debug_aranges.FindAddress(0x1100 - 1), 255u); |
| 147 | EXPECT_EQ(debug_aranges.FindAddress(0x1100), DW_INVALID_OFFSET); |
| 148 | EXPECT_EQ(debug_aranges.FindAddress(0x1fff), DW_INVALID_OFFSET); |
| 149 | EXPECT_EQ(debug_aranges.FindAddress(0x2000), 255u); |
| 150 | EXPECT_EQ(debug_aranges.FindAddress(0x2010 - 1), 255u); |
| 151 | EXPECT_EQ(debug_aranges.FindAddress(0x2010), DW_INVALID_OFFSET); |
| 152 | } |
| 153 | |
| 154 | TEST_F(SymbolFileDWARFTests, ParseArangesIgnoreEmpty) { |
| 155 | // This .debug_aranges set has some address ranges which have zero length |
| 156 | // and we ensure that these are ignored by our DWARFDebugArangeSet parser |
| 157 | // and not included in the descriptors that are returned. |
| 158 | unsigned char binary_data[] = { |
| 159 | 0, 0, 0, 0, // unit_length that will be set correctly after this |
| 160 | 0, 2, // DWARF version number (uint16_t) |
| 161 | 0, 0, 0, 255, // CU offset |
| 162 | 4, // address size |
| 163 | 0, // segment size |
| 164 | 0, 0, 0, 0, // alignment for the first tuple |
| 165 | // BEGIN TUPLES |
| 166 | 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, // [0x1000-0x1100) |
| 167 | 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, // [0x1100-0x1100) |
| 168 | 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, // [0x2000-0x2010) |
| 169 | 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, // [0x2010-0x2010) |
| 170 | // END TUPLES |
| 171 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // terminator |
| 172 | }; |
| 173 | // Set the big endian length correctly. |
| 174 | const offset_t binary_data_size = sizeof(binary_data); |
| 175 | binary_data[3] = (uint8_t)binary_data_size - 4; |
| 176 | DWARFDataExtractor data; |
| 177 | data.SetData(bytes: static_cast<const void *>(binary_data), length: sizeof binary_data, |
| 178 | byte_order: lldb::ByteOrder::eByteOrderBig); |
| 179 | DWARFDebugAranges debug_aranges; |
| 180 | debug_aranges.extract(debug_aranges_data: data); |
| 181 | // Parser should read all terminators to the end of the length specified. |
| 182 | // Previously the DWARFDebugArangeSet would stop at the first terminator |
| 183 | // entry and leave the offset in the middle of the current |
| 184 | // DWARFDebugArangeSet data, and that would cause the next extracted |
| 185 | // DWARFDebugArangeSet to fail. |
| 186 | ASSERT_EQ(debug_aranges.GetNumRanges(), 2U); |
| 187 | EXPECT_EQ(debug_aranges.FindAddress(0x0fff), DW_INVALID_OFFSET); |
| 188 | EXPECT_EQ(debug_aranges.FindAddress(0x1000), 255u); |
| 189 | EXPECT_EQ(debug_aranges.FindAddress(0x1100 - 1), 255u); |
| 190 | EXPECT_EQ(debug_aranges.FindAddress(0x1100), DW_INVALID_OFFSET); |
| 191 | EXPECT_EQ(debug_aranges.FindAddress(0x1fff), DW_INVALID_OFFSET); |
| 192 | EXPECT_EQ(debug_aranges.FindAddress(0x2000), 255u); |
| 193 | EXPECT_EQ(debug_aranges.FindAddress(0x2010 - 1), 255u); |
| 194 | EXPECT_EQ(debug_aranges.FindAddress(0x2010), DW_INVALID_OFFSET); |
| 195 | } |
| 196 | |
| 197 | TEST_F(SymbolFileDWARFTests, ParseAranges) { |
| 198 | // Test we can successfully parse a DWARFDebugAranges. The initial error |
| 199 | // checking code had a bug where it would always return an empty address |
| 200 | // ranges for everything in .debug_aranges and no error. |
| 201 | unsigned char binary_data[] = { |
| 202 | 0, 0, 0, 0, // unit_length that will be set correctly after this |
| 203 | 2, 0, // DWARF version number |
| 204 | 255, 0, 0, 0, // offset into the .debug_info_table |
| 205 | 8, // address size |
| 206 | 0, // segment size |
| 207 | 0, 0, 0, 0, // pad bytes |
| 208 | // BEGIN TUPLES |
| 209 | // First tuple: [0x1000-0x1100) |
| 210 | 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Address 0x1000 |
| 211 | 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Size 0x0100 |
| 212 | // Second tuple: [0x2000-0x2100) |
| 213 | 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Address 0x2000 |
| 214 | 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Size 0x0100 |
| 215 | // Terminating tuple |
| 216 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Terminator |
| 217 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Terminator |
| 218 | }; |
| 219 | // Set the little endian length correctly. |
| 220 | binary_data[0] = sizeof(binary_data) - 4; |
| 221 | DWARFDataExtractor data; |
| 222 | data.SetData(bytes: static_cast<const void *>(binary_data), length: sizeof binary_data, |
| 223 | byte_order: lldb::ByteOrder::eByteOrderLittle); |
| 224 | DWARFDebugAranges debug_aranges; |
| 225 | debug_aranges.extract(debug_aranges_data: data); |
| 226 | EXPECT_EQ(debug_aranges.GetNumRanges(), 2u); |
| 227 | EXPECT_EQ(debug_aranges.FindAddress(0x0fff), DW_INVALID_OFFSET); |
| 228 | EXPECT_EQ(debug_aranges.FindAddress(0x1000), 255u); |
| 229 | EXPECT_EQ(debug_aranges.FindAddress(0x1100 - 1), 255u); |
| 230 | EXPECT_EQ(debug_aranges.FindAddress(0x1100), DW_INVALID_OFFSET); |
| 231 | EXPECT_EQ(debug_aranges.FindAddress(0x1fff), DW_INVALID_OFFSET); |
| 232 | EXPECT_EQ(debug_aranges.FindAddress(0x2000), 255u); |
| 233 | EXPECT_EQ(debug_aranges.FindAddress(0x2100 - 1), 255u); |
| 234 | EXPECT_EQ(debug_aranges.FindAddress(0x2100), DW_INVALID_OFFSET); |
| 235 | } |
| 236 | |
| 237 | TEST_F(SymbolFileDWARFTests, ParseArangesSkipErrors) { |
| 238 | // Test we can successfully parse a DWARFDebugAranges that contains some |
| 239 | // valid DWARFDebugArangeSet objects and some with errors as long as their |
| 240 | // length is set correctly. This helps LLDB ensure that it can parse newer |
| 241 | // .debug_aranges version that LLDB currently doesn't support, or ignore |
| 242 | // errors in individual DWARFDebugArangeSet objects as long as the length |
| 243 | // is set correctly. |
| 244 | const unsigned char binary_data[] = { |
| 245 | // This DWARFDebugArangeSet is well formed and has a single address range |
| 246 | // for [0x1000-0x1100) with a CU offset of 0x00000000. |
| 247 | 0, 0, 0, 28, // unit_length that will be set correctly after this |
| 248 | 0, 2, // DWARF version number (uint16_t) |
| 249 | 0, 0, 0, 0, // CU offset = 0x00000000 |
| 250 | 4, // address size |
| 251 | 0, // segment size |
| 252 | 0, 0, 0, 0, // alignment for the first tuple |
| 253 | 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, // [0x1000-0x1100) |
| 254 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // terminator |
| 255 | // This DWARFDebugArangeSet has the correct length, but an invalid |
| 256 | // version. We need to be able to skip this correctly and ignore it. |
| 257 | 0, 0, 0, 20, // unit_length that will be set correctly after this |
| 258 | 0, 44, // invalid DWARF version number (uint16_t) |
| 259 | 0, 0, 1, 0, // CU offset = 0x00000100 |
| 260 | 4, // address size |
| 261 | 0, // segment size |
| 262 | 0, 0, 0, 0, // alignment for the first tuple |
| 263 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // terminator |
| 264 | // This DWARFDebugArangeSet is well formed and has a single address range |
| 265 | // for [0x2000-0x2100) with a CU offset of 0x00000000. |
| 266 | 0, 0, 0, 28, // unit_length that will be set correctly after this |
| 267 | 0, 2, // DWARF version number (uint16_t) |
| 268 | 0, 0, 2, 0, // CU offset = 0x00000200 |
| 269 | 4, // address size |
| 270 | 0, // segment size |
| 271 | 0, 0, 0, 0, // alignment for the first tuple |
| 272 | 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, // [0x2000-0x2100) |
| 273 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // terminator |
| 274 | }; |
| 275 | |
| 276 | DWARFDataExtractor data; |
| 277 | data.SetData(bytes: static_cast<const void *>(binary_data), length: sizeof binary_data, |
| 278 | byte_order: lldb::ByteOrder::eByteOrderBig); |
| 279 | DWARFDebugAranges debug_aranges; |
| 280 | debug_aranges.extract(debug_aranges_data: data); |
| 281 | EXPECT_EQ(debug_aranges.GetNumRanges(), 2u); |
| 282 | EXPECT_EQ(debug_aranges.FindAddress(0x0fff), DW_INVALID_OFFSET); |
| 283 | EXPECT_EQ(debug_aranges.FindAddress(0x1000), 0u); |
| 284 | EXPECT_EQ(debug_aranges.FindAddress(0x1100 - 1), 0u); |
| 285 | EXPECT_EQ(debug_aranges.FindAddress(0x1100), DW_INVALID_OFFSET); |
| 286 | EXPECT_EQ(debug_aranges.FindAddress(0x1fff), DW_INVALID_OFFSET); |
| 287 | EXPECT_EQ(debug_aranges.FindAddress(0x2000), 0x200u); |
| 288 | EXPECT_EQ(debug_aranges.FindAddress(0x2100 - 1), 0x200u); |
| 289 | EXPECT_EQ(debug_aranges.FindAddress(0x2100), DW_INVALID_OFFSET); |
| 290 | } |
| 291 | |