326 lines
13 KiB
C++
326 lines
13 KiB
C++
|
//===- 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);
|
||
|
}
|
||
|
|
||
|
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
|