DWARFDebugFrameTest.cpp 12.9 KB
//===- 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, /*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, /*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<uint8_t> Instructions,
                      Optional<uint64_t> 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<uint8_t> 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