//===- llvm/unittest/DebugInfo/DWARFDebugFrameTest.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/ADT/DenseSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" using namespace llvm; namespace { dwarf::CIE createCIE(bool IsDWARF64, uint64_t Offset, uint64_t Length) { return dwarf::CIE(IsDWARF64, Offset, Length, /*Version=*/3, /*Augmentation=*/StringRef(), /*AddressSize=*/8, /*SegmentDescriptorSize=*/0, /*CodeAlignmentFactor=*/1, /*DataAlignmentFactor=*/-8, /*ReturnAddressRegister=*/16, /*AugmentationData=*/StringRef(), /*FDEPointerEncoding=*/dwarf::DW_EH_PE_absptr, /*LSDAPointerEncoding=*/dwarf::DW_EH_PE_omit, /*Personality=*/None, /*PersonalityEnc=*/None, /*Arch=*/Triple::x86_64); } void expectDumpResult(const dwarf::CIE &TestCIE, bool IsEH, StringRef ExpectedFirstLine) { std::string Output; raw_string_ostream OS(Output); TestCIE.dump(OS, DIDumpOptions(), /*MRI=*/nullptr, IsEH); OS.flush(); StringRef FirstLine = StringRef(Output).split('\n').first; EXPECT_EQ(FirstLine, ExpectedFirstLine); } void expectDumpResult(const dwarf::FDE &TestFDE, bool IsEH, StringRef ExpectedFirstLine) { std::string Output; raw_string_ostream OS(Output); TestFDE.dump(OS, DIDumpOptions(), /*MRI=*/nullptr, IsEH); OS.flush(); StringRef FirstLine = StringRef(Output).split('\n').first; EXPECT_EQ(FirstLine, ExpectedFirstLine); } TEST(DWARFDebugFrame, DumpDWARF32CIE) { dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false, /*Offset=*/0x1111abcd, /*Length=*/0x2222abcd); expectDumpResult(TestCIE, /*IsEH=*/false, "1111abcd 2222abcd ffffffff CIE"); } TEST(DWARFDebugFrame, DumpDWARF64CIE) { dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/true, /*Offset=*/0x1111abcdabcd, /*Length=*/0x2222abcdabcd); expectDumpResult(TestCIE, /*IsEH=*/false, "1111abcdabcd 00002222abcdabcd ffffffffffffffff CIE"); } TEST(DWARFDebugFrame, DumpEHCIE) { dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false, /*Offset=*/0x1000, /*Length=*/0x20); expectDumpResult(TestCIE, /*IsEH=*/true, "00001000 00000020 00000000 CIE"); } TEST(DWARFDebugFrame, DumpEH64CIE) { dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/true, /*Offset=*/0x1000, /*Length=*/0x20); expectDumpResult(TestCIE, /*IsEH=*/true, "00001000 0000000000000020 00000000 CIE"); } TEST(DWARFDebugFrame, DumpDWARF64FDE) { dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/true, /*Offset=*/0x1111abcdabcd, /*Length=*/0x2222abcdabcd); dwarf::FDE TestFDE(/*IsDWARF64=*/true, /*Offset=*/0x3333abcdabcd, /*Length=*/0x4444abcdabcd, /*CIEPointer=*/0x1111abcdabcd, /*InitialLocation=*/0x5555abcdabcd, /*AddressRange=*/0x111111111111, /*Cie=*/&TestCIE, /*LSDAAddress=*/None, /*Arch=*/Triple::x86_64); expectDumpResult(TestFDE, /*IsEH=*/false, "3333abcdabcd 00004444abcdabcd 00001111abcdabcd FDE " "cie=1111abcdabcd pc=5555abcdabcd...6666bcdebcde"); } TEST(DWARFDebugFrame, DumpEH64FDE) { dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/true, /*Offset=*/0x1111ab9a000c, /*Length=*/0x20); dwarf::FDE TestFDE(/*IsDWARF64=*/true, /*Offset=*/0x1111abcdabcd, /*Length=*/0x2222abcdabcd, /*CIEPointer=*/0x33abcd, /*InitialLocation=*/0x4444abcdabcd, /*AddressRange=*/0x111111111111, /*Cie=*/&TestCIE, /*LSDAAddress=*/None, /*Arch=*/Triple::x86_64); expectDumpResult(TestFDE, /*IsEH=*/true, "1111abcdabcd 00002222abcdabcd 0033abcd FDE " "cie=1111ab9a000c pc=4444abcdabcd...5555bcdebcde"); } static Error parseCFI(dwarf::CIE &C, ArrayRef Instructions, Optional Size = None) { DWARFDataExtractor Data(Instructions, /*IsLittleEndian=*/true, /*AddressSize=*/8); uint64_t Offset = 0; const uint64_t EndOffset = Size ? *Size : (uint64_t)Instructions.size(); return C.cfis().parse(Data, &Offset, EndOffset); } TEST(DWARFDebugFrame, InvalidCFIOpcodesTest) { llvm::DenseSet ValidExtendedOpcodes = { dwarf::DW_CFA_nop, dwarf::DW_CFA_advance_loc, dwarf::DW_CFA_offset, dwarf::DW_CFA_restore, dwarf::DW_CFA_set_loc, dwarf::DW_CFA_advance_loc1, dwarf::DW_CFA_advance_loc2, dwarf::DW_CFA_advance_loc4, dwarf::DW_CFA_offset_extended, dwarf::DW_CFA_restore_extended, dwarf::DW_CFA_undefined, dwarf::DW_CFA_same_value, dwarf::DW_CFA_register, dwarf::DW_CFA_remember_state, dwarf::DW_CFA_restore_state, dwarf::DW_CFA_def_cfa, dwarf::DW_CFA_def_cfa_register, dwarf::DW_CFA_def_cfa_offset, dwarf::DW_CFA_def_cfa_expression, dwarf::DW_CFA_expression, dwarf::DW_CFA_offset_extended_sf, dwarf::DW_CFA_def_cfa_sf, dwarf::DW_CFA_def_cfa_offset_sf, dwarf::DW_CFA_val_offset, dwarf::DW_CFA_val_offset_sf, dwarf::DW_CFA_val_expression, dwarf::DW_CFA_MIPS_advance_loc8, dwarf::DW_CFA_GNU_window_save, dwarf::DW_CFA_AARCH64_negate_ra_state, dwarf::DW_CFA_GNU_args_size}; dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false, /*Offset=*/0x0, /*Length=*/0xff); // See DWARF standard v3, section 7.23: low 6 bits are used to encode an // extended opcode. for (uint8_t Code = 0; Code <= 63; ++Code) { if (ValidExtendedOpcodes.count(Code)) continue; EXPECT_THAT_ERROR(parseCFI(TestCIE, Code), FailedWithMessage(("invalid extended CFI opcode 0x" + Twine::utohexstr(Code)) .str() .c_str())); } } // Here we test how truncated Call Frame Instructions are parsed. TEST(DWARFDebugFrame, ParseTruncatedCFITest) { dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false, /*Offset=*/0x0, /*Length=*/0xff); // Having an empty instructions list is fine. EXPECT_THAT_ERROR(parseCFI(TestCIE, {}), Succeeded()); // Unable to read an opcode, because the instructions list is empty, but we // say to the parser that it is not. EXPECT_THAT_ERROR( parseCFI(TestCIE, {}, /*Size=*/1), FailedWithMessage( "unexpected end of data at offset 0x0 while reading [0x0, 0x1)")); // Unable to read a truncated DW_CFA_offset instruction. EXPECT_THAT_ERROR( parseCFI(TestCIE, {dwarf::DW_CFA_offset}), FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " "malformed uleb128, extends past end")); // Unable to read a truncated DW_CFA_set_loc instruction. EXPECT_THAT_ERROR( parseCFI(TestCIE, {dwarf::DW_CFA_set_loc}), FailedWithMessage( "unexpected end of data at offset 0x1 while reading [0x1, 0x9)")); // Unable to read a truncated DW_CFA_advance_loc1 instruction. EXPECT_THAT_ERROR( parseCFI(TestCIE, {dwarf::DW_CFA_advance_loc1}), FailedWithMessage( "unexpected end of data at offset 0x1 while reading [0x1, 0x2)")); // Unable to read a truncated DW_CFA_advance_loc2 instruction. EXPECT_THAT_ERROR( parseCFI(TestCIE, {dwarf::DW_CFA_advance_loc2}), FailedWithMessage( "unexpected end of data at offset 0x1 while reading [0x1, 0x3)")); // Unable to read a truncated DW_CFA_advance_loc4 instruction. EXPECT_THAT_ERROR( parseCFI(TestCIE, {dwarf::DW_CFA_advance_loc4}), FailedWithMessage( "unexpected end of data at offset 0x1 while reading [0x1, 0x5)")); // A test for an instruction with a single ULEB128 operand. auto CheckOp_ULEB128 = [&](uint8_t Inst) { EXPECT_THAT_ERROR( parseCFI(TestCIE, Inst), FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " "malformed uleb128, extends past end")); }; for (uint8_t Inst : {dwarf::DW_CFA_restore_extended, dwarf::DW_CFA_undefined, dwarf::DW_CFA_same_value, dwarf::DW_CFA_def_cfa_register, dwarf::DW_CFA_def_cfa_offset, dwarf::DW_CFA_GNU_args_size}) CheckOp_ULEB128(Inst); // Unable to read a truncated DW_CFA_def_cfa_offset_sf instruction. EXPECT_THAT_ERROR( parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_offset_sf}), FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " "malformed sleb128, extends past end")); // A test for an instruction with two ULEB128 operands. auto CheckOp_ULEB128_ULEB128 = [&](uint8_t Inst) { EXPECT_THAT_ERROR( parseCFI(TestCIE, Inst), FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " "malformed uleb128, extends past end")); EXPECT_THAT_ERROR( parseCFI(TestCIE, {Inst, /*Op1=*/0}), FailedWithMessage("unable to decode LEB128 at offset 0x00000002: " "malformed uleb128, extends past end")); }; for (uint8_t Inst : {dwarf::DW_CFA_offset_extended, dwarf::DW_CFA_register, dwarf::DW_CFA_def_cfa, dwarf::DW_CFA_val_offset}) CheckOp_ULEB128_ULEB128(Inst); // A test for an instruction with two operands: ULEB128, SLEB128. auto CheckOp_ULEB128_SLEB128 = [&](uint8_t Inst) { EXPECT_THAT_ERROR( parseCFI(TestCIE, Inst), FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " "malformed uleb128, extends past end")); EXPECT_THAT_ERROR( parseCFI(TestCIE, {Inst, /*Op1=*/0}), FailedWithMessage("unable to decode LEB128 at offset 0x00000002: " "malformed sleb128, extends past end")); }; for (uint8_t Inst : {dwarf::DW_CFA_offset_extended_sf, dwarf::DW_CFA_def_cfa_sf, dwarf::DW_CFA_val_offset_sf}) CheckOp_ULEB128_SLEB128(Inst); // Unable to read a truncated DW_CFA_def_cfa_expression instruction. EXPECT_THAT_ERROR( parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression}), FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " "malformed uleb128, extends past end")); EXPECT_THAT_ERROR( parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression, /*expression length=*/0x1}), FailedWithMessage( "unexpected end of data at offset 0x2 while reading [0x2, 0x3)")); // The DW_CFA_def_cfa_expression can contain a zero length expression. EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression, /*ExprLen=*/0}), Succeeded()); // A test for an instruction with three operands: ULEB128, expression length // (ULEB128) and expression bytes. auto CheckOp_ULEB128_Expr = [&](uint8_t Inst) { EXPECT_THAT_ERROR( parseCFI(TestCIE, {Inst}), FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " "malformed uleb128, extends past end")); EXPECT_THAT_ERROR( parseCFI(TestCIE, {Inst, /*Op1=*/0}), FailedWithMessage("unable to decode LEB128 at offset 0x00000002: " "malformed uleb128, extends past end")); // A zero length expression is fine EXPECT_THAT_ERROR(parseCFI(TestCIE, {Inst, /*Op1=*/0, /*ExprLen=*/0}), Succeeded()); EXPECT_THAT_ERROR( parseCFI(TestCIE, {Inst, /*Op1=*/0, /*ExprLen=*/1}), FailedWithMessage( "unexpected end of data at offset 0x3 while reading [0x3, 0x4)")); }; for (uint8_t Inst : {dwarf::DW_CFA_expression, dwarf::DW_CFA_val_expression}) CheckOp_ULEB128_Expr(Inst); } } // end anonymous namespace