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 
33 using namespace llvm;
34 
35 namespace {
36 
37 class DwarfLineTableHeaders : public ::testing::Test {
38 public:
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 
DwarfLineTableHeaders()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(TripleName, Error);
60     if (!TheTarget)
61       return;
62 
63     MRI.reset(TheTarget->createMCRegInfo(TripleName));
64     MCTargetOptions MCOptions;
65     MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
66     STI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", ""));
67   }
68 
69   /// Create all data structures necessary to operate an assembler
createStreamer(raw_pwrite_stream & OS)70   StreamerContext createStreamer(raw_pwrite_stream &OS) {
71     StreamerContext Res;
72     Res.Ctx =
73         std::make_unique<MCContext>(Triple(TripleName), MAI.get(), MRI.get(),
74                                     /*MSTI=*/nullptr);
75     Res.MOFI.reset(TheTarget->createMCObjectFileInfo(*Res.Ctx.get(),
76                                                      /*PIC=*/false));
77     Res.Ctx->setObjectFileInfo(Res.MOFI.get());
78 
79     Res.MII.reset(TheTarget->createMCInstrInfo());
80     MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*Res.MII, *Res.Ctx);
81     MCAsmBackend *MAB =
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,
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
emitDebugLineSection(StreamerContext & C)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);
100 
101     // Create a mock function
102     MCSection *Section = C.MOFI->getTextSection();
103     Section->setHasInstructions(true);
104     TheStreamer->switchSection(Section);
105     TheStreamer->emitCFIStartProc(true);
106 
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();
116 
117     // Start emission of .debug_line
118     TheStreamer->switchSection(C.MOFI->getDwarfLineSection());
119     MCDwarfLineTableHeader Header;
120     MCDwarfLineTableParams Params = Assembler.getDWARFLinetableParams();
121     Optional<MCDwarfLineStr> LineStr(None);
122     if (Ctx.getDwarfVersion() >= 5) {
123       LineStr.emplace(Ctx);
124       Header.setRootFile("dir", "file", None, None);
125     }
126     MCSymbol *LineEndSym = Header.Emit(TheStreamer, Params, LineStr).second;
127 
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);
134     if (LineStr) {
135       SmallString<0> Data = LineStr->getFinalizedData();
136       TheStreamer->switchSection(TheStreamer->getContext()
137                                      .getObjectFileInfo()
138                                      ->getDwarfLineStrSection());
139       TheStreamer->emitBinaryData(Data.str());
140     }
141   }
142 
143   /// Check contents of .debug_line section
verifyDebugLineContents(const llvm::object::ObjectFile & E,ArrayRef<uint8_t> ExpectedEncoding)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
verifyDebugLineStrContents(const llvm::object::ObjectFile & E)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.find("dir") != StringRef::npos);
176       ASSERT_TRUE(Contents.find("file") != StringRef::npos);
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
readAndCheckDebugContents(StringRef ObjFileData,ArrayRef<uint8_t> Expected,uint8_t DwarfVersion)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);
194       return;
195     }
196     llvm_unreachable("ELF object file not found");
197   }
198 };
199 } // namespace
200 
TEST_F(DwarfLineTableHeaders,TestDWARF4HeaderEmission)201 TEST_F(DwarfLineTableHeaders, TestDWARF4HeaderEmission) {
202   if (!MRI)
203     return;
204 
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),
221        /*      line_range=*/14,
222        /*     opcode_base=*/13}, DwarfVersion);
223 }
224 
TEST_F(DwarfLineTableHeaders,TestDWARF5HeaderEmission)225 TEST_F(DwarfLineTableHeaders, TestDWARF5HeaderEmission) {
226   if (!MRI)
227     return;
228 
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,
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