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