diff --git a/llvm/test/tools/llvm-dwp/X86/cu_and_tu_info_section_v5.s b/llvm/test/tools/llvm-dwp/X86/cu_and_tu_info_section_v5.s new file mode 100644 index 00000000000000..8a1051ba751f1c --- /dev/null +++ b/llvm/test/tools/llvm-dwp/X86/cu_and_tu_info_section_v5.s @@ -0,0 +1,48 @@ +# This test checks if llvm-dwp can find the compilation unit if +# both type and compile units are available in the debug info section (v5) + +# RUN: llvm-mc --triple=x86_64-unknown-linux --filetype=obj --split-dwarf-file=%t.dwo -dwarf-version=5 %s -o %t.o +# RUN: llvm-dwp %t.dwo -o %t.dwp +# RUN: llvm-dwarfdump -debug-info -debug-tu-index %t.dwp | FileCheck %s + +## Note: For this test we do not need to define the DIE for the structure type, as we only want to +## have the info on the type and compile units. + +# CHECK-DAG: .debug_info.dwo contents +# CHECK-NOT: Type Unit: +# CHECK: 0x00000000: Compile Unit: length = 0x00000011, format = DWARF32, version = 0x0005, unit_type = DW_UT_split_compile, abbr_offset = 0x0000, addr_size = 0x08, DWO_id = {{.*}} (next unit at 0x00000015) + .section .debug_info.dwo,"e",@progbits + .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit +.Ldebug_info_dwo_start0: + .short 5 # DWARF version number + .byte 6 # DWARF Unit Type (DW_UT_split_type) + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad 5657452045627120676 # Type Signature + .long 25 # Type DIE Offset + .byte 1 # Abbrev [1] DW_TAG_type_unit + .byte 2 # Abbrev [2] DW_TAG_structure_type + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end0: + .section .debug_info.dwo,"e",@progbits + .long .Ldebug_info_dwo_end1-.Ldebug_info_dwo_start1 # Length of Unit +.Ldebug_info_dwo_start1: + .short 5 # DWARF version number + .byte 5 # DWARF Unit Type (DW_UT_split_compile) + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad -1506010254921578184 + .byte 3 # Abbrev [3] DW_TAG_compile_unit +.Ldebug_info_dwo_end1: + .section .debug_abbrev.dwo,"e",@progbits + .byte 1 # Abbreviation Code + .byte 65 # DW_TAG_type_unit + .byte 1 # DW_CHILDREN_yes + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 0 # DW_CHILDREN_no + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) diff --git a/llvm/test/tools/llvm-dwp/X86/invalid_cu_header_length.s b/llvm/test/tools/llvm-dwp/X86/invalid_cu_header_length.s index 8f32822eebe65a..ba0c0860dacda5 100644 --- a/llvm/test/tools/llvm-dwp/X86/invalid_cu_header_length.s +++ b/llvm/test/tools/llvm-dwp/X86/invalid_cu_header_length.s @@ -4,5 +4,5 @@ # CHECK: error: compile unit exceeds .debug_info section range: 20 >= 6 .section .debug_info.dwo,"e",@progbits - .long 16 # Length of Unit + .long 16 # Length of Unit .short 5 # Version \ No newline at end of file diff --git a/llvm/test/tools/llvm-dwp/X86/invalid_tu_header_length.s b/llvm/test/tools/llvm-dwp/X86/invalid_tu_header_length.s new file mode 100644 index 00000000000000..cef2ea3600cb0d --- /dev/null +++ b/llvm/test/tools/llvm-dwp/X86/invalid_tu_header_length.s @@ -0,0 +1,15 @@ +# RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.o \ +# RUN: -split-dwarf-file=%t.dwo -dwarf-version=5 +# RUN: not llvm-dwp %t.dwo -o %t.dwp 2>&1 | FileCheck %s + +# CHECK: error: type unit is missing type offset + .section .debug_info.dwo,"e",@progbits + .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit +.Ldebug_info_dwo_start0: + .short 5 # DWARF version number + .byte 6 # DWARF Unit Type (DW_UT_split_type) + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad 5657452045627120676 # Type Signature +.Ldebug_info_dwo_end0: + diff --git a/llvm/test/tools/llvm-dwp/X86/multiple_debug_info_sections_in_dwp.s b/llvm/test/tools/llvm-dwp/X86/multiple_debug_info_sections_in_dwp.s new file mode 100644 index 00000000000000..7819f8115280fa --- /dev/null +++ b/llvm/test/tools/llvm-dwp/X86/multiple_debug_info_sections_in_dwp.s @@ -0,0 +1,24 @@ +# RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.dwp +# RUN: not llvm-dwp %t.dwp -o /dev/null 2>&1 | FileCheck %s + +## Note: To reach the test point, we need to use comdat groups, to have multiple +## .debug_info sections. One comdat group needs to have one complete unit header, +## the second one may be completely empty. +## Furthermore, the .debug_cu_index also does not need to be complete. + +# CHECK: error: expected exactly one occurrence of a debug info section in a .dwp file + .section .debug_info.dwo,"G",@progbits,0xFDFDFDFD,comdat + .long .Ldebug_info_dwo_end1-.Ldebug_info_dwo_start1 # Length of Unit +.Ldebug_info_dwo_start1: + .short 5 # DWARF version number + .byte 5 # DWARF Unit Type (DW_UT_split_compile) + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad -1506010254921578184 + .byte 1 # Abbrev [1] DW_TAG_compile_unit +.Ldebug_info_dwo_end1: +.section .debug_info.dwo,"G",@progbits,0xDFDFDFDF,comdat + .long 0 # Length of Unit + .section .debug_cu_index, "", @progbits +## Incomplete Header: + .long 2 # Version diff --git a/llvm/test/tools/llvm-dwp/X86/wrong-unit-type-info-v5.s b/llvm/test/tools/llvm-dwp/X86/no_cu_found.s similarity index 89% rename from llvm/test/tools/llvm-dwp/X86/wrong-unit-type-info-v5.s rename to llvm/test/tools/llvm-dwp/X86/no_cu_found.s index fbcdc36da05491..015919294ad9b4 100644 --- a/llvm/test/tools/llvm-dwp/X86/wrong-unit-type-info-v5.s +++ b/llvm/test/tools/llvm-dwp/X86/no_cu_found.s @@ -1,12 +1,12 @@ # RUN: llvm-mc --triple=x86_64-unknown-linux --filetype=obj --split-dwarf-file=%t.dwo -dwarf-version=5 %s -o %t.o # RUN: not llvm-dwp %t.dwo -o /dev/null 2>&1 | FileCheck %s -# CHECK: error: {{.*}}: unit type DW_UT_split_compile type not found in debug_info header. Unexpected unit type 0x12 found +# CHECK: error: no compile unit found in file: {{.*}}no_cu_found.s .section .debug_info.dwo,"e",@progbits .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit .Ldebug_info_dwo_start0: .short 5 # DWARF version number - .byte 12 # DWARF Unit Type + .byte 12 # DWARF Unit Type (DW_TAG_string_type, wrong type) .byte 8 # Address Size (in bytes) .long 0 # Offset Into Abbrev. Section .quad -1173350285159172090 diff --git a/llvm/test/tools/llvm-dwp/X86/wrong-unit-type-info-v4.s b/llvm/test/tools/llvm-dwp/X86/wrong-unit-type-info-v4.s new file mode 100644 index 00000000000000..d8feb310d61a87 --- /dev/null +++ b/llvm/test/tools/llvm-dwp/X86/wrong-unit-type-info-v4.s @@ -0,0 +1,15 @@ +# RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.dwp +# RUN: not llvm-dwp %t.dwp -o %t 2>&1 | FileCheck %s + +# CHECK: error: {{.*}}: top level DIE is not a compile unit + .section .debug_info.dwo,"e",@progbits + .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit +.Ldebug_info_dwo_start0: + .short 4 # DWARF version number + .long 0 # Offset Into Abbrev. Section + .byte 8 # Address Size (in bytes) + .byte 1 # Abbrev [1] 0xb:0x1 DW_TAG_string_type +.Ldebug_info_dwo_end0: + .section .debug_abbrev.dwo,"e",@progbits + .byte 1 # Abbreviation Code + .byte 18 # DW_TAG_string_type diff --git a/llvm/tools/llvm-dwp/llvm-dwp.cpp b/llvm/tools/llvm-dwp/llvm-dwp.cpp index c7cb1fc23a40bc..926df64f4e37ac 100644 --- a/llvm/tools/llvm-dwp/llvm-dwp.cpp +++ b/llvm/tools/llvm-dwp/llvm-dwp.cpp @@ -77,9 +77,10 @@ static uint64_t debugStrOffsetsHeaderSize(DataExtractor StrOffsetsData, return 8; // unit length: 4 bytes, version: 2 bytes, padding: 2 bytes. } -// Holds data for Skeleton and Split Compilation Unit Headers as defined in -// Dwarf 5 specification, 7.5.1.2 and Dwarf 4 specification 7.5.1.1. -struct CompileUnitHeader { +// Holds data for Skeleton, Split Compilation, and Type Unit Headers (only in +// v5) as defined in Dwarf 5 specification, 7.5.1.2, 7.5.1.3 and Dwarf 4 +// specification 7.5.1.1. +struct InfoSectionUnitHeader { // unit_length field. Note that the type is uint64_t even in 32-bit dwarf. uint64_t Length = 0; @@ -108,9 +109,10 @@ struct CompileUnitHeader { uint8_t HeaderSize = 0; }; -// Parse and return the header of the compile unit. -static Expected parseCompileUnitHeader(StringRef Info) { - CompileUnitHeader Header; +// Parse and return the header of an info section compile/type unit. +static Expected +parseInfoSectionUnitHeader(StringRef Info) { + InfoSectionUnitHeader Header; Error Err = Error::success(); uint64_t Offset = 0; DWARFDataExtractor InfoData(Info, true, 0); @@ -141,15 +143,22 @@ static Expected parseCompileUnitHeader(StringRef Info) { MinHeaderLength = 7; } if (Header.Length < MinHeaderLength) { - return make_error( - "compile unit length is too small: expected at least " + - utostr(MinHeaderLength) + " got " + utostr(Header.Length) + "."); + return make_error("unit length is too small: expected at least " + + utostr(MinHeaderLength) + " got " + + utostr(Header.Length) + "."); } if (Header.Version >= 5) { Header.UnitType = InfoData.getU8(&Offset); Header.AddrSize = InfoData.getU8(&Offset); Header.DebugAbbrevOffset = InfoData.getU32(&Offset); Header.Signature = InfoData.getU64(&Offset); + if (Header.UnitType == dwarf::DW_UT_split_type) { + // Type offset. + MinHeaderLength += 4; + if (Header.Length < MinHeaderLength) + return make_error("type unit is missing type offset"); + InfoData.getU32(&Offset); + } } else { // Note that, address_size and debug_abbrev_offset fields have switched // places between dwarf version 4 and 5. @@ -165,7 +174,7 @@ static void writeStringsAndOffsets(MCStreamer &Out, DWPStringPool &Strings, MCSection *StrOffsetSection, StringRef CurStrSection, StringRef CurStrOffsetSection, - const CompileUnitHeader &Header) { + const InfoSectionUnitHeader &Header) { // Could possibly produce an error or warning if one of these was non-null but // the other was null. if (CurStrSection.empty() || CurStrOffsetSection.empty()) @@ -259,14 +268,9 @@ getIndexedString(dwarf::Form Form, DataExtractor InfoData, uint64_t &InfoOffset, return StrData.getCStr(&StrOffset); } -static Expected getCUIdentifiers(StringRef Abbrev, - StringRef Info, - StringRef StrOffsets, - StringRef Str) { - Expected HeaderOrError = parseCompileUnitHeader(Info); - if (!HeaderOrError) - return HeaderOrError.takeError(); - CompileUnitHeader &Header = *HeaderOrError; +static Expected +getCUIdentifiers(InfoSectionUnitHeader &Header, StringRef Abbrev, + StringRef Info, StringRef StrOffsets, StringRef Str) { DataExtractor InfoData(Info, true, 0); uint64_t Offset = Header.HeaderSize; if (Header.Version >= 5 && Header.UnitType != dwarf::DW_UT_split_compile) @@ -540,13 +544,14 @@ static Error handleSection( const StringMap> &KnownSections, const MCSection *StrSection, const MCSection *StrOffsetSection, const MCSection *TypesSection, const MCSection *CUIndexSection, - const MCSection *TUIndexSection, const SectionRef &Section, MCStreamer &Out, + const MCSection *TUIndexSection, const MCSection *InfoSection, + const SectionRef &Section, MCStreamer &Out, std::deque> &UncompressedSections, uint32_t (&ContributionOffsets)[8], UnitIndexEntry &CurEntry, StringRef &CurStrSection, StringRef &CurStrOffsetSection, - std::vector &CurTypesSection, StringRef &InfoSection, - StringRef &AbbrevSection, StringRef &CurCUIndexSection, - StringRef &CurTUIndexSection) { + std::vector &CurTypesSection, + std::vector &CurInfoSection, StringRef &AbbrevSection, + StringRef &CurCUIndexSection, StringRef &CurTUIndexSection) { if (Section.isBSS()) return Error::success(); @@ -574,21 +579,14 @@ static Error handleSection( if (DWARFSectionKind Kind = SectionPair->second.second) { auto Index = getContributionIndex(Kind); - if (Kind != DW_SECT_EXT_TYPES) { + if (Kind != DW_SECT_EXT_TYPES && Kind != DW_SECT_INFO) { CurEntry.Contributions[Index].Offset = ContributionOffsets[Index]; ContributionOffsets[Index] += (CurEntry.Contributions[Index].Length = Contents.size()); } - switch (Kind) { - case DW_SECT_INFO: - InfoSection = Contents; - break; - case DW_SECT_ABBREV: + if (Kind == DW_SECT_ABBREV) { AbbrevSection = Contents; - break; - default: - break; } } @@ -603,6 +601,8 @@ static Error handleSection( CurCUIndexSection = Contents; else if (OutSection == TUIndexSection) CurTUIndexSection = Contents; + else if (OutSection == InfoSection) + CurInfoSection.push_back(Contents); else { Out.SwitchSection(OutSection); Out.emitBytes(Contents); @@ -656,8 +656,9 @@ static Error write(MCStreamer &Out, ArrayRef Inputs) { MCSection *const TypesSection = MCOFI.getDwarfTypesDWOSection(); MCSection *const CUIndexSection = MCOFI.getDwarfCUIndexSection(); MCSection *const TUIndexSection = MCOFI.getDwarfTUIndexSection(); + MCSection *const InfoSection = MCOFI.getDwarfInfoDWOSection(); const StringMap> KnownSections = { - {"debug_info.dwo", {MCOFI.getDwarfInfoDWOSection(), DW_SECT_INFO}}, + {"debug_info.dwo", {InfoSection, DW_SECT_INFO}}, {"debug_types.dwo", {MCOFI.getDwarfTypesDWOSection(), DW_SECT_EXT_TYPES}}, {"debug_str_offsets.dwo", {StrOffsetSection, DW_SECT_STR_OFFSETS}}, {"debug_str.dwo", {StrSection, static_cast(0)}}, @@ -692,7 +693,7 @@ static Error write(MCStreamer &Out, ArrayRef Inputs) { StringRef CurStrSection; StringRef CurStrOffsetSection; std::vector CurTypesSection; - StringRef InfoSection; + std::vector CurInfoSection; StringRef AbbrevSection; StringRef CurCUIndexSection; StringRef CurTUIndexSection; @@ -700,41 +701,86 @@ static Error write(MCStreamer &Out, ArrayRef Inputs) { for (const auto &Section : Obj.sections()) if (auto Err = handleSection( KnownSections, StrSection, StrOffsetSection, TypesSection, - CUIndexSection, TUIndexSection, Section, Out, + CUIndexSection, TUIndexSection, InfoSection, Section, Out, UncompressedSections, ContributionOffsets, CurEntry, - CurStrSection, CurStrOffsetSection, CurTypesSection, InfoSection, - AbbrevSection, CurCUIndexSection, CurTUIndexSection)) + CurStrSection, CurStrOffsetSection, CurTypesSection, + CurInfoSection, AbbrevSection, CurCUIndexSection, + CurTUIndexSection)) return Err; - if (InfoSection.empty()) + if (CurInfoSection.empty()) continue; - Expected CompileUnitHeaderOrErr = - parseCompileUnitHeader(InfoSection); - if (!CompileUnitHeaderOrErr) - return CompileUnitHeaderOrErr.takeError(); - CompileUnitHeader &CompileUnitHeader = *CompileUnitHeaderOrErr; + Expected HeaderOrErr = + parseInfoSectionUnitHeader(CurInfoSection.front()); + if (!HeaderOrErr) + return HeaderOrErr.takeError(); + InfoSectionUnitHeader &Header = *HeaderOrErr; writeStringsAndOffsets(Out, Strings, StrOffsetSection, CurStrSection, - CurStrOffsetSection, CompileUnitHeader); + CurStrOffsetSection, Header); + uint32_t &InfoSectionOffset = + ContributionOffsets[getContributionIndex(DW_SECT_INFO)]; if (CurCUIndexSection.empty()) { - Expected EID = getCUIdentifiers( - AbbrevSection, InfoSection, CurStrOffsetSection, CurStrSection); - if (!EID) - return createFileError(Input, EID.takeError()); - const auto &ID = *EID; - auto P = IndexEntries.insert(std::make_pair(ID.Signature, CurEntry)); - if (!P.second) - return buildDuplicateError(*P.first, ID, ""); - P.first->second.Name = ID.Name; - P.first->second.DWOName = ID.DWOName; + bool FoundCUUnit = false; + Out.SwitchSection(InfoSection); + for (StringRef Info : CurInfoSection) { + if (FoundCUUnit) + break; + uint64_t UnitOffset = 0; + while (Info.size() > UnitOffset) { + Expected HeaderOrError = + parseInfoSectionUnitHeader(Info.substr(UnitOffset, Info.size())); + if (!HeaderOrError) + return HeaderOrError.takeError(); + InfoSectionUnitHeader &Header = *HeaderOrError; + + UnitIndexEntry Entry = CurEntry; + auto &C = Entry.Contributions[getContributionIndex(DW_SECT_INFO)]; + C.Offset = InfoSectionOffset; + C.Length = Header.Length + 4; + + if (Header.Version < 5 || + Header.UnitType == dwarf::DW_UT_split_compile) { + Expected EID = getCUIdentifiers( + Header, AbbrevSection, Info.substr(UnitOffset, C.Length), + CurStrOffsetSection, CurStrSection); + + if (!EID) + return createFileError(Input, EID.takeError()); + const auto &ID = *EID; + auto P = IndexEntries.insert(std::make_pair(ID.Signature, Entry)); + if (!P.second) + return buildDuplicateError(*P.first, ID, ""); + P.first->second.Name = ID.Name; + P.first->second.DWOName = ID.DWOName; + + Out.emitBytes(Info.substr(UnitOffset, C.Length)); + InfoSectionOffset += C.Length; + FoundCUUnit = true; + break; + } + UnitOffset += Header.Length + 4; + } + } + + if (!FoundCUUnit) + return make_error("no compile unit found in file: " + Input); + + // Add types from the .debug_types section from DWARF < 5. addAllTypes(Out, TypeIndexEntries, TypesSection, CurTypesSection, CurEntry, ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES)]); + continue; } + if (CurInfoSection.size() != 1) + return make_error("expected exactly one occurrence of a debug " + "info section in a .dwp file"); + StringRef DwpSingleInfoSection = CurInfoSection.front(); + DWARFUnitIndex CUIndex(DW_SECT_INFO); DataExtractor CUIndexData(CurCUIndexSection, Obj.isLittleEndian(), 0); if (!CUIndex.parse(CUIndexData)) @@ -744,14 +790,23 @@ static Error write(MCStreamer &Out, ArrayRef Inputs) { "unsupported cu_index version: " + utostr(CUIndex.getVersion()) + " (only version 2 is supported)"); + Out.SwitchSection(InfoSection); for (const DWARFUnitIndex::Entry &E : CUIndex.getRows()) { auto *I = E.getContributions(); if (!I) continue; auto P = IndexEntries.insert(std::make_pair(E.getSignature(), CurEntry)); + StringRef CUInfoSection = + getSubsection(DwpSingleInfoSection, E, DW_SECT_INFO); + Expected HeaderOrError = + parseInfoSectionUnitHeader(CUInfoSection); + if (!HeaderOrError) + return HeaderOrError.takeError(); + InfoSectionUnitHeader &Header = *HeaderOrError; + Expected EID = getCUIdentifiers( - getSubsection(AbbrevSection, E, DW_SECT_ABBREV), - getSubsection(InfoSection, E, DW_SECT_INFO), + Header, getSubsection(AbbrevSection, E, DW_SECT_ABBREV), + CUInfoSection, getSubsection(CurStrOffsetSection, E, DW_SECT_STR_OFFSETS), CurStrSection); if (!EID) @@ -771,6 +826,11 @@ static Error write(MCStreamer &Out, ArrayRef Inputs) { C.Length = I->Length; ++I; } + unsigned Index = getContributionIndex(DW_SECT_INFO); + auto &C = NewEntry.Contributions[Index]; + Out.emitBytes(CUInfoSection); + C.Offset = InfoSectionOffset; + InfoSectionOffset += C.Length; } if (!CurTypesSection.empty()) {