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, 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 return Res; 88 } 89 90 /// Emit a .debug_line section with the given context parameters 91 void emitDebugLineSection(StreamerContext &C) { 92 MCContext &Ctx = *C.Ctx; 93 MCStreamer *TheStreamer = C.Streamer.get(); 94 MCAssembler &Assembler = 95 static_cast<MCObjectStreamer *>(TheStreamer)->getAssembler(); 96 TheStreamer->initSections(false, *STI); 97 98 // Create a mock function 99 MCSection *Section = C.MOFI->getTextSection(); 100 Section->setHasInstructions(true); 101 TheStreamer->switchSection(Section); 102 TheStreamer->emitCFIStartProc(true); 103 104 // Create a mock dwarfloc 105 Ctx.setCurrentDwarfLoc(/*FileNo=*/0, /*Line=*/1, /*Column=*/1, /*Flags=*/0, 106 /*Isa=*/0, /*Discriminator=*/0); 107 MCDwarfLoc Loc = Ctx.getCurrentDwarfLoc(); 108 MCSymbol *LineSym = Ctx.createTempSymbol(); 109 // Set the value of the symbol to use for the MCDwarfLineEntry. 110 TheStreamer->emitLabel(LineSym); 111 TheStreamer->emitNops(4, 1, SMLoc(), *STI); 112 TheStreamer->emitCFIEndProc(); 113 114 // Start emission of .debug_line 115 TheStreamer->switchSection(C.MOFI->getDwarfLineSection()); 116 MCDwarfLineTableHeader Header; 117 MCDwarfLineTableParams Params = Assembler.getDWARFLinetableParams(); 118 std::optional<MCDwarfLineStr> LineStr(std::nullopt); 119 if (Ctx.getDwarfVersion() >= 5) { 120 LineStr.emplace(Ctx); 121 Header.setRootFile("dir", "file", std::nullopt, std::nullopt); 122 } 123 MCSymbol *LineEndSym = Header.Emit(TheStreamer, Params, LineStr).second; 124 125 // Put out the line tables. 126 MCLineSection::MCDwarfLineEntryCollection LineEntries; 127 MCDwarfLineEntry LineEntry(LineSym, Loc); 128 LineEntries.push_back(LineEntry); 129 MCDwarfLineTable::emitOne(TheStreamer, Section, LineEntries); 130 TheStreamer->emitLabel(LineEndSym); 131 if (LineStr) { 132 SmallString<0> Data = LineStr->getFinalizedData(); 133 TheStreamer->switchSection(TheStreamer->getContext() 134 .getObjectFileInfo() 135 ->getDwarfLineStrSection()); 136 TheStreamer->emitBinaryData(Data.str()); 137 } 138 } 139 140 /// Check contents of .debug_line section 141 void verifyDebugLineContents(const llvm::object::ObjectFile &E, 142 ArrayRef<uint8_t> ExpectedEncoding) { 143 for (const llvm::object::SectionRef &Section : E.sections()) { 144 Expected<StringRef> SectionNameOrErr = Section.getName(); 145 ASSERT_TRUE(static_cast<bool>(SectionNameOrErr)); 146 StringRef SectionName = *SectionNameOrErr; 147 if (SectionName.empty() || SectionName != ".debug_line") 148 continue; 149 Expected<StringRef> ContentsOrErr = Section.getContents(); 150 ASSERT_TRUE(static_cast<bool>(ContentsOrErr)); 151 StringRef Contents = *ContentsOrErr; 152 ASSERT_TRUE(Contents.size() > ExpectedEncoding.size()); 153 EXPECT_EQ( 154 arrayRefFromStringRef(Contents.slice(0, ExpectedEncoding.size())), 155 ExpectedEncoding); 156 return; 157 } 158 llvm_unreachable(".debug_line not found"); 159 } 160 161 /// Check contents of .debug_line_str section 162 void verifyDebugLineStrContents(const llvm::object::ObjectFile &E) { 163 for (const llvm::object::SectionRef &Section : E.sections()) { 164 Expected<StringRef> SectionNameOrErr = Section.getName(); 165 ASSERT_TRUE(static_cast<bool>(SectionNameOrErr)); 166 StringRef SectionName = *SectionNameOrErr; 167 if (SectionName.empty() || SectionName != ".debug_line_str") 168 continue; 169 Expected<StringRef> ContentsOrErr = Section.getContents(); 170 ASSERT_TRUE(static_cast<bool>(ContentsOrErr)); 171 StringRef Contents = *ContentsOrErr; 172 ASSERT_TRUE(Contents.contains("dir")); 173 ASSERT_TRUE(Contents.contains("file")); 174 ASSERT_TRUE(Contents.size() == 9); 175 return; 176 } 177 llvm_unreachable(".debug_line_str not found"); 178 } 179 180 /// Open ObjFileData as an object file and read its .debug_line section 181 void readAndCheckDebugContents(StringRef ObjFileData, 182 ArrayRef<uint8_t> Expected, uint8_t DwarfVersion) { 183 std::unique_ptr<MemoryBuffer> MB = 184 MemoryBuffer::getMemBuffer(ObjFileData, "", false); 185 std::unique_ptr<object::Binary> Bin = 186 cantFail(llvm::object::createBinary(MB->getMemBufferRef())); 187 if (auto *E = dyn_cast<llvm::object::ELFObjectFileBase>(&*Bin)) { 188 verifyDebugLineContents(*E, Expected); 189 if (DwarfVersion >= 5) 190 verifyDebugLineStrContents(*E); 191 return; 192 } 193 llvm_unreachable("ELF object file not found"); 194 } 195 }; 196 } // namespace 197 198 TEST_F(DwarfLineTableHeaders, TestDWARF4HeaderEmission) { 199 if (!MRI) 200 GTEST_SKIP(); 201 202 SmallString<0> EmittedBinContents; 203 raw_svector_ostream VecOS(EmittedBinContents); 204 StreamerContext C = createStreamer(VecOS); 205 constexpr uint8_t DwarfVersion = 4; 206 C.Ctx->setDwarfVersion(DwarfVersion); 207 emitDebugLineSection(C); 208 C.Streamer->finish(); 209 readAndCheckDebugContents( 210 EmittedBinContents.str(), 211 {/* Total length=*/0x30, 0, 0, 0, 212 /* DWARF version=*/DwarfVersion, 0, 213 /* Prologue length=*/0x14, 0, 0, 0, 214 /* min_inst_length=*/1, 215 /*max_ops_per_inst=*/1, 216 /* default_is_stmt=*/DWARF2_LINE_DEFAULT_IS_STMT, 217 /* line_base=*/static_cast<uint8_t>(-5), 218 /* line_range=*/14, 219 /* opcode_base=*/13}, DwarfVersion); 220 } 221 222 TEST_F(DwarfLineTableHeaders, TestDWARF5HeaderEmission) { 223 if (!MRI) 224 GTEST_SKIP(); 225 226 SmallString<0> EmittedBinContents; 227 raw_svector_ostream VecOS(EmittedBinContents); 228 StreamerContext C = createStreamer(VecOS); 229 constexpr uint8_t DwarfVersion = 5; 230 C.Ctx->setDwarfVersion(DwarfVersion); 231 emitDebugLineSection(C); 232 C.Streamer->finish(); 233 readAndCheckDebugContents( 234 EmittedBinContents.str(), 235 {/* Total length=*/0x43, 0, 0, 0, 236 /* DWARF version=*/DwarfVersion, 0, 237 /* ptr size=*/8, 238 /* segment=*/0, 239 /* Prologue length=*/0x25, 0, 0, 0, 240 /* min_inst_length=*/1, 241 /*max_ops_per_inst=*/1, 242 /* default_is_stmt=*/DWARF2_LINE_DEFAULT_IS_STMT, 243 /* line_base=*/static_cast<uint8_t>(-5), 244 /* line_range=*/14, 245 /* opcode_base=*/13}, DwarfVersion); 246 } 247