247 lines · cpp
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-exception6//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 static constexpr char TripleName[] = "x86_64-pc-linux";40 Triple TT;41 std::unique_ptr<MCRegisterInfo> MRI;42 std::unique_ptr<MCAsmInfo> MAI;43 std::unique_ptr<const MCSubtargetInfo> STI;44 const Target *TheTarget;45 46 struct StreamerContext {47 std::unique_ptr<MCObjectFileInfo> MOFI;48 std::unique_ptr<MCContext> Ctx;49 std::unique_ptr<const MCInstrInfo> MII;50 std::unique_ptr<MCStreamer> Streamer;51 };52 53 DwarfLineTableHeaders() : TT(TripleName) {54 llvm::InitializeAllTargetInfos();55 llvm::InitializeAllTargetMCs();56 llvm::InitializeAllDisassemblers();57 58 // If we didn't build x86, do not run the test.59 std::string Error;60 TheTarget = TargetRegistry::lookupTarget(TT, Error);61 if (!TheTarget)62 return;63 64 MRI.reset(TheTarget->createMCRegInfo(TT));65 MCTargetOptions MCOptions;66 MAI.reset(TheTarget->createMCAsmInfo(*MRI, TT, MCOptions));67 STI.reset(TheTarget->createMCSubtargetInfo(TT, "", ""));68 }69 70 /// Create all data structures necessary to operate an assembler71 StreamerContext createStreamer(raw_pwrite_stream &OS) {72 StreamerContext Res;73 Res.Ctx = std::make_unique<MCContext>(TT, 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 TT, *Res.Ctx, std::unique_ptr<MCAsmBackend>(MAB), std::move(OW),86 std::unique_ptr<MCCodeEmitter>(MCE), *STI));87 return Res;88 }89 90 /// Emit a .debug_line section with the given context parameters91 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 function99 MCSection *Section = C.MOFI->getTextSection();100 Section->setHasInstructions(true);101 TheStreamer->switchSection(Section);102 TheStreamer->emitCFIStartProc(true);103 104 // Create a mock dwarfloc105 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_line115 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 section141 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 section162 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 section181 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} // namespace197 198TEST_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 222TEST_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