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