blob: fda6d265f8e950a348a5e3fb7bd5c8b49ddff3f9 [file] [log] [blame]
//===- 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<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);
}
static Error parseCFI(dwarf::FDE &FDE, ArrayRef<uint8_t> Instructions) {
DWARFDataExtractor Data(Instructions, /*IsLittleEndian=*/true,
/*AddressSize=*/8);
uint64_t Offset = 0;
return FDE.cfis().parse(Data, &Offset, Instructions.size());
}
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);
}
void expectDumpResult(const dwarf::UnwindLocation &Loc,
StringRef ExpectedFirstLine) {
std::string Output;
raw_string_ostream OS(Output);
OS << Loc;
OS.flush();
StringRef FirstLine = StringRef(Output).split('\n').first;
EXPECT_EQ(FirstLine, ExpectedFirstLine);
}
TEST(DWARFDebugFrame, DumpUnwindLocations) {
// Test constructing unwind locations and dumping each kind.
constexpr int32_t PlusOff = 8;
constexpr int32_t MinusOff = -8;
constexpr uint8_t RegNum = 12;
expectDumpResult(dwarf::UnwindLocation::createUnspecified(), "unspecified");
expectDumpResult(dwarf::UnwindLocation::createUndefined(), "undefined");
expectDumpResult(dwarf::UnwindLocation::createSame(), "same");
expectDumpResult(dwarf::UnwindLocation::createIsCFAPlusOffset(PlusOff),
"CFA+8");
expectDumpResult(dwarf::UnwindLocation::createIsCFAPlusOffset(MinusOff),
"CFA-8");
expectDumpResult(dwarf::UnwindLocation::createAtCFAPlusOffset(PlusOff),
"[CFA+8]");
expectDumpResult(dwarf::UnwindLocation::createAtCFAPlusOffset(MinusOff),
"[CFA-8]");
expectDumpResult(
dwarf::UnwindLocation::createIsRegisterPlusOffset(RegNum, PlusOff),
"reg12+8");
expectDumpResult(
dwarf::UnwindLocation::createIsRegisterPlusOffset(RegNum, MinusOff),
"reg12-8");
expectDumpResult(
dwarf::UnwindLocation::createAtRegisterPlusOffset(RegNum, PlusOff),
"[reg12+8]");
expectDumpResult(
dwarf::UnwindLocation::createAtRegisterPlusOffset(RegNum, MinusOff),
"[reg12-8]");
expectDumpResult(dwarf::UnwindLocation::createIsConstant(12), "12");
expectDumpResult(dwarf::UnwindLocation::createIsConstant(-32), "-32");
}
void expectDumpResult(const dwarf::RegisterLocations &Locs,
StringRef ExpectedFirstLine) {
std::string Output;
raw_string_ostream OS(Output);
OS << Locs;
OS.flush();
StringRef FirstLine = StringRef(Output).split('\n').first;
EXPECT_EQ(FirstLine, ExpectedFirstLine);
}
TEST(DWARFDebugFrame, RegisterLocations) {
// Test the functionality of the RegisterLocations class.
dwarf::RegisterLocations Locs;
expectDumpResult(Locs, "");
EXPECT_FALSE(Locs.hasLocations());
// Set a register location for reg12 to unspecified and verify it dumps
// correctly.
Locs.setRegisterLocation(12, dwarf::UnwindLocation::createUnspecified());
EXPECT_TRUE(Locs.hasLocations());
expectDumpResult(Locs, "reg12=unspecified");
// Replace the register location for reg12 to "same" and verify it dumps
// correctly after it is modified
Locs.setRegisterLocation(12, dwarf::UnwindLocation::createSame());
EXPECT_TRUE(Locs.hasLocations());
expectDumpResult(Locs, "reg12=same");
// Remove the register location for reg12 verify it dumps correctly after it
// is removed.
Locs.removeRegisterLocation(12);
EXPECT_FALSE(Locs.hasLocations());
expectDumpResult(Locs, "");
// Verify multiple registers added to the list dump correctly.
auto Reg12Loc = dwarf::UnwindLocation::createAtCFAPlusOffset(4);
auto Reg13Loc = dwarf::UnwindLocation::createAtCFAPlusOffset(8);
auto Reg14Loc = dwarf::UnwindLocation::createSame();
Locs.setRegisterLocation(12, Reg12Loc);
Locs.setRegisterLocation(13, Reg13Loc);
Locs.setRegisterLocation(14, Reg14Loc);
EXPECT_TRUE(Locs.hasLocations());
expectDumpResult(Locs, "reg12=[CFA+4], reg13=[CFA+8], reg14=same");
// Verify RegisterLocations::getRegisterLocation() works as expected.
Optional<dwarf::UnwindLocation> OptionalLoc;
OptionalLoc = Locs.getRegisterLocation(0);
EXPECT_FALSE(OptionalLoc.hasValue());
OptionalLoc = Locs.getRegisterLocation(12);
EXPECT_TRUE(OptionalLoc.hasValue());
EXPECT_EQ(*OptionalLoc, Reg12Loc);
OptionalLoc = Locs.getRegisterLocation(13);
EXPECT_TRUE(OptionalLoc.hasValue());
EXPECT_EQ(*OptionalLoc, Reg13Loc);
OptionalLoc = Locs.getRegisterLocation(14);
EXPECT_TRUE(OptionalLoc.hasValue());
EXPECT_EQ(*OptionalLoc, Reg14Loc);
// Verify registers are correctly removed when multiple exist in the list.
Locs.removeRegisterLocation(13);
EXPECT_FALSE(Locs.getRegisterLocation(13).hasValue());
EXPECT_TRUE(Locs.hasLocations());
expectDumpResult(Locs, "reg12=[CFA+4], reg14=same");
Locs.removeRegisterLocation(14);
EXPECT_FALSE(Locs.getRegisterLocation(14).hasValue());
EXPECT_TRUE(Locs.hasLocations());
expectDumpResult(Locs, "reg12=[CFA+4]");
Locs.removeRegisterLocation(12);
EXPECT_FALSE(Locs.getRegisterLocation(12).hasValue());
EXPECT_FALSE(Locs.hasLocations());
expectDumpResult(Locs, "");
}
TEST(DWARFDebugFrame, UnwindTableErrorNonAscendingFDERows) {
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition.
constexpr uint8_t Reg = 12;
constexpr uint8_t Offset = 32;
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, Reg, Offset}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that have valid
// syntax, but will cause an error when we parse them into a UnwindTable.
// Here we encode two DW_CFA_set_loc opcodes:
// DW_CFA_set_loc(0x1100)
// DW_CFA_set_loc(0x1000)
// These opcodes cause a new row to be appended to the rows in a UnwindTable
// and the resulting rows are not in ascending address order and should cause
// a state machine error.
EXPECT_THAT_ERROR(
parseCFI(TestFDE, {dwarf::DW_CFA_set_loc, 0x00, 0x11, 0, 0, 0, 0, 0, 0,
dwarf::DW_CFA_set_loc, 0x00, 0x10, 0, 0, 0, 0, 0, 0}),
Succeeded());
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(),
FailedWithMessage("DW_CFA_set_loc with adrress 0x1000 which"
" must be greater than the current row "
"address 0x1100"));
}
TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_restore_state) {
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition.
constexpr uint8_t Reg = 12;
constexpr uint8_t Offset = 32;
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, Reg, Offset}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that have valid
// syntax, but will cause an error when we parse them into a UnwindTable.
// Here we encode a DW_CFA_restore_state opcode that was not preceded by a
// DW_CFA_remember_state, and an error should be returned.
EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_restore_state}),
Succeeded());
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(),
FailedWithMessage("DW_CFA_restore_state without a matching "
"previous DW_CFA_remember_state"));
}
TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_GNU_window_save) {
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition.
constexpr uint8_t Reg = 12;
constexpr uint8_t Offset = 32;
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, Reg, Offset}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that have valid
// syntax, but will cause an error when we parse them into a UnwindTable.
// Here we encode a DW_CFA_GNU_window_save that is not supported. I have not
// found any documentation that describes what this does after some brief
// searching.
EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_GNU_window_save}),
Succeeded());
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(),
FailedWithMessage("DW_CFA opcode 0x2d is not supported for "
"architecture x86_64"));
}
TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_def_cfa_offset) {
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has an invalid CFA definition. We do this so we can try
// and use a DW_CFA_def_cfa_register opcode in the FDE and get an appropriate
// error back.
EXPECT_THAT_ERROR(parseCFI(TestCIE, {}), Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that have valid
// syntax, but will cause an error when we parse them into a UnwindTable.
// Here we encode a DW_CFA_def_cfa_offset with a offset of 16, but our CIE
// didn't define the CFA in terms of a register plus offset, so this should
// cause an error.
EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_def_cfa_offset, 16}),
Succeeded());
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(),
FailedWithMessage("DW_CFA_def_cfa_offset found when CFA "
"rule was not RegPlusOffset"));
}
TEST(DWARFDebugFrame, UnwindTableDefCFAOffsetSFCFAError) {
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has an invalid CFA definition. We do this so we can try
// and use a DW_CFA_def_cfa_offset_sf opcode in the FDE and get an
// appropriate error back.
EXPECT_THAT_ERROR(parseCFI(TestCIE, {}), Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that have valid
// syntax, but will cause an error when we parse them into a UnwindTable.
// Here we encode a DW_CFA_def_cfa_offset_sf with a offset of 4, but our CIE
// didn't define the CFA in terms of a register plus offset, so this should
// cause an error.
EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_def_cfa_offset_sf, 4}),
Succeeded());
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(),
FailedWithMessage("DW_CFA_def_cfa_offset_sf found when CFA "
"rule was not RegPlusOffset"));
}
TEST(DWARFDebugFrame, UnwindTable_DW_CFA_def_cfa_register) {
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has only defines the CFA register with no offset. Some
// architectures do this and we must ensure that we set the CFA value to be
// equal to that register with no offset.
constexpr uint8_t CFAReg = 12;
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_register, CFAReg}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that have valid
// syntax, but will cause an error when we parse them into a UnwindTable.
// Here we encode a DW_CFA_def_cfa_register with a register number of 12, but
// our CIE didn't define the CFA in terms of a register plus offset, so this
// should cause an error.
EXPECT_THAT_ERROR(parseCFI(TestFDE, {}), Succeeded());
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
EXPECT_EQ(Rows[0].getCFAValue(),
dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg, 0));
}
TEST(DWARFDebugFrame, UnwindTableRowPushingOpcodes) {
// Test all opcodes that should end up pushing a UnwindRow into a UnwindTable.
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition and a single register unwind
// rule for register that we will verify is in all of the pushed rows.
constexpr uint8_t CFAReg = 12;
constexpr uint8_t CFAOffset = 32;
constexpr uint8_t Reg = 13;
constexpr uint8_t InReg = 14;
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, CFAReg, CFAOffset,
dwarf::DW_CFA_register, Reg, InReg}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that use all of the
// row pushing opcodes. This will verify that all opcodes that should create
// a row are correctly working. Each opcode will push a row prior to
// advancing the address, and then a row will be automatically pushed at the
// end of the parsing, so we should end up with 6 rows starting at address
// 0x1000 (from the FDE) and incrementing each one by 4 * CodeAlignmentFactor
// from the CIE.
EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_advance_loc | 4,
dwarf::DW_CFA_advance_loc1,
4,
dwarf::DW_CFA_advance_loc2,
4,
0,
dwarf::DW_CFA_advance_loc4,
4,
0,
0,
0,
dwarf::DW_CFA_set_loc,
0x14,
0x10,
0,
0,
0,
0,
0,
0}),
Succeeded());
// Create locations that we expect the UnwindRow objects to contain after
// parsing the DWARF call frame instructions.
dwarf::RegisterLocations VerifyLocs;
VerifyLocs.setRegisterLocation(
Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
ASSERT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 6u);
EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
EXPECT_EQ(Rows[0].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
EXPECT_EQ(Rows[1].getAddress(), 0x1004u);
EXPECT_EQ(Rows[1].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs);
EXPECT_EQ(Rows[2].getAddress(), 0x1008u);
EXPECT_EQ(Rows[2].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[2].getRegisterLocations(), VerifyLocs);
EXPECT_EQ(Rows[3].getAddress(), 0x100cu);
EXPECT_EQ(Rows[3].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[3].getRegisterLocations(), VerifyLocs);
EXPECT_EQ(Rows[4].getAddress(), 0x1010u);
EXPECT_EQ(Rows[4].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[4].getRegisterLocations(), VerifyLocs);
EXPECT_EQ(Rows[5].getAddress(), 0x1014u);
EXPECT_EQ(Rows[5].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[5].getRegisterLocations(), VerifyLocs);
}
TEST(DWARFDebugFrame, UnwindTable_DW_CFA_restore) {
// Test that DW_CFA_restore works as expected when parsed in the state
// machine.
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition and a single register unwind
// rule for register that we will verify is in all of the pushed rows.
constexpr uint8_t CFAReg = 12;
constexpr uint8_t CFAOffset = 32;
constexpr uint8_t Reg = 13;
constexpr uint8_t InReg = 14;
constexpr int32_t RegCFAOffset = -8;
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, CFAReg, CFAOffset,
dwarf::DW_CFA_register, Reg, InReg}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that changes the rule
// for register "Reg" to be [CFA-8], then push a row, and then restore the
// register unwind rule for "Reg" using DW_CFA_restore. We should end up with
// two rows:
// - one with Reg = [CFA-8]
// - one with Reg = InReg
EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_offset | Reg, 1,
dwarf::DW_CFA_advance_loc | 4,
dwarf::DW_CFA_restore | Reg}),
Succeeded());
// Create locations that we expect the UnwindRow objects to contain after
// parsing the DWARF call frame instructions.
dwarf::RegisterLocations VerifyLocs1;
VerifyLocs1.setRegisterLocation(
Reg, dwarf::UnwindLocation::createAtCFAPlusOffset(RegCFAOffset));
dwarf::RegisterLocations VerifyLocs2;
VerifyLocs2.setRegisterLocation(
Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 2u);
EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
EXPECT_EQ(Rows[0].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs1);
EXPECT_EQ(Rows[1].getAddress(), 0x1004u);
EXPECT_EQ(Rows[1].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs2);
}
TEST(DWARFDebugFrame, UnwindTable_DW_CFA_restore_extended) {
// Test that DW_CFA_restore works as expected when parsed in the state
// machine.
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition and a single register unwind
// rule for register that we will verify is in all of the pushed rows.
constexpr uint8_t CFAReg = 12;
constexpr uint8_t CFAOffset = 32;
constexpr uint8_t Reg = 13;
constexpr uint8_t InReg = 14;
constexpr int32_t RegCFAOffset = -8;
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, CFAReg, CFAOffset,
dwarf::DW_CFA_register, Reg, InReg}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that changes the rule
// for register "Reg" to be [CFA-8], then push a row, and then restore the
// register unwind rule for "Reg" using DW_CFA_restore_extended. We should
// end up with two rows:
// - one with Reg = [CFA-8]
// - one with Reg = InReg
EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_offset | Reg, 1,
dwarf::DW_CFA_advance_loc | 4,
dwarf::DW_CFA_restore_extended, Reg}),
Succeeded());
// Create locations that we expect the UnwindRow objects to contain after
// parsing the DWARF call frame instructions.
dwarf::RegisterLocations VerifyLocs1;
VerifyLocs1.setRegisterLocation(
Reg, dwarf::UnwindLocation::createAtCFAPlusOffset(RegCFAOffset));
dwarf::RegisterLocations VerifyLocs2;
VerifyLocs2.setRegisterLocation(
Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 2u);
EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
EXPECT_EQ(Rows[0].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs1);
EXPECT_EQ(Rows[1].getAddress(), 0x1004u);
EXPECT_EQ(Rows[1].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs2);
}
TEST(DWARFDebugFrame, UnwindTable_DW_CFA_offset) {
// Test that DW_CFA_offset, DW_CFA_offset_extended and
// DW_CFA_offset_extended_sf work as expected when parsed in the state
// machine.
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition and a single register unwind
// rule for register that we will verify is in all of the pushed rows.
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that changes the
// unwind rules for the follwing registers:
// Reg1 = [CFA-8]
// Reg2 = [CFA-16]
// Reg3 = [CFA+8]
constexpr uint8_t Reg1 = 14;
constexpr uint8_t Reg2 = 15;
constexpr uint8_t Reg3 = 16;
constexpr uint8_t Neg1SLEB = 0x7f;
EXPECT_THAT_ERROR(
parseCFI(TestFDE,
{dwarf::DW_CFA_offset | Reg1, 1, dwarf::DW_CFA_offset_extended,
Reg2, 2, dwarf::DW_CFA_offset_extended_sf, Reg3, Neg1SLEB}),
Succeeded());
// Create locations that we expect the UnwindRow objects to contain after
// parsing the DWARF call frame instructions.
dwarf::RegisterLocations VerifyLocs;
VerifyLocs.setRegisterLocation(
Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));
VerifyLocs.setRegisterLocation(
Reg2, dwarf::UnwindLocation::createAtCFAPlusOffset(-16));
VerifyLocs.setRegisterLocation(
Reg3, dwarf::UnwindLocation::createAtCFAPlusOffset(8));
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
}
TEST(DWARFDebugFrame, UnwindTable_DW_CFA_val_offset) {
// Test that DW_CFA_val_offset and DW_CFA_val_offset_sf work as expected when
// parsed in the state machine.
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition and a single register unwind
// rule for register that we will verify is in all of the pushed rows.
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that changes the
// unwind rules for the follwing registers:
// Reg1 = [CFA-8]
// Reg2 = [CFA-16]
// Reg3 = [CFA+8]
constexpr uint8_t Reg1 = 14;
constexpr uint8_t Reg2 = 15;
constexpr uint8_t Neg1SLEB = 0x7f;
EXPECT_THAT_ERROR(
parseCFI(TestFDE, {dwarf::DW_CFA_val_offset, Reg1, 1,
dwarf::DW_CFA_val_offset_sf, Reg2, Neg1SLEB}),
Succeeded());
// Create locations that we expect the UnwindRow objects to contain after
// parsing the DWARF call frame instructions.
dwarf::RegisterLocations VerifyLocs;
VerifyLocs.setRegisterLocation(
Reg1, dwarf::UnwindLocation::createIsCFAPlusOffset(-8));
VerifyLocs.setRegisterLocation(
Reg2, dwarf::UnwindLocation::createIsCFAPlusOffset(8));
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
}
TEST(DWARFDebugFrame, UnwindTable_DW_CFA_nop) {
// Test that DW_CFA_nop works as expected when parsed in the state machine.
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition and a single register unwind
// rule for register that we will verify is in all of the pushed rows.
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that changes the
// unwind rules for the follwing registers:
// Reg1 = [CFA-8]
// The opcodes for setting Reg1 are preceded by a DW_CFA_nop.
constexpr uint8_t Reg1 = 14;
EXPECT_THAT_ERROR(
parseCFI(TestFDE, {dwarf::DW_CFA_nop, dwarf::DW_CFA_offset | Reg1, 1}),
Succeeded());
// Create locations that we expect the UnwindRow objects to contain after
// parsing the DWARF call frame instructions.
dwarf::RegisterLocations VerifyLocs;
VerifyLocs.setRegisterLocation(
Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
}
TEST(DWARFDebugFrame, UnwindTable_DW_CFA_remember_state) {
// Test that DW_CFA_remember_state and DW_CFA_restore_state work as expected
// when parsed in the state machine.
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition and a single register unwind
// rule for register that we will verify is in all of the pushed rows.
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that encodes the
// follwing rows:
// 0x1000: CFA=reg12+32: Reg1=[CFA-8]
// 0x1004: CFA=reg12+32: Reg1=[CFA-8] Reg2=[CFA-16]
// 0x1008: CFA=reg12+32: Reg1=[CFA-8] Reg2=[CFA-16] Reg3=[CFA-24]
// 0x100C: CFA=reg12+32: Reg1=[CFA-8] Reg2=[CFA-16]
// 0x1010: CFA=reg12+32: Reg1=[CFA-8]
// This state machine will:
// - set Reg1 location
// - push a row (from DW_CFA_advance_loc)
// - remember the state
// - set Reg2 location
// - push a row (from DW_CFA_advance_loc)
// - remember the state
// - set Reg3 location
// - push a row (from DW_CFA_advance_loc)
// - remember the state where Reg1 and Reg2 were set
// - push a row (from DW_CFA_advance_loc)
// - remember the state where only Reg1 was set
// - push a row (automatically at the end of instruction parsing)
// Then we verify that all registers are correct in all generated rows.
constexpr uint8_t Reg1 = 14;
constexpr uint8_t Reg2 = 15;
constexpr uint8_t Reg3 = 16;
EXPECT_THAT_ERROR(
parseCFI(TestFDE,
{dwarf::DW_CFA_offset | Reg1, 1, dwarf::DW_CFA_advance_loc | 4,
dwarf::DW_CFA_remember_state, dwarf::DW_CFA_offset | Reg2, 2,
dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_remember_state,
dwarf::DW_CFA_offset | Reg3, 3, dwarf::DW_CFA_advance_loc | 4,
dwarf::DW_CFA_restore_state, dwarf::DW_CFA_advance_loc | 4,
dwarf::DW_CFA_restore_state}),
Succeeded());
// Create locations that we expect the UnwindRow objects to contain after
// parsing the DWARF call frame instructions.
dwarf::RegisterLocations VerifyLocs1;
VerifyLocs1.setRegisterLocation(
Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));
dwarf::RegisterLocations VerifyLocs2;
VerifyLocs2.setRegisterLocation(
Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));
VerifyLocs2.setRegisterLocation(
Reg2, dwarf::UnwindLocation::createAtCFAPlusOffset(-16));
dwarf::RegisterLocations VerifyLocs3;
VerifyLocs3.setRegisterLocation(
Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8));
VerifyLocs3.setRegisterLocation(
Reg2, dwarf::UnwindLocation::createAtCFAPlusOffset(-16));
VerifyLocs3.setRegisterLocation(
Reg3, dwarf::UnwindLocation::createAtCFAPlusOffset(-24));
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 5u);
EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs1);
EXPECT_EQ(Rows[1].getAddress(), 0x1004u);
EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs2);
EXPECT_EQ(Rows[2].getAddress(), 0x1008u);
EXPECT_EQ(Rows[2].getRegisterLocations(), VerifyLocs3);
EXPECT_EQ(Rows[3].getAddress(), 0x100Cu);
EXPECT_EQ(Rows[3].getRegisterLocations(), VerifyLocs2);
EXPECT_EQ(Rows[4].getAddress(), 0x1010u);
EXPECT_EQ(Rows[4].getRegisterLocations(), VerifyLocs1);
}
TEST(DWARFDebugFrame, UnwindTable_DW_CFA_undefined) {
// Test that DW_CFA_undefined works as expected when parsed in the state
// machine.
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition and a single register unwind
// rule for register that we will verify is in all of the pushed rows.
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that encodes the
// follwing rows:
// 0x1000: CFA=reg12+32: Reg1=undefined
// Then we verify that all registers are correct in all generated rows.
constexpr uint8_t Reg1 = 14;
EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_undefined, Reg1}),
Succeeded());
// Create locations that we expect the UnwindRow objects to contain after
// parsing the DWARF call frame instructions.
dwarf::RegisterLocations VerifyLocs;
VerifyLocs.setRegisterLocation(Reg1,
dwarf::UnwindLocation::createUndefined());
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
}
TEST(DWARFDebugFrame, UnwindTable_DW_CFA_same_value) {
// Test that DW_CFA_same_value works as expected when parsed in the state
// machine.
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition and a single register unwind
// rule for register that we will verify is in all of the pushed rows.
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that encodes the
// follwing rows:
// 0x1000: CFA=reg12+32: Reg1=same
// Then we verify that all registers are correct in all generated rows.
constexpr uint8_t Reg1 = 14;
EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_same_value, Reg1}),
Succeeded());
// Create locations that we expect the UnwindRow objects to contain after
// parsing the DWARF call frame instructions.
dwarf::RegisterLocations VerifyLocs;
VerifyLocs.setRegisterLocation(Reg1, dwarf::UnwindLocation::createSame());
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
}
TEST(DWARFDebugFrame, UnwindTable_DW_CFA_register) {
// Test that DW_CFA_register works as expected when parsed in the state
// machine.
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition and a single register unwind
// rule for register that we will verify is in all of the pushed rows.
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that encodes the
// follwing rows:
// 0x1000: CFA=reg12+32: Reg1=same
// Then we verify that all registers are correct in all generated rows.
constexpr uint8_t Reg = 13;
constexpr uint8_t InReg = 14;
EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_register, Reg, InReg}),
Succeeded());
// Create locations that we expect the UnwindRow objects to contain after
// parsing the DWARF call frame instructions.
dwarf::RegisterLocations VerifyLocs;
VerifyLocs.setRegisterLocation(
Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
}
TEST(DWARFDebugFrame, UnwindTable_DW_CFA_expression) {
// Test that DW_CFA_expression works as expected when parsed in the state
// machine.
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition and a single register unwind
// rule for register that we will verify is in all of the pushed rows.
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that encodes the
// follwing rows:
// 0x1000: CFA=reg12+32: Reg1=DWARFExpr(DW_OP_reg12)
// Then we verify that all registers are correct in all generated rows.
constexpr uint8_t Reg = 13;
constexpr uint8_t AddrSize = 8;
std::vector<uint8_t> CFIBytes = {dwarf::DW_CFA_expression, Reg, 1,
dwarf::DW_OP_reg12};
EXPECT_THAT_ERROR(parseCFI(TestFDE, CFIBytes), Succeeded());
// Create locations that we expect the UnwindRow objects to contain after
// parsing the DWARF call frame instructions.
dwarf::RegisterLocations VerifyLocs;
std::vector<uint8_t> ExprBytes = {dwarf::DW_OP_reg12};
DataExtractor ExprData(ExprBytes, true, AddrSize);
DWARFExpression Expr(ExprData, AddrSize);
VerifyLocs.setRegisterLocation(
Reg, dwarf::UnwindLocation::createAtDWARFExpression(Expr));
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
}
TEST(DWARFDebugFrame, UnwindTable_DW_CFA_val_expression) {
// Test that DW_CFA_val_expression works as expected when parsed in the state
// machine.
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition and a single register unwind
// rule for register that we will verify is in all of the pushed rows.
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, 12, 32}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that encodes the
// follwing rows:
// 0x1000: CFA=reg12+32: Reg1=DWARFExpr(DW_OP_reg12)
// Then we verify that all registers are correct in all generated rows.
constexpr uint8_t Reg = 13;
constexpr uint8_t AddrSize = 8;
std::vector<uint8_t> CFIBytes = {dwarf::DW_CFA_val_expression, Reg, 1,
dwarf::DW_OP_reg12};
EXPECT_THAT_ERROR(parseCFI(TestFDE, CFIBytes), Succeeded());
// Create locations that we expect the UnwindRow objects to contain after
// parsing the DWARF call frame instructions.
dwarf::RegisterLocations VerifyLocs;
std::vector<uint8_t> ExprBytes = {dwarf::DW_OP_reg12};
DataExtractor ExprData(ExprBytes, true, AddrSize);
DWARFExpression Expr(ExprData, AddrSize);
VerifyLocs.setRegisterLocation(
Reg, dwarf::UnwindLocation::createIsDWARFExpression(Expr));
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 1u);
EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
}
TEST(DWARFDebugFrame, UnwindTable_DW_CFA_def_cfa) {
// Test that DW_CFA_def_cfa, DW_CFA_def_cfa_sf, DW_CFA_def_cfa_register,
// DW_CFA_def_cfa_offset, and DW_CFA_def_cfa_offset_sf works as expected when
// parsed in the state machine.
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
/*Offset=*/0x0,
/*Length=*/0xff);
dwarf::FDE TestFDE(/*IsDWARF64=*/true,
/*Offset=*/0x3333abcdabcd,
/*Length=*/0x4444abcdabcd,
/*CIEPointer=*/0x1111abcdabcd,
/*InitialLocation=*/0x1000,
/*AddressRange=*/0x1000,
/*Cie=*/&TestCIE,
/*LSDAAddress=*/None,
/*Arch=*/Triple::x86_64);
// Make a CIE that has a valid CFA definition and a single register unwind
// rule for register that we will verify is in all of the pushed rows.
constexpr uint8_t CFAReg1 = 12;
constexpr uint8_t CFAOff1 = 32;
constexpr uint8_t CFAReg2 = 13;
constexpr uint8_t CFAOff2 = 48;
constexpr uint8_t Reg = 13;
constexpr uint8_t InReg = 14;
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa, CFAReg1, CFAOff1,
dwarf::DW_CFA_register, Reg, InReg}),
Succeeded());
// Make a FDE with DWARF call frame instruction opcodes that use all of the
// DW_CFA_def_cfa* opcodes. This will verify that all opcodes that should
// create a row are correctly working.
EXPECT_THAT_ERROR(
parseCFI(
TestFDE,
{
dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_def_cfa_register,
CFAReg2, dwarf::DW_CFA_advance_loc | 4,
dwarf::DW_CFA_def_cfa_offset, CFAOff2,
dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_def_cfa_offset_sf,
0x7c, // -4 SLEB to make offset = 32 (CFAOff1)
dwarf::DW_CFA_advance_loc | 4, dwarf::DW_CFA_def_cfa_sf, CFAReg1,
0x7a, // -6 SLEB to make CFA offset 48 (CFAOff2)
}),
Succeeded());
// Create locations that we expect the UnwindRow objects to contain after
// parsing the DWARF call frame instructions.
dwarf::RegisterLocations VerifyLocs;
VerifyLocs.setRegisterLocation(
Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0));
// Verify we catch state machine error.
Expected<dwarf::UnwindTable> RowsOrErr = dwarf::UnwindTable::create(&TestFDE);
EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded());
const dwarf::UnwindTable &Rows = RowsOrErr.get();
EXPECT_EQ(Rows.size(), 5u);
EXPECT_EQ(Rows[0].getAddress(), 0x1000u);
EXPECT_EQ(
Rows[0].getCFAValue(),
dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg1, CFAOff1));
EXPECT_EQ(Rows[0].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[0].getRegisterLocations(), VerifyLocs);
EXPECT_EQ(Rows[1].getAddress(), 0x1004u);
EXPECT_EQ(
Rows[1].getCFAValue(),
dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg2, CFAOff1));
EXPECT_EQ(Rows[1].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[1].getRegisterLocations(), VerifyLocs);
EXPECT_EQ(Rows[2].getAddress(), 0x1008u);
EXPECT_EQ(
Rows[2].getCFAValue(),
dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg2, CFAOff2));
EXPECT_EQ(Rows[2].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[2].getRegisterLocations(), VerifyLocs);
EXPECT_EQ(Rows[3].getAddress(), 0x100cu);
EXPECT_EQ(
Rows[3].getCFAValue(),
dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg2, CFAOff1));
EXPECT_EQ(Rows[3].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[3].getRegisterLocations(), VerifyLocs);
EXPECT_EQ(Rows[4].getAddress(), 0x1010u);
EXPECT_EQ(
Rows[4].getCFAValue(),
dwarf::UnwindLocation::createIsRegisterPlusOffset(CFAReg1, CFAOff2));
EXPECT_EQ(Rows[4].getRegisterLocations().size(), 1u);
EXPECT_EQ(Rows[4].getRegisterLocations(), VerifyLocs);
}
} // end anonymous namespace