Index: llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h =================================================================== --- llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h +++ llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h @@ -61,6 +61,7 @@ std::unique_ptr Line; std::unique_ptr DebugFrame; std::unique_ptr EHFrame; + std::unique_ptr Macro; std::unique_ptr Macinfo; std::unique_ptr Names; std::unique_ptr AppleNames; @@ -273,11 +274,9 @@ const DWARFDebugFrame *getEHFrame(); /// Get a pointer to the parsed DebugMacro object. - const DWARFDebugMacro *getDebugMacinfo(); - - /// Get a pointer to the parsed dwo DebugMacro object. - const DWARFDebugMacro *getDebugMacinfoDWO(); - + Error + getDWARFDebugMacro(std::unique_ptr &MacroPtr, + bool IsLittleEndian, bool IsDWO, bool IsMacro); /// Get a reference to the parsed accelerator table object. const DWARFDebugNames &getDebugNames(); Index: llvm/include/llvm/DebugInfo/DWARF/DWARFDebugMacro.h =================================================================== --- llvm/include/llvm/DebugInfo/DWARF/DWARFDebugMacro.h +++ llvm/include/llvm/DebugInfo/DWARF/DWARFDebugMacro.h @@ -10,14 +10,57 @@ #define LLVM_DEBUGINFO_DWARF_DWARFDEBUGMACRO_H #include "llvm/ADT/SmallVector.h" -#include "llvm/Support/DataExtractor.h" +#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/WithColor.h" #include namespace llvm { class raw_ostream; +class DWARFContext; class DWARFDebugMacro { + /// DWARFv5 section 6.3.1 Macro Information Header. + enum HeaderFlagMask { +#define HANDLE_MACRO_FLAG(ID, NAME) MACRO_##NAME = ID, +#include "llvm/BinaryFormat/Dwarf.def" + }; + struct MacroHeader { + /// Macro version information number. + uint16_t Version = 0; + + /// The bits of the flags field are interpreted as a set of flags, some of + /// which may indicate that additional fields follow. The following flags, + /// beginning with the least significant bit, are defined: + /// offset_size_flag: + /// If the offset_size_flag is zero, the header is for a 32-bit DWARF + /// format macro section and all offsets are 4 bytes long; if it is one, + /// the header is for a 64-bit DWARF format macro section and all offsets + /// are 8 bytes long. + /// debug_line_offset_flag: + /// If the debug_line_offset_flag is one, the debug_line_offset field (see + /// below) is present. If zero, that field is omitted. + /// opcode_operands_table_flag: + /// If the opcode_operands_table_flag is one, the opcode_operands_table + /// field (see below) is present. If zero, that field is omitted. + uint8_t Flags; + + /// debug_line_offset + /// An offset in the .debug_line section of the beginning of the line + /// number information in the containing compilation unit, encoded as a + /// 4-byte offset for a 32-bit DWARF format macro section and an 8-byte + /// offset for a 64-bit DWARF format macro section. + uint64_t DebugLineOffset; + + /// Print the macro header from the debug_macro section. + void dumpMacroHeader(raw_ostream &OS) const; + + /// Parse the debug_macro header. + Error parseMacroHeader(DWARFDataExtractor Data, uint64_t *Offset); + }; + /// A single macro entry within a macro list. struct Entry { /// The type of the macro entry. @@ -40,6 +83,10 @@ }; struct MacroList { + // A value 0 in the `Header.Version` field indicates that we're parsing + // a macinfo[.dwo] section which doesn't have header itself, hence + // for that case other fields in the `Header` are uninitialized. + MacroHeader Header; SmallVector Macros; uint64_t Offset; }; @@ -50,11 +97,13 @@ public: DWARFDebugMacro() = default; - /// Print the macro list found within the debug_macinfo section. + /// Print the macro list found within the debug_macinfo/debug_macro section. void dump(raw_ostream &OS) const; - /// Parse the debug_macinfo section accessible via the 'data' parameter. - void parse(DataExtractor data); + /// Parse the debug_macinfo/debug_macro section accessible via the 'Data' + /// parameter. + Error parse(DataExtractor StringExtractor, DWARFDataExtractor Data, + bool IsMacro); /// Return whether the section has any entries. bool empty() const { return MacroLists.empty(); } Index: llvm/include/llvm/DebugInfo/DWARF/DWARFObject.h =================================================================== --- llvm/include/llvm/DebugInfo/DWARF/DWARFObject.h +++ llvm/include/llvm/DebugInfo/DWARF/DWARFObject.h @@ -47,6 +47,7 @@ virtual StringRef getStrSection() const { return ""; } virtual const DWARFSection &getRangesSection() const { return Dummy; } virtual const DWARFSection &getRnglistsSection() const { return Dummy; } + virtual const DWARFSection &getMacroSection() const { return Dummy; } virtual StringRef getMacinfoSection() const { return ""; } virtual StringRef getMacinfoDWOSection() const { return ""; } virtual const DWARFSection &getPubnamesSection() const { return Dummy; } Index: llvm/lib/DebugInfo/DWARF/DWARFContext.cpp =================================================================== --- llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -292,6 +292,29 @@ } } +Error +DWARFContext::getDWARFDebugMacro(std::unique_ptr &MacroPtr, + bool IsLittleEndian, bool IsDWO, + bool IsMacro) { + if (MacroPtr) + return Error::success(); + MacroPtr = std::make_unique(); + std::unique_ptr Macro = std::make_unique(); + DWARFDataExtractor Data = + IsMacro ? DWARFDataExtractor(*DObj, DObj->getMacroSection(), + IsLittleEndian, 0) + : DWARFDataExtractor(DObj->getMacinfoSection(), + IsLittleEndian, 0); + // FIXME: Add support for debug_macro.dwo section. + DWARFDataExtractor DataDWO = + DWARFDataExtractor(DObj->getMacinfoDWOSection(), IsLittleEndian, 0); + if (Error Err = Macro->parse(getStringExtractor(), + IsDWO ? DataDWO : Data, IsMacro)) + return std::move(Err); + MacroPtr = std::move(Macro); + return Error::success(); +} + static void dumpLoclistsSection(raw_ostream &OS, DIDumpOptions DumpOpts, DWARFDataExtractor Data, const MCRegisterInfo *MRI, @@ -444,14 +467,37 @@ DObj->getEHFrameSection().Data)) getEHFrame()->dump(OS, getRegisterInfo(), *Off); + if (shouldDump(Explicit, ".debug_macro", DIDT_ID_DebugMacro, + DObj->getMacroSection().Data)) { + Error Err = + getDWARFDebugMacro(Macro, /*IsLittleEndian=*/true, + /*IsDWO=*/false, /*IsMacro=*/true); + if (Err) + DumpOpts.RecoverableErrorHandler(std::move(Err)); + else + Macro.get()->dump(OS); + } + if (shouldDump(Explicit, ".debug_macinfo", DIDT_ID_DebugMacro, DObj->getMacinfoSection())) { - getDebugMacinfo()->dump(OS); + Error Err = + getDWARFDebugMacro(Macinfo, /*IsLittleEndian=*/true, + /*IsDWO=*/false, /*IsMacro=*/false); + if (Err) + DumpOpts.RecoverableErrorHandler(std::move(Err)); + else + Macinfo.get()->dump(OS); } if (shouldDump(Explicit, ".debug_macinfo.dwo", DIDT_ID_DebugMacro, DObj->getMacinfoDWOSection())) { - getDebugMacinfoDWO()->dump(OS); + Error Err = + getDWARFDebugMacro(MacinfoDWO, /*IsLittleEndian=*/true, + /*IsDWO=*/true, /*IsMacro=*/false); + if (Err) + DumpOpts.RecoverableErrorHandler(std::move(Err)); + else + MacinfoDWO.get()->dump(OS); } if (shouldDump(Explicit, ".debug_aranges", DIDT_ID_DebugAranges, @@ -815,27 +861,6 @@ return DebugFrame.get(); } -const DWARFDebugMacro *DWARFContext::getDebugMacinfoDWO() { - if (MacinfoDWO) - return MacinfoDWO.get(); - - DataExtractor MacinfoDWOData(DObj->getMacinfoDWOSection(), isLittleEndian(), - 0); - MacinfoDWO.reset(new DWARFDebugMacro()); - MacinfoDWO->parse(MacinfoDWOData); - return MacinfoDWO.get(); -} - -const DWARFDebugMacro *DWARFContext::getDebugMacinfo() { - if (Macinfo) - return Macinfo.get(); - - DataExtractor MacinfoData(DObj->getMacinfoSection(), isLittleEndian(), 0); - Macinfo.reset(new DWARFDebugMacro()); - Macinfo->parse(MacinfoData); - return Macinfo.get(); -} - template static T &getAccelTable(std::unique_ptr &Cache, const DWARFObject &Obj, const DWARFSection &Section, StringRef StringSection, @@ -1476,6 +1501,7 @@ DWARFSectionMap PubtypesSection; DWARFSectionMap GnuPubnamesSection; DWARFSectionMap GnuPubtypesSection; + DWARFSectionMap MacroSection; DWARFSectionMap *mapNameToDWARFSection(StringRef Name) { return StringSwitch(Name) @@ -1503,6 +1529,7 @@ .Case("apple_namespaces", &AppleNamespacesSection) .Case("apple_namespac", &AppleNamespacesSection) .Case("apple_objc", &AppleObjCSection) + .Case("debug_macro", &MacroSection) .Default(nullptr); } @@ -1847,6 +1874,7 @@ const DWARFSection &getRnglistsSection() const override { return RnglistsSection; } + const DWARFSection &getMacroSection() const override { return MacroSection; } StringRef getMacinfoSection() const override { return MacinfoSection; } StringRef getMacinfoDWOSection() const override { return MacinfoDWOSection; } const DWARFSection &getPubnamesSection() const override { return PubnamesSection; } Index: llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp =================================================================== --- llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp +++ llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp @@ -8,6 +8,8 @@ #include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h" #include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include @@ -15,10 +17,22 @@ using namespace llvm; using namespace dwarf; +void DWARFDebugMacro::MacroHeader::dumpMacroHeader(raw_ostream &OS) const { + // FIXME: Add support for dumping opcode_operands_table + OS << format("macro header: version = 0x%04" PRIx16 ", flags = 0x%02" PRIx8, + Version, Flags); + if (Flags & MACRO_DEBUG_LINE_OFFSET) + OS << format(", debug_line_offset = 0x%04" PRIx64 "\n", DebugLineOffset); + else + OS << "\n"; +} + void DWARFDebugMacro::dump(raw_ostream &OS) const { unsigned IndLevel = 0; for (const auto &Macros : MacroLists) { OS << format("0x%08" PRIx64 ":\n", Macros.Offset); + if (Macros.Header.Version >= 5) + Macros.Header.dumpMacroHeader(OS); for (const Entry &E : Macros.Macros) { // There should not be DW_MACINFO_end_file when IndLevel is Zero. However, // this check handles the case of corrupted ".debug_macinfo" section. @@ -28,22 +42,34 @@ for (unsigned I = 0; I < IndLevel; I++) OS << " "; IndLevel += (E.Type == DW_MACINFO_start_file); - - WithColor(OS, HighlightColor::Macro).get() << MacinfoString(E.Type); + // Based on which version we are handling choose appropriate macro forms. + if (Macros.Header.Version >= 5) + WithColor(OS, HighlightColor::Macro).get() << MacroString(E.Type); + else + WithColor(OS, HighlightColor::Macro).get() << MacinfoString(E.Type); switch (E.Type) { default: - // Got a corrupted ".debug_macinfo" section (invalid macinfo type). + // Got a corrupted ".debug_macinfo/.debug_macro" section (invalid + // macinfo type). break; - case DW_MACINFO_define: - case DW_MACINFO_undef: + // debug_macro and debug_macinfo share some common encodings. + // DW_MACRO_define == DW_MACINFO_define + // DW_MACRO_undef == DW_MACINFO_undef + // DW_MACRO_start_file == DW_MACINFO_start_file + // DW_MACRO_end_file == DW_MACINFO_end_file + // For readability/uniformity we are using DW_MACRO_*. + case DW_MACRO_define: + case DW_MACRO_undef: + case DW_MACRO_define_strp: + case DW_MACRO_undef_strp: OS << " - lineno: " << E.Line; OS << " macro: " << E.MacroStr; break; - case DW_MACINFO_start_file: + case DW_MACRO_start_file: OS << " - lineno: " << E.Line; OS << " filenum: " << E.File; break; - case DW_MACINFO_end_file: + case DW_MACRO_end_file: break; case DW_MACINFO_vendor_ext: OS << " - constant: " << E.ExtConstant; @@ -55,23 +81,29 @@ } } -void DWARFDebugMacro::parse(DataExtractor data) { +Error DWARFDebugMacro::parse(DataExtractor StringExtractor, + DWARFDataExtractor Data, bool IsMacro) { uint64_t Offset = 0; MacroList *M = nullptr; - while (data.isValidOffset(Offset)) { + while (Data.isValidOffset(Offset)) { if (!M) { MacroLists.emplace_back(); M = &MacroLists.back(); M->Offset = Offset; + if (IsMacro) { + auto Err = M->Header.parseMacroHeader(Data, &Offset); + if (Err) + return std::move(Err); + } } // A macro list entry consists of: M->Macros.emplace_back(); Entry &E = M->Macros.back(); // 1. Macinfo type - E.Type = data.getULEB128(&Offset); + E.Type = Data.getULEB128(&Offset); if (E.Type == 0) { - // Reached end of a ".debug_macinfo" section contribution. + // Reached end of a ".debug_macinfo/debug_macro" section contribution. M = nullptr; continue; } @@ -81,28 +113,64 @@ // Got a corrupted ".debug_macinfo" section (invalid macinfo type). // Push the corrupted entry to the list and halt parsing. E.Type = DW_MACINFO_invalid; - return; - case DW_MACINFO_define: - case DW_MACINFO_undef: + return Error::success(); + // debug_macro and debug_macinfo share some common encodings. + // DW_MACRO_define == DW_MACINFO_define + // DW_MACRO_undef == DW_MACINFO_undef + // DW_MACRO_start_file == DW_MACINFO_start_file + // DW_MACRO_end_file == DW_MACINFO_end_file + // For readibility/uniformity we are using DW_MACRO_*. + case DW_MACRO_define: + case DW_MACRO_undef: // 2. Source line - E.Line = data.getULEB128(&Offset); + E.Line = Data.getULEB128(&Offset); // 3. Macro string - E.MacroStr = data.getCStr(&Offset); + E.MacroStr = Data.getCStr(&Offset); break; - case DW_MACINFO_start_file: + case DW_MACRO_define_strp: + case DW_MACRO_undef_strp: { + uint64_t StrOffset = 0; // 2. Source line - E.Line = data.getULEB128(&Offset); + E.Line = Data.getULEB128(&Offset); + // 3. Macro string + // FIXME: Add support for DWARF64 + StrOffset = Data.getRelocatedValue(/*OffsetSize=*/4, &Offset); + E.MacroStr = StringExtractor.getCStr(&StrOffset); + break; + } + case DW_MACRO_start_file: + // 2. Source line + E.Line = Data.getULEB128(&Offset); // 3. Source file id - E.File = data.getULEB128(&Offset); + E.File = Data.getULEB128(&Offset); break; - case DW_MACINFO_end_file: + case DW_MACRO_end_file: break; case DW_MACINFO_vendor_ext: // 2. Vendor extension constant - E.ExtConstant = data.getULEB128(&Offset); + E.ExtConstant = Data.getULEB128(&Offset); // 3. Vendor extension string - E.ExtStr = data.getCStr(&Offset); + E.ExtStr = Data.getCStr(&Offset); break; } } + return Error::success(); +} + +Error DWARFDebugMacro::MacroHeader::parseMacroHeader(DWARFDataExtractor Data, + uint64_t *Offset) { + Version = Data.getU16(Offset); + uint8_t FlagData = Data.getU8(Offset); + // FIXME: Add support for DWARF64 + if (FlagData & MACRO_OFFSET_SIZE) + return createStringError(errc::not_supported, "DWARF64 is not supported"); + + // FIXME: Add support for parsing opcode_operands_table + if (FlagData & MACRO_OPCODE_OPERANDS_TABLE) + return createStringError(errc::not_supported, + "opcode_operands_table is not supported"); + Flags = FlagData; + if (Flags & MACRO_DEBUG_LINE_OFFSET) + DebugLineOffset = Data.getU32(Offset); + return Error::success(); } Index: llvm/test/DebugInfo/X86/debug-macro-macinfo.s =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/debug-macro-macinfo.s @@ -0,0 +1,47 @@ +## This test checks that llvm-dwarfdump can dump both debug_macro and debug_macinfo +## sections present in the same object. + +# RUN: llvm-mc -triple x86_64-unknown-linux -filetype=obj %s -o -| \ +# RUN: llvm-dwarfdump -debug-macro - | FileCheck -strict-whitespace -match-full-lines %s + +# CHECK:.debug_macro contents: +# CHECK-NEXT:0x00000000: +# CHECK-NEXT:macro header: version = 0x0005, flags = 0x02, debug_line_offset = 0x0000 +# CHECK-NEXT:DW_MACRO_start_file - lineno: 0 filenum: 0 +# CHECK-NEXT: DW_MACRO_define_strp - lineno: 1 macro: DWARF_VERSION 5 +# CHECK-NEXT:DW_MACRO_end_file + +# CHECK:.debug_macinfo contents: +# CHECK-NEXT:0x00000000: +# CHECK-NEXT:DW_MACINFO_start_file - lineno: 0 filenum: 2 +# CHECK-NEXT: DW_MACINFO_define - lineno: 6 macro: DWARF_VERSION 4 +# CHECK-NEXT:DW_MACINFO_end_file + + .section .debug_macro,"",@progbits +.Lcu_macro_begin0: + .short 5 # Macro information version + .byte 2 # Flags: 32 bit, debug_line_offset present + .long 0 # debug_line_offset + .byte 3 # DW_MACRO_start_file + .byte 0 # Line Number + .byte 0 # File Number + .byte 5 # DW_MACRO_define_strp + .byte 1 # Line Number + .long .Linfo_string0 # Macro String + .byte 4 # DW_MACRO_end_file + .byte 0 # End Of Macro List Mark + + .section .debug_macinfo,"",@progbits +.Lcu_macro_begin1: + .byte 3 # DW_MACINFO_start_file + .byte 0 # Line Number + .byte 2 # File Number + .byte 1 # DW_MACINFO_define + .byte 6 # Line Number + .asciz "DWARF_VERSION 4" # Macro String + .byte 4 # DW_MACINFO_end_file + .byte 0 # End Of Macro List Mark + + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "DWARF_VERSION 5" Index: llvm/test/DebugInfo/X86/debug-macro-v5.s =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/debug-macro-v5.s @@ -0,0 +1,54 @@ +## This test checks llvm-dwarfdump can dump debug_macro section containing +## multiple macro units. + +# RUN: llvm-mc -triple x86_64-unknown-linux -filetype=obj %s -o -| \ +# RUN: llvm-dwarfdump -debug-macro - | FileCheck -strict-whitespace -match-full-lines %s + +# CHECK:.debug_macro contents: +# CHECK-NEXT:0x00000000: +# CHECK-NEXT:macro header: version = 0x0005, flags = 0x02, debug_line_offset = 0x0000 +# CHECK-NEXT:DW_MACRO_start_file - lineno: 0 filenum: 0 +# CHECK-NEXT: DW_MACRO_start_file - lineno: 1 filenum: 6 +# CHECK-NEXT: DW_MACRO_define_strp - lineno: 1 macro: FOO 5 +# CHECK-NEXT: DW_MACRO_end_file +# CHECK-NEXT: DW_MACRO_undef_strp - lineno: 8 macro: WORLD1 +# CHECK-NEXT:DW_MACRO_end_file + +# CHECK:0x{{.*}}: +# CHECK-NEXT:macro header: version = 0x0005, flags = 0x00 +# CHECK-NEXT:DW_MACRO_define_strp - lineno: 0 macro: WORLD 2 + + .section .debug_macro,"",@progbits +.Lcu_macro_begin0: + .short 5 # Macro information version + .byte 2 # Flags: 32 bit, debug_line_offset present + .long 0 # debug_line_offset + .byte 3 # DW_MACRO_start_file + .byte 0 # Line Number + .byte 0 # File Number + .byte 3 # DW_MACRO_start_file + .byte 1 # Line Number + .byte 6 # File Number + .byte 5 # DW_MACRO_define_strp + .byte 1 # Line Number + .long .Linfo_string0 # Macro String + .byte 4 # DW_MACRO_end_file + .byte 6 # DW_MACRO_undef_strp + .byte 8 # Line Number + .long .Linfo_string1 # Macro String + .byte 4 # DW_MACRO_end_file + .byte 0 # End Of Macro List Mark + .short 5 # Macro information version + .byte 0 # Flags: 32 bit + .byte 5 # DW_MACRO_define_strp + .byte 0 # Line Number + .long .Linfo_string2 # Macro String + .byte 0 # End Of Macro List Mark + + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "FOO 5" +.Linfo_string1: + .asciz "WORLD1" +.Linfo_string2: + .asciz "WORLD 2" Index: llvm/test/DebugInfo/X86/unsupported-dwarf64-debug-macro-v5.s =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/unsupported-dwarf64-debug-macro-v5.s @@ -0,0 +1,13 @@ +## This test checks llvm-dwarfdump emits correct error diagnostics for the +## unsupported case where DWARF64 flag is present in the debug_macro section header. + +# RUN: llvm-mc -triple x86_64-unknown-linux -filetype=obj %s -o -| \ +# RUN: not llvm-dwarfdump -debug-macro - /dev/null 2>&1 | FileCheck %s + +# CHECK:error: DWARF64 is not supported + + .section .debug_macro,"",@progbits +.Lcu_macro_begin0: + .short 5 # Macro information version + .byte 3 # Flags: 64 bit, debug_line_offset present + .quad 0 # debug_line_offset Index: llvm/test/DebugInfo/X86/unsupported-opcode_operands_table-debug-macro-v5.s =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/unsupported-opcode_operands_table-debug-macro-v5.s @@ -0,0 +1,13 @@ +## This test checks llvm-dwarfdump emits correct error diagnostics for the +## unsupported case where the opcode_operands_table flag is present in the +## debug_macro section header. + +# RUN: llvm-mc -triple x86_64-unknown-linux -filetype=obj %s -o -| \ +# RUN: not llvm-dwarfdump -debug-macro - /dev/null 2>&1 | FileCheck %s + +# CHECK:error: opcode_operands_table is not supported + + .section .debug_macro,"",@progbits +.Lcu_macro_begin0: + .short 5 # Macro information version + .byte 4 # Flags: 32 bit, debug_line_offset absent, opcode_operands_table present