1//===- llvm/unittest/MC/DwarfLineTableHeaders.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/STLExtras.h"
10#include "llvm/BinaryFormat/Dwarf.h"
11#include "llvm/MC/MCAsmBackend.h"
12#include "llvm/MC/MCAsmInfo.h"
13#include "llvm/MC/MCAssembler.h"
14#include "llvm/MC/MCCodeEmitter.h"
15#include "llvm/MC/MCContext.h"
16#include "llvm/MC/MCDwarf.h"
17#include "llvm/MC/MCInstrInfo.h"
18#include "llvm/MC/MCObjectStreamer.h"
19#include "llvm/MC/MCObjectWriter.h"
20#include "llvm/MC/MCRegisterInfo.h"
21#include "llvm/MC/MCStreamer.h"
22#include "llvm/MC/MCSubtargetInfo.h"
23#include "llvm/MC/MCTargetOptions.h"
24#include "llvm/MC/TargetRegistry.h"
25#include "llvm/Object/Binary.h"
26#include "llvm/Object/ELFObjectFile.h"
27#include "llvm/Support/FileSystem.h"
28#include "llvm/Support/MemoryBuffer.h"
29#include "llvm/Support/TargetSelect.h"
30#include "llvm/Support/ToolOutputFile.h"
31#include "gtest/gtest.h"
32
33using namespace llvm;
34
35namespace {
36
37class DwarfLineTableHeaders : public ::testing::Test {
38public:
39 const char *TripleName = "x86_64-pc-linux";
40 std::unique_ptr<MCRegisterInfo> MRI;
41 std::unique_ptr<MCAsmInfo> MAI;
42 std::unique_ptr<const MCSubtargetInfo> STI;
43 const Target *TheTarget;
44
45 struct StreamerContext {
46 std::unique_ptr<MCObjectFileInfo> MOFI;
47 std::unique_ptr<MCContext> Ctx;
48 std::unique_ptr<const MCInstrInfo> MII;
49 std::unique_ptr<MCStreamer> Streamer;
50 };
51
52 DwarfLineTableHeaders() {
53 llvm::InitializeAllTargetInfos();
54 llvm::InitializeAllTargetMCs();
55 llvm::InitializeAllDisassemblers();
56
57 // If we didn't build x86, do not run the test.
58 std::string Error;
59 TheTarget = TargetRegistry::lookupTarget(Triple: TripleName, Error);
60 if (!TheTarget)
61 return;
62
63 MRI.reset(p: TheTarget->createMCRegInfo(TT: TripleName));
64 MCTargetOptions MCOptions;
65 MAI.reset(p: TheTarget->createMCAsmInfo(MRI: *MRI, TheTriple: TripleName, Options: MCOptions));
66 STI.reset(p: TheTarget->createMCSubtargetInfo(TheTriple: TripleName, CPU: "", Features: ""));
67 }
68
69 /// Create all data structures necessary to operate an assembler
70 StreamerContext createStreamer(raw_pwrite_stream &OS) {
71 StreamerContext Res;
72 Res.Ctx =
73 std::make_unique<MCContext>(args: Triple(TripleName), args: MAI.get(), args: MRI.get(),
74 /*MSTI=*/args: nullptr);
75 Res.MOFI.reset(p: TheTarget->createMCObjectFileInfo(Ctx&: *Res.Ctx.get(),
76 /*PIC=*/false));
77 Res.Ctx->setObjectFileInfo(Res.MOFI.get());
78
79 Res.MII.reset(p: TheTarget->createMCInstrInfo());
80 MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(II: *Res.MII, Ctx&: *Res.Ctx);
81 MCAsmBackend *MAB =
82 TheTarget->createMCAsmBackend(STI: *STI, MRI: *MRI, Options: MCTargetOptions());
83 std::unique_ptr<MCObjectWriter> OW = MAB->createObjectWriter(OS);
84 Res.Streamer.reset(p: TheTarget->createMCObjectStreamer(
85 T: Triple(TripleName), Ctx&: *Res.Ctx, TAB: std::unique_ptr<MCAsmBackend>(MAB),
86 OW: std::move(OW), Emitter: std::unique_ptr<MCCodeEmitter>(MCE), STI: *STI,
87 /* RelaxAll */ false,
88 /* IncrementalLinkerCompatible */ false,
89 /* DWARFMustBeAtTheEnd */ false));
90 return Res;
91 }
92
93 /// Emit a .debug_line section with the given context parameters
94 void emitDebugLineSection(StreamerContext &C) {
95 MCContext &Ctx = *C.Ctx;
96 MCStreamer *TheStreamer = C.Streamer.get();
97 MCAssembler &Assembler =
98 static_cast<MCObjectStreamer *>(TheStreamer)->getAssembler();
99 TheStreamer->initSections(NoExecStack: false, STI: *STI);
100
101 // Create a mock function
102 MCSection *Section = C.MOFI->getTextSection();
103 Section->setHasInstructions(true);
104 TheStreamer->switchSection(Section);
105 TheStreamer->emitCFIStartProc(IsSimple: true);
106
107 // Create a mock dwarfloc
108 Ctx.setCurrentDwarfLoc(/*FileNo=*/FileNum: 0, /*Line=*/1, /*Column=*/1, /*Flags=*/0,
109 /*Isa=*/0, /*Discriminator=*/0);
110 MCDwarfLoc Loc = Ctx.getCurrentDwarfLoc();
111 MCSymbol *LineSym = Ctx.createTempSymbol();
112 // Set the value of the symbol to use for the MCDwarfLineEntry.
113 TheStreamer->emitLabel(Symbol: LineSym);
114 TheStreamer->emitNops(NumBytes: 4, ControlledNopLength: 1, Loc: SMLoc(), STI: *STI);
115 TheStreamer->emitCFIEndProc();
116
117 // Start emission of .debug_line
118 TheStreamer->switchSection(Section: C.MOFI->getDwarfLineSection());
119 MCDwarfLineTableHeader Header;
120 MCDwarfLineTableParams Params = Assembler.getDWARFLinetableParams();
121 std::optional<MCDwarfLineStr> LineStr(std::nullopt);
122 if (Ctx.getDwarfVersion() >= 5) {
123 LineStr.emplace(args&: Ctx);
124 Header.setRootFile(Directory: "dir", FileName: "file", Checksum: std::nullopt, Source: std::nullopt);
125 }
126 MCSymbol *LineEndSym = Header.Emit(MCOS: TheStreamer, Params, LineStr).second;
127
128 // Put out the line tables.
129 MCLineSection::MCDwarfLineEntryCollection LineEntries;
130 MCDwarfLineEntry LineEntry(LineSym, Loc);
131 LineEntries.push_back(x: LineEntry);
132 MCDwarfLineTable::emitOne(MCOS: TheStreamer, Section, LineEntries);
133 TheStreamer->emitLabel(Symbol: LineEndSym);
134 if (LineStr) {
135 SmallString<0> Data = LineStr->getFinalizedData();
136 TheStreamer->switchSection(Section: TheStreamer->getContext()
137 .getObjectFileInfo()
138 ->getDwarfLineStrSection());
139 TheStreamer->emitBinaryData(Data: Data.str());
140 }
141 }
142
143 /// Check contents of .debug_line section
144 void verifyDebugLineContents(const llvm::object::ObjectFile &E,
145 ArrayRef<uint8_t> ExpectedEncoding) {
146 for (const llvm::object::SectionRef &Section : E.sections()) {
147 Expected<StringRef> SectionNameOrErr = Section.getName();
148 ASSERT_TRUE(static_cast<bool>(SectionNameOrErr));
149 StringRef SectionName = *SectionNameOrErr;
150 if (SectionName.empty() || SectionName != ".debug_line")
151 continue;
152 Expected<StringRef> ContentsOrErr = Section.getContents();
153 ASSERT_TRUE(static_cast<bool>(ContentsOrErr));
154 StringRef Contents = *ContentsOrErr;
155 ASSERT_TRUE(Contents.size() > ExpectedEncoding.size());
156 EXPECT_EQ(
157 arrayRefFromStringRef(Contents.slice(0, ExpectedEncoding.size())),
158 ExpectedEncoding);
159 return;
160 }
161 llvm_unreachable(".debug_line not found");
162 }
163
164 /// Check contents of .debug_line_str section
165 void verifyDebugLineStrContents(const llvm::object::ObjectFile &E) {
166 for (const llvm::object::SectionRef &Section : E.sections()) {
167 Expected<StringRef> SectionNameOrErr = Section.getName();
168 ASSERT_TRUE(static_cast<bool>(SectionNameOrErr));
169 StringRef SectionName = *SectionNameOrErr;
170 if (SectionName.empty() || SectionName != ".debug_line_str")
171 continue;
172 Expected<StringRef> ContentsOrErr = Section.getContents();
173 ASSERT_TRUE(static_cast<bool>(ContentsOrErr));
174 StringRef Contents = *ContentsOrErr;
175 ASSERT_TRUE(Contents.contains("dir"));
176 ASSERT_TRUE(Contents.contains("file"));
177 ASSERT_TRUE(Contents.size() == 9);
178 return;
179 }
180 llvm_unreachable(".debug_line_str not found");
181 }
182
183 /// Open ObjFileData as an object file and read its .debug_line section
184 void readAndCheckDebugContents(StringRef ObjFileData,
185 ArrayRef<uint8_t> Expected, uint8_t DwarfVersion) {
186 std::unique_ptr<MemoryBuffer> MB =
187 MemoryBuffer::getMemBuffer(InputData: ObjFileData, BufferName: "", RequiresNullTerminator: false);
188 std::unique_ptr<object::Binary> Bin =
189 cantFail(ValOrErr: llvm::object::createBinary(Source: MB->getMemBufferRef()));
190 if (auto *E = dyn_cast<llvm::object::ELFObjectFileBase>(Val: &*Bin)) {
191 verifyDebugLineContents(E: *E, ExpectedEncoding: Expected);
192 if (DwarfVersion >= 5)
193 verifyDebugLineStrContents(E: *E);
194 return;
195 }
196 llvm_unreachable("ELF object file not found");
197 }
198};
199} // namespace
200
201TEST_F(DwarfLineTableHeaders, TestDWARF4HeaderEmission) {
202 if (!MRI)
203 GTEST_SKIP();
204
205 SmallString<0> EmittedBinContents;
206 raw_svector_ostream VecOS(EmittedBinContents);
207 StreamerContext C = createStreamer(OS&: VecOS);
208 constexpr uint8_t DwarfVersion = 4;
209 C.Ctx->setDwarfVersion(DwarfVersion);
210 emitDebugLineSection(C);
211 C.Streamer->finish();
212 readAndCheckDebugContents(
213 ObjFileData: EmittedBinContents.str(),
214 Expected: {/* Total length=*/0x30, 0, 0, 0,
215 /* DWARF version=*/DwarfVersion, 0,
216 /* Prologue length=*/0x14, 0, 0, 0,
217 /* min_inst_length=*/1,
218 /*max_ops_per_inst=*/1,
219 /* default_is_stmt=*/DWARF2_LINE_DEFAULT_IS_STMT,
220 /* line_base=*/static_cast<uint8_t>(-5),
221 /* line_range=*/14,
222 /* opcode_base=*/13}, DwarfVersion);
223}
224
225TEST_F(DwarfLineTableHeaders, TestDWARF5HeaderEmission) {
226 if (!MRI)
227 GTEST_SKIP();
228
229 SmallString<0> EmittedBinContents;
230 raw_svector_ostream VecOS(EmittedBinContents);
231 StreamerContext C = createStreamer(OS&: VecOS);
232 constexpr uint8_t DwarfVersion = 5;
233 C.Ctx->setDwarfVersion(DwarfVersion);
234 emitDebugLineSection(C);
235 C.Streamer->finish();
236 readAndCheckDebugContents(
237 ObjFileData: EmittedBinContents.str(),
238 Expected: {/* Total length=*/0x43, 0, 0, 0,
239 /* DWARF version=*/DwarfVersion, 0,
240 /* ptr size=*/8,
241 /* segment=*/0,
242 /* Prologue length=*/0x25, 0, 0, 0,
243 /* min_inst_length=*/1,
244 /*max_ops_per_inst=*/1,
245 /* default_is_stmt=*/DWARF2_LINE_DEFAULT_IS_STMT,
246 /* line_base=*/static_cast<uint8_t>(-5),
247 /* line_range=*/14,
248 /* opcode_base=*/13}, DwarfVersion);
249}
250

source code of llvm/unittests/MC/DwarfLineTableHeaders.cpp