1 //===- llvm/unittest/MC/DwarfLineTableHeaders.cpp -------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
37 class DwarfLineTableHeaders
: public ::testing::Test
{
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
;
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
;
52 DwarfLineTableHeaders() {
53 llvm::InitializeAllTargetInfos();
54 llvm::InitializeAllTargetMCs();
55 llvm::InitializeAllDisassemblers();
57 // If we didn't build x86, do not run the test.
59 TheTarget
= TargetRegistry::lookupTarget(TripleName
, Error
);
63 MRI
.reset(TheTarget
->createMCRegInfo(TripleName
));
64 MCTargetOptions MCOptions
;
65 MAI
.reset(TheTarget
->createMCAsmInfo(*MRI
, TripleName
, MCOptions
));
66 STI
.reset(TheTarget
->createMCSubtargetInfo(TripleName
, "", ""));
69 /// Create all data structures necessary to operate an assembler
70 StreamerContext
createStreamer(raw_pwrite_stream
&OS
) {
73 std::make_unique
<MCContext
>(Triple(TripleName
), MAI
.get(), MRI
.get(),
75 Res
.MOFI
.reset(TheTarget
->createMCObjectFileInfo(*Res
.Ctx
.get(),
77 Res
.Ctx
->setObjectFileInfo(Res
.MOFI
.get());
79 Res
.MII
.reset(TheTarget
->createMCInstrInfo());
80 MCCodeEmitter
*MCE
= TheTarget
->createMCCodeEmitter(*Res
.MII
, *Res
.Ctx
);
82 TheTarget
->createMCAsmBackend(*STI
, *MRI
, MCTargetOptions());
83 std::unique_ptr
<MCObjectWriter
> OW
= MAB
->createObjectWriter(OS
);
84 Res
.Streamer
.reset(TheTarget
->createMCObjectStreamer(
85 Triple(TripleName
), *Res
.Ctx
, std::unique_ptr
<MCAsmBackend
>(MAB
),
86 std::move(OW
), std::unique_ptr
<MCCodeEmitter
>(MCE
), *STI
,
88 /* IncrementalLinkerCompatible */ false,
89 /* DWARFMustBeAtTheEnd */ false));
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(false, *STI
);
101 // Create a mock function
102 MCSection
*Section
= C
.MOFI
->getTextSection();
103 Section
->setHasInstructions(true);
104 TheStreamer
->switchSection(Section
);
105 TheStreamer
->emitCFIStartProc(true);
107 // Create a mock dwarfloc
108 Ctx
.setCurrentDwarfLoc(/*FileNo=*/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(LineSym
);
114 TheStreamer
->emitNops(4, 1, SMLoc(), *STI
);
115 TheStreamer
->emitCFIEndProc();
117 // Start emission of .debug_line
118 TheStreamer
->switchSection(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(Ctx
);
124 Header
.setRootFile("dir", "file", std::nullopt
, std::nullopt
);
126 MCSymbol
*LineEndSym
= Header
.Emit(TheStreamer
, Params
, LineStr
).second
;
128 // Put out the line tables.
129 MCLineSection::MCDwarfLineEntryCollection LineEntries
;
130 MCDwarfLineEntry
LineEntry(LineSym
, Loc
);
131 LineEntries
.push_back(LineEntry
);
132 MCDwarfLineTable::emitOne(TheStreamer
, Section
, LineEntries
);
133 TheStreamer
->emitLabel(LineEndSym
);
135 SmallString
<0> Data
= LineStr
->getFinalizedData();
136 TheStreamer
->switchSection(TheStreamer
->getContext()
138 ->getDwarfLineStrSection());
139 TheStreamer
->emitBinaryData(Data
.str());
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")
152 Expected
<StringRef
> ContentsOrErr
= Section
.getContents();
153 ASSERT_TRUE(static_cast<bool>(ContentsOrErr
));
154 StringRef Contents
= *ContentsOrErr
;
155 ASSERT_TRUE(Contents
.size() > ExpectedEncoding
.size());
157 arrayRefFromStringRef(Contents
.slice(0, ExpectedEncoding
.size())),
161 llvm_unreachable(".debug_line not found");
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")
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);
180 llvm_unreachable(".debug_line_str not found");
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(ObjFileData
, "", false);
188 std::unique_ptr
<object::Binary
> Bin
=
189 cantFail(llvm::object::createBinary(MB
->getMemBufferRef()));
190 if (auto *E
= dyn_cast
<llvm::object::ELFObjectFileBase
>(&*Bin
)) {
191 verifyDebugLineContents(*E
, Expected
);
192 if (DwarfVersion
>= 5)
193 verifyDebugLineStrContents(*E
);
196 llvm_unreachable("ELF object file not found");
201 TEST_F(DwarfLineTableHeaders
, TestDWARF4HeaderEmission
) {
205 SmallString
<0> EmittedBinContents
;
206 raw_svector_ostream
VecOS(EmittedBinContents
);
207 StreamerContext C
= createStreamer(VecOS
);
208 constexpr uint8_t DwarfVersion
= 4;
209 C
.Ctx
->setDwarfVersion(DwarfVersion
);
210 emitDebugLineSection(C
);
211 C
.Streamer
->finish();
212 readAndCheckDebugContents(
213 EmittedBinContents
.str(),
214 {/* 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),
222 /* opcode_base=*/13}, DwarfVersion
);
225 TEST_F(DwarfLineTableHeaders
, TestDWARF5HeaderEmission
) {
229 SmallString
<0> EmittedBinContents
;
230 raw_svector_ostream
VecOS(EmittedBinContents
);
231 StreamerContext C
= createStreamer(VecOS
);
232 constexpr uint8_t DwarfVersion
= 5;
233 C
.Ctx
->setDwarfVersion(DwarfVersion
);
234 emitDebugLineSection(C
);
235 C
.Streamer
->finish();
236 readAndCheckDebugContents(
237 EmittedBinContents
.str(),
238 {/* Total length=*/0x43, 0, 0, 0,
239 /* DWARF version=*/DwarfVersion
, 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),
248 /* opcode_base=*/13}, DwarfVersion
);