Skip to content

Commit d8bbfe8

Browse files
committed
[DWARF] Expose raw bytes in DWARFExpression
This information is necessary for clients of DebugInfo that do not want to process a DWARF expression, but just treat it as a blob of data. In BOLT, for example, we need to read these expressions in CFIs and write them back to the binary, unchanged, so having access to the original expression encoding is a shortcut to avoid the need to re-encode the entire expression when re-writing exception handling info (CFIs). This patch is an alternative to https://reviews.llvm.org/D98301, in which we implement the support to re-encode these expressions. But since we don't really need to change anything in these expressions, we can just copy their bytes. Reviewed By: aprantl Differential Revision: https://reviews.llvm.org/D107515
1 parent 41e5dbe commit d8bbfe8

File tree

3 files changed

+244
-0
lines changed

3 files changed

+244
-0
lines changed

llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ class DWARFExpression {
159159

160160
bool operator==(const DWARFExpression &RHS) const;
161161

162+
StringRef getData() const { return Data.getData(); }
163+
162164
private:
163165
DataExtractor Data;
164166
uint8_t AddressSize;

llvm/unittests/DebugInfo/DWARF/CMakeLists.txt

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

0 commit comments

Comments
 (0)