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 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 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 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 = MCDwarfLineStr(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 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.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 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 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 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