245 lines · cpp
1//===- llvm/unittest/DebugInfo/DWARFExpressionRawDataTest.cpp -------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/ADT/ArrayRef.h"10#include "llvm/BinaryFormat/Dwarf.h"11#include "llvm/DebugInfo/DWARF/DWARFContext.h"12#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h"13#include "llvm/DebugInfo/DWARF/DWARFDie.h"14#include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h"15#include "llvm/MC/MCAsmBackend.h"16#include "llvm/MC/MCAsmInfo.h"17#include "llvm/MC/MCCodeEmitter.h"18#include "llvm/MC/MCContext.h"19#include "llvm/MC/MCInstrInfo.h"20#include "llvm/MC/MCObjectWriter.h"21#include "llvm/MC/MCRegisterInfo.h"22#include "llvm/MC/MCStreamer.h"23#include "llvm/MC/MCSubtargetInfo.h"24#include "llvm/MC/MCTargetOptions.h"25#include "llvm/MC/TargetRegistry.h"26#include "llvm/Object/Binary.h"27#include "llvm/Object/ELFObjectFile.h"28#include "llvm/Support/DataExtractor.h"29#include "llvm/Support/LEB128.h"30#include "llvm/Support/MemoryBuffer.h"31#include "llvm/Support/TargetSelect.h"32#include "llvm/TargetParser/Triple.h"33#include "llvm/Testing/Support/Error.h"34#include "gtest/gtest.h"35 36using namespace llvm;37using namespace dwarf;38 39namespace {40 41/// Tests that a client of DebugInfo/DWARF is able to read raw data bytes of a42/// DWARFExpression parsed from CFI with the intent of writing them back as is43/// via MC layer / cfi_escape.44/// This is relevant for binary tools that need to rewrite/copy unwind and45/// debug info from input to output binary.46class DWARFExpressionCopyBytesTest : public ::testing::Test {47public:48 static constexpr char TripleName[] = "x86_64-pc-linux";49 Triple TheTriple;50 51 std::unique_ptr<MCRegisterInfo> MRI;52 std::unique_ptr<MCAsmInfo> MAI;53 std::unique_ptr<const MCSubtargetInfo> STI;54 const Target *TheTarget;55 56 DWARFExpressionCopyBytesTest() : TheTriple(TripleName) {57 InitializeAllTargets();58 InitializeAllTargetMCs();59 InitializeAllAsmPrinters();60 61 std::string ErrorStr;62 TheTarget = TargetRegistry::lookupTarget(TheTriple, ErrorStr);63 if (!TheTarget)64 return;65 66 MRI.reset(TheTarget->createMCRegInfo(TheTriple));67 MAI.reset(TheTarget->createMCAsmInfo(*MRI, TheTriple, MCTargetOptions()));68 STI.reset(TheTarget->createMCSubtargetInfo(TheTriple, "", ""));69 }70 71 struct StreamerContext {72 std::unique_ptr<MCObjectFileInfo> MOFI;73 std::unique_ptr<MCContext> Ctx;74 std::unique_ptr<const MCInstrInfo> MII;75 std::unique_ptr<MCStreamer> Streamer;76 };77 78 /// Create all data structures necessary to operate an assembler79 StreamerContext createStreamer(raw_pwrite_stream &OS);80 /// Emit a dummy obj file with a single CFI instruction,81 /// DW_CFA_def_cfa_expression, encoding as its operand the DWARF expression82 /// represented by ExprBytes83 SmallString<0> emitObjFile(StringRef ExprBytes);84 /// Peruse the object file looking for the encoded DWARF expression, and check85 /// that its operand was encoded correctly86 void parseCFIsAndCheckExpression(const llvm::object::ObjectFile &E,87 ArrayRef<uint8_t> Expected);88 /// Open the in-memory relocatable object file and verify that it contains89 /// the expected DWARF expression bytes90 void readAndCheckObjFile(StringRef ObjFileData, ArrayRef<uint8_t> Expected);91 /// Run this test on the DWARF expression represented by the bytes in92 /// ExprData. Check that the getData() API retrieves these original bytes and93 /// that we can use them to encode a CFI with those bytes as operands (via94 /// cfi_escape).95 void testExpr(ArrayRef<uint8_t> ExprData);96};97 98} // namespace99 100DWARFExpressionCopyBytesTest::StreamerContext101DWARFExpressionCopyBytesTest::createStreamer(raw_pwrite_stream &OS) {102 StreamerContext Res;103 Res.Ctx =104 std::make_unique<MCContext>(Triple(TripleName), MAI.get(), MRI.get(),105 /*MSTI=*/nullptr);106 Res.MOFI.reset(TheTarget->createMCObjectFileInfo(*Res.Ctx,107 /*PIC=*/false));108 Res.Ctx->setObjectFileInfo(Res.MOFI.get());109 110 Res.MII.reset(TheTarget->createMCInstrInfo());111 MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*Res.MII, *Res.Ctx);112 MCAsmBackend *MAB =113 TheTarget->createMCAsmBackend(*STI, *MRI, MCTargetOptions());114 std::unique_ptr<MCObjectWriter> OW = MAB->createObjectWriter(OS);115 Res.Streamer.reset(TheTarget->createMCObjectStreamer(116 Triple(TripleName), *Res.Ctx, std::unique_ptr<MCAsmBackend>(MAB),117 std::move(OW), std::unique_ptr<MCCodeEmitter>(MCE), *STI));118 return Res;119}120 121SmallString<0> DWARFExpressionCopyBytesTest::emitObjFile(StringRef ExprBytes) {122 auto EncodeDefCfaExpr = [&](StringRef Bytes) {123 std::string Str;124 raw_string_ostream OS(Str);125 OS << static_cast<uint8_t>(dwarf::DW_CFA_def_cfa_expression);126 encodeULEB128(Bytes.size(), OS);127 OS << Bytes;128 return Str;129 };130 131 SmallString<0> Storage;132 raw_svector_ostream VecOS(Storage);133 StreamerContext C = createStreamer(VecOS);134 C.Streamer->initSections(false, *STI);135 MCSection *Section = C.MOFI->getTextSection();136 Section->setHasInstructions(true);137 C.Streamer->switchSection(Section);138 C.Streamer->emitCFIStartProc(true);139 auto Str = EncodeDefCfaExpr(ExprBytes);140 C.Streamer->emitCFIEscape(Str);141 C.Streamer->emitNops(4, 1, SMLoc(), *STI);142 C.Streamer->emitCFIEndProc();143 C.Streamer->finish();144 return Storage;145}146 147void DWARFExpressionCopyBytesTest::parseCFIsAndCheckExpression(148 const llvm::object::ObjectFile &E, ArrayRef<uint8_t> Expected) {149 auto FetchFirstCfaExpression = [](const DWARFDebugFrame &EHFrame)150 -> std::optional<CFIProgram::Instruction> {151 for (const dwarf::FrameEntry &Entry : EHFrame.entries()) {152 const auto *CurFDE = dyn_cast<dwarf::FDE>(&Entry);153 if (!CurFDE)154 continue;155 for (const CFIProgram::Instruction &Instr : CurFDE->cfis()) {156 if (Instr.Opcode != dwarf::DW_CFA_def_cfa_expression)157 continue;158 return Instr;159 }160 }161 return std::nullopt;162 };163 164 std::unique_ptr<DWARFContext> Ctx = DWARFContext::create(E);165 const DWARFDebugFrame *EHFrame = cantFail(Ctx->getEHFrame());166 ASSERT_NE(nullptr, EHFrame);167 auto CfiInstr = FetchFirstCfaExpression(*EHFrame);168 ASSERT_TRUE(CfiInstr);169 DWARFExpression Expr = *(CfiInstr->Expression);170 StringRef ExprData = Expr.getData();171 EXPECT_EQ(ExprData.size(), Expected.size());172 for (unsigned I = 0, E = ExprData.size(); I != E; ++I) {173 EXPECT_EQ(static_cast<uint8_t>(ExprData[I]), Expected[I]);174 }175}176 177void DWARFExpressionCopyBytesTest::readAndCheckObjFile(178 StringRef ObjFileData, ArrayRef<uint8_t> Expected) {179 std::unique_ptr<MemoryBuffer> MB =180 MemoryBuffer::getMemBuffer(ObjFileData, "", false);181 std::unique_ptr<object::Binary> Bin =182 cantFail(llvm::object::createBinary(MB->getMemBufferRef()));183 if (auto *E = dyn_cast<llvm::object::ELFObjectFileBase>(&*Bin)) {184 parseCFIsAndCheckExpression(*E, Expected);185 }186}187 188void DWARFExpressionCopyBytesTest::testExpr(ArrayRef<uint8_t> ExprData) {189 // If we didn't build x86, do not run the test.190 if (!MRI)191 GTEST_SKIP();192 193 DataExtractor DE(ExprData, true, 8);194 DWARFExpression Expr(DE, 8);195 196 // Copy this expression into the CFI of a binary and check that we are able to197 // get it back correctly from this binary.198 const SmallString<0> EmittedBinContents = emitObjFile(Expr.getData());199 readAndCheckObjFile(EmittedBinContents.str(), ExprData);200}201 202TEST_F(DWARFExpressionCopyBytesTest, Test_OP_reg0) { testExpr({DW_OP_reg0}); }203 204TEST_F(DWARFExpressionCopyBytesTest, Test_OP_reg10) { testExpr({DW_OP_reg10}); }205 206TEST_F(DWARFExpressionCopyBytesTest, Test_OP_regx) {207 testExpr({DW_OP_regx, 0x80, 0x02});208}209 210TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg0) {211 testExpr({DW_OP_breg0, 0x04});212}213 214TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg0_large_offset) {215 testExpr({DW_OP_breg0, 0x80, 0x02});216}217 218TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg13) {219 testExpr({DW_OP_breg13, 0x10});220}221 222TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg13_zero_offset) {223 testExpr({DW_OP_breg13, 0x00});224}225 226TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg0_negative) {227 testExpr({DW_OP_breg0, 0x70});228}229 230TEST_F(DWARFExpressionCopyBytesTest, Test_OP_bregx) {231 testExpr({DW_OP_bregx, 0x0d, 0x28});232}233 234TEST_F(DWARFExpressionCopyBytesTest, Test_OP_stack_value) {235 testExpr({DW_OP_breg13, 0x04, DW_OP_stack_value});236}237 238TEST_F(DWARFExpressionCopyBytesTest, Test_OP_entry_value) {239 testExpr({DW_OP_entry_value, 0x01, DW_OP_reg0, DW_OP_stack_value});240}241 242TEST_F(DWARFExpressionCopyBytesTest, Test_OP_entry_value_mem) {243 testExpr({DW_OP_entry_value, 0x02, DW_OP_breg13, 0x10, DW_OP_stack_value});244}245