Index: llvm/trunk/include/llvm/MC/MCDwarf.h =================================================================== --- llvm/trunk/include/llvm/MC/MCDwarf.h +++ llvm/trunk/include/llvm/MC/MCDwarf.h @@ -32,6 +32,7 @@ template class ArrayRef; class MCAsmBackend; class MCContext; +class MCDwarfLineStr; class MCObjectStreamer; class MCStreamer; class MCSymbol; @@ -214,15 +215,18 @@ unsigned getFile(StringRef &Directory, StringRef &FileName, MD5::MD5Result *Checksum, unsigned FileNumber = 0); - std::pair Emit(MCStreamer *MCOS, - MCDwarfLineTableParams Params) const; std::pair Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params, - ArrayRef SpecialOpcodeLengths) const; + Optional &LineStr) const; + std::pair + Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params, + ArrayRef SpecialOpcodeLengths, + Optional &LineStr) const; private: void emitV2FileDirTables(MCStreamer *MCOS) const; - void emitV5FileDirTables(MCStreamer *MCOS) const; + void emitV5FileDirTables(MCStreamer *MCOS, + Optional &LineStr) const; }; class MCDwarfDwoLineTable { @@ -250,7 +254,8 @@ static void Emit(MCObjectStreamer *MCOS, MCDwarfLineTableParams Params); // This emits the Dwarf file and the line tables for a given Compile Unit. - void EmitCU(MCObjectStreamer *MCOS, MCDwarfLineTableParams Params) const; + void EmitCU(MCObjectStreamer *MCOS, MCDwarfLineTableParams Params, + Optional &LineStr) const; unsigned getFile(StringRef &Directory, StringRef &FileName, MD5::MD5Result *Checksum, unsigned FileNumber = 0); Index: llvm/trunk/include/llvm/MC/MCObjectFileInfo.h =================================================================== --- llvm/trunk/include/llvm/MC/MCObjectFileInfo.h +++ llvm/trunk/include/llvm/MC/MCObjectFileInfo.h @@ -79,6 +79,7 @@ MCSection *DwarfAbbrevSection; MCSection *DwarfInfoSection; MCSection *DwarfLineSection; + MCSection *DwarfLineStrSection; MCSection *DwarfFrameSection; MCSection *DwarfPubTypesSection; const MCSection *DwarfDebugInlineSection; @@ -234,6 +235,7 @@ MCSection *getDwarfAbbrevSection() const { return DwarfAbbrevSection; } MCSection *getDwarfInfoSection() const { return DwarfInfoSection; } MCSection *getDwarfLineSection() const { return DwarfLineSection; } + MCSection *getDwarfLineStrSection() const { return DwarfLineStrSection; } MCSection *getDwarfFrameSection() const { return DwarfFrameSection; } MCSection *getDwarfPubNamesSection() const { return DwarfPubNamesSection; } MCSection *getDwarfPubTypesSection() const { return DwarfPubTypesSection; } Index: llvm/trunk/include/llvm/MC/StringTableBuilder.h =================================================================== --- llvm/trunk/include/llvm/MC/StringTableBuilder.h +++ llvm/trunk/include/llvm/MC/StringTableBuilder.h @@ -23,7 +23,7 @@ /// \brief Utility for building string tables with deduplicated suffixes. class StringTableBuilder { public: - enum Kind { ELF, WinCOFF, MachO, RAW }; + enum Kind { ELF, WinCOFF, MachO, RAW, DWARF }; private: DenseMap StringIndexMap; Index: llvm/trunk/lib/MC/MCDwarf.cpp =================================================================== --- llvm/trunk/lib/MC/MCDwarf.cpp +++ llvm/trunk/lib/MC/MCDwarf.cpp @@ -28,6 +28,7 @@ #include "llvm/MC/MCSection.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" +#include "llvm/MC/StringTableBuilder.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Endian.h" #include "llvm/Support/EndianStream.h" @@ -45,6 +46,29 @@ using namespace llvm; +/// Manage the .debug_line_str section contents, if we use it. +class llvm::MCDwarfLineStr { + MCSymbol *LineStrLabel = nullptr; + StringTableBuilder LineStrings{StringTableBuilder::DWARF}; + bool UseRelocs = false; + +public: + /// Construct an instance that can emit .debug_line_str (for use in a normal + /// v5 line table). + explicit MCDwarfLineStr(MCContext &Ctx) { + UseRelocs = Ctx.getAsmInfo()->doesDwarfUseRelocationsAcrossSections(); + if (UseRelocs) + LineStrLabel = + Ctx.getObjectFileInfo()->getDwarfLineStrSection()->getBeginSymbol(); + } + + /// Emit a reference to the string. + void emitRef(MCStreamer *MCOS, StringRef Path); + + /// Emit the .debug_line_str section if appropriate. + void emitSection(MCStreamer *MCOS); +}; + static inline uint64_t ScaleAddrDelta(MCContext &Context, uint64_t AddrDelta) { unsigned MinInsnLength = Context.getAsmInfo()->getMinInstAlignment(); if (MinInsnLength == 1) @@ -108,6 +132,18 @@ } // +// This helper routine returns an expression of Start + IntVal . +// +static inline const MCExpr * +makeStartPlusIntExpr(MCContext &Ctx, const MCSymbol &Start, int IntVal) { + MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None; + const MCExpr *LHS = MCSymbolRefExpr::create(&Start, Variant, Ctx); + const MCExpr *RHS = MCConstantExpr::create(IntVal, Ctx); + const MCExpr *Res = MCBinaryExpr::create(MCBinaryExpr::Add, LHS, RHS, Ctx); + return Res; +} + +// // This emits the Dwarf line table for the specified section from the entries // in the LineSection. // @@ -205,22 +241,31 @@ if (LineTables.empty()) return; + // In a v5 non-split line table, put the strings in a separate section. + Optional LineStr; + if (context.getDwarfVersion() >= 5) + LineStr = MCDwarfLineStr(context); + // Switch to the section where the table will be emitted into. MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfLineSection()); // Handle the rest of the Compile Units. for (const auto &CUIDTablePair : LineTables) - CUIDTablePair.second.EmitCU(MCOS, Params); + CUIDTablePair.second.EmitCU(MCOS, Params, LineStr); + + if (LineStr) + LineStr->emitSection(MCOS); } void MCDwarfDwoLineTable::Emit(MCStreamer &MCOS, MCDwarfLineTableParams Params) const { - MCOS.EmitLabel(Header.Emit(&MCOS, Params, None).second); + Optional NoLineStr(None); + MCOS.EmitLabel(Header.Emit(&MCOS, Params, None, NoLineStr).second); } std::pair -MCDwarfLineTableHeader::Emit(MCStreamer *MCOS, - MCDwarfLineTableParams Params) const { +MCDwarfLineTableHeader::Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params, + Optional &LineStr) const { static const char StandardOpcodeLengths[] = { 0, // length of DW_LNS_copy 1, // length of DW_LNS_advance_pc @@ -237,8 +282,10 @@ }; assert(array_lengthof(StandardOpcodeLengths) >= (Params.DWARF2LineOpcodeBase - 1U)); - return Emit(MCOS, Params, makeArrayRef(StandardOpcodeLengths, - Params.DWARF2LineOpcodeBase - 1)); + return Emit( + MCOS, Params, + makeArrayRef(StandardOpcodeLengths, Params.DWARF2LineOpcodeBase - 1), + LineStr); } static const MCExpr *forceExpAbs(MCStreamer &OS, const MCExpr* Expr) { @@ -257,6 +304,28 @@ OS.EmitValue(ABS, Size); } +void MCDwarfLineStr::emitSection(MCStreamer *MCOS) { + // Switch to the .debug_line_str section. + MCOS->SwitchSection( + MCOS->getContext().getObjectFileInfo()->getDwarfLineStrSection()); + // Emit the strings without perturbing the offsets we used. + LineStrings.finalizeInOrder(); + SmallString<0> Data; + Data.resize(LineStrings.getSize()); + LineStrings.write((uint8_t *)Data.data()); + MCOS->EmitBinaryData(Data.str()); +} + +void MCDwarfLineStr::emitRef(MCStreamer *MCOS, StringRef Path) { + int RefSize = 4; // FIXME: Support DWARF-64 + size_t Offset = LineStrings.add(Path); + if (UseRelocs) { + MCContext &Ctx = MCOS->getContext(); + MCOS->EmitValue(makeStartPlusIntExpr(Ctx, *LineStrLabel, Offset), RefSize); + } else + MCOS->EmitIntValue(Offset, RefSize); +} + void MCDwarfLineTableHeader::emitV2FileDirTables(MCStreamer *MCOS) const { // First the directory table. for (auto &Dir : MCDwarfDirs) { @@ -277,18 +346,29 @@ MCOS->EmitIntValue(0, 1); // Terminate the file list. } -void MCDwarfLineTableHeader::emitV5FileDirTables(MCStreamer *MCOS) const { - // The directory format, which is just inline null-terminated strings. +void MCDwarfLineTableHeader::emitV5FileDirTables( + MCStreamer *MCOS, Optional &LineStr) const { + // The directory format, which is just a list of the directory paths. In a + // non-split object, these are references to .debug_line_str; in a split + // object, they are inline strings. MCOS->EmitIntValue(1, 1); MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_path); - MCOS->EmitULEB128IntValue(dwarf::DW_FORM_string); - // Then the list of directory paths. CompilationDir comes first. + MCOS->EmitULEB128IntValue(LineStr ? dwarf::DW_FORM_line_strp + : dwarf::DW_FORM_string); MCOS->EmitULEB128IntValue(MCDwarfDirs.size() + 1); - MCOS->EmitBytes(CompilationDir); - MCOS->EmitBytes(StringRef("\0", 1)); - for (auto &Dir : MCDwarfDirs) { - MCOS->EmitBytes(Dir); // The DirectoryName, and... - MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator. + if (LineStr) { + // Record path strings, emit references here. + LineStr->emitRef(MCOS, CompilationDir); + for (auto &Dir : MCDwarfDirs) + LineStr->emitRef(MCOS, Dir); + } else { + // The list of directory paths. CompilationDir comes first. + MCOS->EmitBytes(CompilationDir); + MCOS->EmitBytes(StringRef("\0", 1)); + for (auto &Dir : MCDwarfDirs) { + MCOS->EmitBytes(Dir); // The DirectoryName, and... + MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator. + } } // The file format, which is the inline null-terminated filename and a @@ -296,7 +376,8 @@ // in the v5 table. Emit MD5 checksums if we have them. MCOS->EmitIntValue(HasMD5 ? 3 : 2, 1); MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_path); - MCOS->EmitULEB128IntValue(dwarf::DW_FORM_string); + MCOS->EmitULEB128IntValue(LineStr ? dwarf::DW_FORM_line_strp + : dwarf::DW_FORM_string); MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_directory_index); MCOS->EmitULEB128IntValue(dwarf::DW_FORM_udata); if (HasMD5) { @@ -307,8 +388,12 @@ MCOS->EmitULEB128IntValue(MCDwarfFiles.size() - 1); for (unsigned i = 1; i < MCDwarfFiles.size(); ++i) { assert(!MCDwarfFiles[i].Name.empty()); - MCOS->EmitBytes(MCDwarfFiles[i].Name); // FileName and... - MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator. + if (LineStr) + LineStr->emitRef(MCOS, MCDwarfFiles[i].Name); + else { + MCOS->EmitBytes(MCDwarfFiles[i].Name); // FileName and... + MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator. + } MCOS->EmitULEB128IntValue(MCDwarfFiles[i].DirIndex); // Directory number. if (HasMD5) { MD5::MD5Result *Cksum = MCDwarfFiles[i].Checksum; @@ -321,7 +406,8 @@ std::pair MCDwarfLineTableHeader::Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params, - ArrayRef StandardOpcodeLengths) const { + ArrayRef StandardOpcodeLengths, + Optional &LineStr) const { MCContext &context = MCOS->getContext(); // Create a symbol at the beginning of the line table. @@ -386,7 +472,7 @@ // Put out the directory and file tables. The formats vary depending on // the version. if (LineTableVersion >= 5) - emitV5FileDirTables(MCOS); + emitV5FileDirTables(MCOS, LineStr); else emitV2FileDirTables(MCOS); @@ -398,8 +484,9 @@ } void MCDwarfLineTable::EmitCU(MCObjectStreamer *MCOS, - MCDwarfLineTableParams Params) const { - MCSymbol *LineEndSym = Header.Emit(MCOS, Params).second; + MCDwarfLineTableParams Params, + Optional &LineStr) const { + MCSymbol *LineEndSym = Header.Emit(MCOS, Params, LineStr).second; // Put out the line tables. for (const auto &LineSec : MCLineSections.getMCLineEntries()) Index: llvm/trunk/lib/MC/MCObjectFileInfo.cpp =================================================================== --- llvm/trunk/lib/MC/MCObjectFileInfo.cpp +++ llvm/trunk/lib/MC/MCObjectFileInfo.cpp @@ -228,6 +228,9 @@ DwarfLineSection = Ctx->getMachOSection("__DWARF", "__debug_line", MachO::S_ATTR_DEBUG, SectionKind::getMetadata(), "section_line"); + DwarfLineStrSection = + Ctx->getMachOSection("__DWARF", "__debug_line_str", MachO::S_ATTR_DEBUG, + SectionKind::getMetadata(), "section_line_str"); DwarfFrameSection = Ctx->getMachOSection("__DWARF", "__debug_frame", MachO::S_ATTR_DEBUG, SectionKind::getMetadata()); @@ -527,6 +530,9 @@ Ctx->getELFSection(".debug_abbrev", DebugSecType, 0); DwarfInfoSection = Ctx->getELFSection(".debug_info", DebugSecType, 0); DwarfLineSection = Ctx->getELFSection(".debug_line", DebugSecType, 0); + DwarfLineStrSection = + Ctx->getELFSection(".debug_line_str", DebugSecType, + ELF::SHF_MERGE | ELF::SHF_STRINGS, 1, ""); DwarfFrameSection = Ctx->getELFSection(".debug_frame", DebugSecType, 0); DwarfPubNamesSection = Ctx->getELFSection(".debug_pubnames", DebugSecType, 0); @@ -677,7 +683,11 @@ COFF::IMAGE_SCN_MEM_DISCARDABLE | COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ, SectionKind::getMetadata(), "section_line"); - + DwarfLineStrSection = Ctx->getCOFFSection( + ".debug_line_str", + COFF::IMAGE_SCN_MEM_DISCARDABLE | COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + COFF::IMAGE_SCN_MEM_READ, + SectionKind::getMetadata(), "section_line_str"); DwarfFrameSection = Ctx->getCOFFSection( ".debug_frame", COFF::IMAGE_SCN_MEM_DISCARDABLE | COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | @@ -842,6 +852,8 @@ // TODO: Set the section types and flags. DwarfLineSection = Ctx->getWasmSection(".debug_line", SectionKind::getMetadata()); + DwarfLineStrSection = + Ctx->getWasmSection(".debug_line_str", SectionKind::getMetadata()); DwarfStrSection = Ctx->getWasmSection(".debug_str", SectionKind::getMetadata()); DwarfLocSection = Ctx->getWasmSection(".debug_loc", SectionKind::getMetadata()); DwarfAbbrevSection = Ctx->getWasmSection(".debug_abbrev", SectionKind::getMetadata(), "section_abbrev"); Index: llvm/trunk/lib/MC/StringTableBuilder.cpp =================================================================== --- llvm/trunk/lib/MC/StringTableBuilder.cpp +++ llvm/trunk/lib/MC/StringTableBuilder.cpp @@ -31,6 +31,7 @@ // correct. switch (K) { case RAW: + case DWARF: Size = 0; break; case MachO: @@ -116,6 +117,7 @@ } void StringTableBuilder::finalize() { + assert(K != DWARF); finalizeStringTable(/*Optimize=*/true); } Index: llvm/trunk/test/MC/ELF/debug-md5.s =================================================================== --- llvm/trunk/test/MC/ELF/debug-md5.s +++ llvm/trunk/test/MC/ELF/debug-md5.s @@ -1,4 +1,4 @@ -// RUN: llvm-mc -triple x86_64-unknown-unknown -dwarf-version 5 -filetype=obj %s -o -| llvm-dwarfdump --debug-line - | FileCheck %s +// RUN: llvm-mc -triple x86_64-unknown-unknown -dwarf-version 5 -filetype=obj %s -o - | llvm-dwarfdump --debug-line --debug-line-str -v - | FileCheck %s .file 1 "dir1/foo" md5 "00112233445566778899aabbccddeeff" .file 2 "dir2" "bar" md5 "ffeeddccbbaa99887766554433221100" @@ -9,10 +9,17 @@ # CHECK: debug_line[0x00000000] # CHECK: version: 5 -# CHECK: include_directories[ 0] = "" -# CHECK: include_directories[ 1] = "dir1" -# CHECK: include_directories[ 2] = "dir2" +# CHECK: include_directories[ 0] = .debug_line_str[0x00000000] = "" +# CHECK: include_directories[ 1] = .debug_line_str[0x00000001] = "dir1" +# CHECK: include_directories[ 2] = .debug_line_str[0x00000006] = "dir2" # CHECK-NOT: include_directories # CHECK: Dir MD5 Checksum File Name -# CHECK: file_names[ 1] 1 00112233445566778899aabbccddeeff "foo" -# CHECK: file_names[ 2] 2 ffeeddccbbaa99887766554433221100 "bar" +# CHECK: file_names[ 1] 1 00112233445566778899aabbccddeeff .debug_line_str[0x0000000b] = "foo" +# CHECK: file_names[ 2] 2 ffeeddccbbaa99887766554433221100 .debug_line_str[0x0000000f] = "bar" + +# CHECK: .debug_line_str contents: +# CHECK-NEXT: 0x00000000: "" +# CHECK-NEXT: 0x00000001: "dir1" +# CHECK-NEXT: 0x00000006: "dir2" +# CHECK-NEXT: 0x0000000b: "foo" +# CHECK-NEXT: 0x0000000f: "bar"