#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h"
#include "llvm/DebugInfo/DWARF/DWARFDie.h"
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCCodeEmitter.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCTargetOptions.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace dwarf;
namespace {
class DWARFExpressionCopyBytesTest : public ::testing::Test {
public:
const char *TripleName = "x86_64-pc-linux";
std::unique_ptr<MCRegisterInfo> MRI;
std::unique_ptr<MCAsmInfo> MAI;
std::unique_ptr<const MCSubtargetInfo> STI;
const Target *TheTarget;
DWARFExpressionCopyBytesTest() {
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
std::string ErrorStr;
TheTarget = TargetRegistry::lookupTarget(TripleName, ErrorStr);
if (!TheTarget)
return;
MRI.reset(TheTarget->createMCRegInfo(TripleName));
MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCTargetOptions()));
STI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", ""));
}
struct StreamerContext {
std::unique_ptr<MCObjectFileInfo> MOFI;
std::unique_ptr<MCContext> Ctx;
std::unique_ptr<const MCInstrInfo> MII;
std::unique_ptr<MCStreamer> Streamer;
};
StreamerContext createStreamer(raw_pwrite_stream &OS);
SmallString<0> emitObjFile(StringRef ExprBytes);
void parseCFIsAndCheckExpression(const llvm::object::ObjectFile &E,
ArrayRef<uint8_t> Expected);
void readAndCheckObjFile(StringRef ObjFileData, ArrayRef<uint8_t> Expected);
void testExpr(ArrayRef<uint8_t> ExprData);
};
}
DWARFExpressionCopyBytesTest::StreamerContext
DWARFExpressionCopyBytesTest::createStreamer(raw_pwrite_stream &OS) {
StreamerContext Res;
Res.Ctx =
std::make_unique<MCContext>(Triple(TripleName), MAI.get(), MRI.get(),
nullptr);
Res.MOFI.reset(TheTarget->createMCObjectFileInfo(*Res.Ctx.get(),
false));
Res.Ctx->setObjectFileInfo(Res.MOFI.get());
Res.MII.reset(TheTarget->createMCInstrInfo());
MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*Res.MII, *Res.Ctx);
MCAsmBackend *MAB =
TheTarget->createMCAsmBackend(*STI, *MRI, MCTargetOptions());
std::unique_ptr<MCObjectWriter> OW = MAB->createObjectWriter(OS);
Res.Streamer.reset(TheTarget->createMCObjectStreamer(
Triple(TripleName), *Res.Ctx, std::unique_ptr<MCAsmBackend>(MAB),
std::move(OW), std::unique_ptr<MCCodeEmitter>(MCE), *STI,
false,
false,
false));
return Res;
}
SmallString<0> DWARFExpressionCopyBytesTest::emitObjFile(StringRef ExprBytes) {
auto EncodeDefCfaExpr = [&](StringRef Bytes) {
std::string Str;
raw_string_ostream OS(Str);
OS << static_cast<uint8_t>(dwarf::DW_CFA_def_cfa_expression);
encodeULEB128(Bytes.size(), OS);
OS << Bytes;
return Str;
};
SmallString<0> Storage;
raw_svector_ostream VecOS(Storage);
StreamerContext C = createStreamer(VecOS);
C.Streamer->initSections(false, *STI);
MCSection *Section = C.MOFI->getTextSection();
Section->setHasInstructions(true);
C.Streamer->switchSection(Section);
C.Streamer->emitCFIStartProc(true);
auto Str = EncodeDefCfaExpr(ExprBytes);
C.Streamer->emitCFIEscape(Str);
C.Streamer->emitNops(4, 1, SMLoc(), *STI);
C.Streamer->emitCFIEndProc();
C.Streamer->finish();
return Storage;
}
void DWARFExpressionCopyBytesTest::parseCFIsAndCheckExpression(
const llvm::object::ObjectFile &E, ArrayRef<uint8_t> Expected) {
auto FetchFirstCfaExpression =
[](const DWARFDebugFrame &EHFrame) -> Optional<CFIProgram::Instruction> {
for (const dwarf::FrameEntry &Entry : EHFrame.entries()) {
const auto *CurFDE = dyn_cast<dwarf::FDE>(&Entry);
if (!CurFDE)
continue;
for (const CFIProgram::Instruction &Instr : CurFDE->cfis()) {
if (Instr.Opcode != dwarf::DW_CFA_def_cfa_expression)
continue;
return Instr;
}
}
return NoneType();
};
std::unique_ptr<DWARFContext> Ctx = DWARFContext::create(E);
const DWARFDebugFrame *EHFrame = cantFail(Ctx->getEHFrame());
ASSERT_NE(nullptr, EHFrame);
auto CfiInstr = FetchFirstCfaExpression(*EHFrame);
ASSERT_TRUE(CfiInstr);
DWARFExpression Expr = *(CfiInstr->Expression);
StringRef ExprData = Expr.getData();
EXPECT_EQ(ExprData.size(), Expected.size());
for (unsigned I = 0, E = ExprData.size(); I != E; ++I) {
EXPECT_EQ(static_cast<uint8_t>(ExprData[I]), Expected[I]);
}
}
void DWARFExpressionCopyBytesTest::readAndCheckObjFile(
StringRef ObjFileData, ArrayRef<uint8_t> Expected) {
std::unique_ptr<MemoryBuffer> MB =
MemoryBuffer::getMemBuffer(ObjFileData, "", false);
std::unique_ptr<object::Binary> Bin =
cantFail(llvm::object::createBinary(MB->getMemBufferRef()));
if (auto *E = dyn_cast<llvm::object::ELFObjectFileBase>(&*Bin)) {
parseCFIsAndCheckExpression(*E, Expected);
}
}
void DWARFExpressionCopyBytesTest::testExpr(ArrayRef<uint8_t> ExprData) {
if (!MRI)
GTEST_SKIP();
DataExtractor DE(ExprData, true, 8);
DWARFExpression Expr(DE, 8);
const SmallString<0> EmittedBinContents = emitObjFile(Expr.getData());
readAndCheckObjFile(EmittedBinContents.str(), ExprData);
}
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_reg0) { testExpr({DW_OP_reg0}); }
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_reg10) { testExpr({DW_OP_reg10}); }
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_regx) {
testExpr({DW_OP_regx, 0x80, 0x02});
}
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg0) {
testExpr({DW_OP_breg0, 0x04});
}
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg0_large_offset) {
testExpr({DW_OP_breg0, 0x80, 0x02});
}
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg13) {
testExpr({DW_OP_breg13, 0x10});
}
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg13_zero_offset) {
testExpr({DW_OP_breg13, 0x00});
}
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg0_negative) {
testExpr({DW_OP_breg0, 0x70});
}
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_bregx) {
testExpr({DW_OP_bregx, 0x0d, 0x28});
}
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_stack_value) {
testExpr({DW_OP_breg13, 0x04, DW_OP_stack_value});
}
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_entry_value) {
testExpr({DW_OP_entry_value, 0x01, DW_OP_reg0, DW_OP_stack_value});
}
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_entry_value_mem) {
testExpr({DW_OP_entry_value, 0x02, DW_OP_breg13, 0x10, DW_OP_stack_value});
}