diff --git a/llvm/include/llvm/ObjectYAML/DWARFEmitter.h b/llvm/include/llvm/ObjectYAML/DWARFEmitter.h --- a/llvm/include/llvm/ObjectYAML/DWARFEmitter.h +++ b/llvm/include/llvm/ObjectYAML/DWARFEmitter.h @@ -38,6 +38,7 @@ Error emitDebugInfo(raw_ostream &OS, const Data &DI); Error emitDebugLine(raw_ostream &OS, const Data &DI); Error emitDebugAddr(raw_ostream &OS, const Data &DI); +Error emitDebugStrOffsets(raw_ostream &OS, const Data &DI); Expected>> emitDebugSections(StringRef YAMLString, bool ApplyFixups = false, diff --git a/llvm/include/llvm/ObjectYAML/DWARFYAML.h b/llvm/include/llvm/ObjectYAML/DWARFYAML.h --- a/llvm/include/llvm/ObjectYAML/DWARFYAML.h +++ b/llvm/include/llvm/ObjectYAML/DWARFYAML.h @@ -177,11 +177,20 @@ std::vector SegAddrPairs; }; +struct StringOffsetsTable { + dwarf::DwarfFormat Format; + Optional Length; + yaml::Hex16 Version; + yaml::Hex16 Padding; + std::vector Offsets; +}; + struct Data { bool IsLittleEndian; bool Is64BitAddrSize; std::vector AbbrevDecls; std::vector DebugStrings; + std::vector DebugStrOffsets; std::vector ARanges; std::vector DebugRanges; std::vector DebugAddr; @@ -218,6 +227,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::LineTableOpcode) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::SegAddrPair) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::AddrTableEntry) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::DWARFYAML::StringOffsetsTable) namespace llvm { namespace yaml { @@ -290,6 +300,10 @@ static void mapping(IO &IO, DWARFYAML::AddrTableEntry &AddrTable); }; +template <> struct MappingTraits { + static void mapping(IO &IO, DWARFYAML::StringOffsetsTable &StrOffsetsTable); +}; + template <> struct MappingTraits { static void mapping(IO &IO, DWARFYAML::InitialLength &DWARF); }; diff --git a/llvm/lib/ObjectYAML/DWARFEmitter.cpp b/llvm/lib/ObjectYAML/DWARFEmitter.cpp --- a/llvm/lib/ObjectYAML/DWARFEmitter.cpp +++ b/llvm/lib/ObjectYAML/DWARFEmitter.cpp @@ -416,6 +416,30 @@ return Error::success(); } +Error DWARFYAML::emitDebugStrOffsets(raw_ostream &OS, const Data &DI) { + for (const DWARFYAML::StringOffsetsTable &Table : DI.DebugStrOffsets) { + uint64_t Length; + if (Table.Length) + Length = *Table.Length; + else + // sizeof(version) + sizeof(padding) = 4 + Length = + 4 + Table.Offsets.size() * (Table.Format == dwarf::DWARF64 ? 8 : 4); + + writeInitialLength(Table.Format, Length, OS, DI.IsLittleEndian); + writeInteger((uint16_t)Table.Version, OS, DI.IsLittleEndian); + writeInteger((uint16_t)Table.Padding, OS, DI.IsLittleEndian); + + for (uint64_t Offset : Table.Offsets) { + cantFail(writeVariableSizedInteger(Offset, + Table.Format == dwarf::DWARF64 ? 8 : 4, + OS, DI.IsLittleEndian)); + } + } + + return Error::success(); +} + using EmitFuncType = Error (*)(raw_ostream &, const DWARFYAML::Data &); static Error diff --git a/llvm/lib/ObjectYAML/DWARFYAML.cpp b/llvm/lib/ObjectYAML/DWARFYAML.cpp --- a/llvm/lib/ObjectYAML/DWARFYAML.cpp +++ b/llvm/lib/ObjectYAML/DWARFYAML.cpp @@ -46,6 +46,8 @@ SecNames.insert("debug_gnu_pubnames"); if (GNUPubTypes) SecNames.insert("debug_gnu_pubtypes"); + if (!DebugStrOffsets.empty()) + SecNames.insert("debug_str_offsets"); return SecNames; } @@ -69,6 +71,7 @@ IO.mapOptional("debug_info", DWARF.CompileUnits); IO.mapOptional("debug_line", DWARF.DebugLines); IO.mapOptional("debug_addr", DWARF.DebugAddr); + IO.mapOptional("debug_str_offsets", DWARF.DebugStrOffsets); IO.setContext(OldContext); } @@ -221,6 +224,15 @@ IO.mapOptional("Entries", AddrTable.SegAddrPairs); } +void MappingTraits::mapping( + IO &IO, DWARFYAML::StringOffsetsTable &StrOffsetsTable) { + IO.mapOptional("Format", StrOffsetsTable.Format, dwarf::DWARF32); + IO.mapOptional("Length", StrOffsetsTable.Length); + IO.mapOptional("Version", StrOffsetsTable.Version, 5); + IO.mapOptional("Padding", StrOffsetsTable.Padding, 0); + IO.mapOptional("Offsets", StrOffsetsTable.Offsets); +} + void MappingTraits::mapping( IO &IO, DWARFYAML::InitialLength &InitialLength) { IO.mapRequired("TotalLength", InitialLength.TotalLength); diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -973,6 +973,8 @@ else if (Name == ".debug_gnu_pubtypes") Err = DWARFYAML::emitPubSection(*OS, *DWARF.GNUPubTypes, DWARF.IsLittleEndian, /*IsGNUStyle=*/true); + else if (Name == ".debug_str_offsets") + Err = DWARFYAML::emitDebugStrOffsets(*OS, DWARF); else llvm_unreachable("unexpected emitDWARF() call"); diff --git a/llvm/test/tools/yaml2obj/ELF/DWARF/debug-str-offsets.yaml b/llvm/test/tools/yaml2obj/ELF/DWARF/debug-str-offsets.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/yaml2obj/ELF/DWARF/debug-str-offsets.yaml @@ -0,0 +1,280 @@ +## Test that yaml2obj emits a .debug_str_offsets section when requested. + +## a) Generate and verify a little endian .debug_str_offsets section. + +# RUN: yaml2obj -DENDIAN=ELFDATA2LSB --docnum=1 %s -o %t1.le.o +# RUN: llvm-readobj --sections --section-data %t1.le.o | \ +# RUN: FileCheck -DSIZE=48 -DADDRALIGN=1 %s --check-prefixes=SHDR,DWARF-LE + +# SHDR: Index: 1 +# SHDR-NEXT: Name: .debug_str_offsets (1) +# SHDR-NEXT: Type: SHT_PROGBITS (0x1) +# SHDR-NEXT: Flags [ (0x0) +# SHDR-NEXT: ] +# SHDR-NEXT: Address: 0x0 +# SHDR-NEXT: Offset: 0x40 +# SHDR-NEXT: Size: [[SIZE]] +# SHDR-NEXT: Link: 0 +# SHDR-NEXT: Info: 0 +# SHDR-NEXT: AddressAlignment: [[ADDRALIGN]] +# SHDR-NEXT: EntrySize: 0 +# DWARF-LE-NEXT: SectionData ( +# DWARF-LE-NEXT: 0000: 0C000000 05000000 34120000 78560000 |........4...xV..| +## ^------- unit_length (4-byte) +## ^--- version (2-byte) +## ^--- padding (2-byte) +## ^------- offsets[0] (4-byte) +## ^------- offsets[1] (4-byte) +# DWARF-LE-NEXT: 0010: FFFFFFFF 14000000 00000000 05000000 |................| +## ^------------------------- unit_length (12-byte) +## ^--- version (2-byte) +## ^--- padding (2-byte) +# DWARF-LE-NEXT: 0020: 34120000 00000000 21430000 00000000 |4.......!C......| +## ^---------------- offsets[0] (8-byte) +## ^---------------- offsets[1] (8-byte) +# DWARF-LE-NEXT: ) + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: [[ENDIAN]] + Type: ET_EXEC + Machine: EM_X86_64 +DWARF: + debug_str_offsets: + - Offsets: + - 0x00001234 + - 0x00005678 + - Format: DWARF64 + Offsets: + - 0x0000000000001234 + - 0x0000000000004321 + +## b) Generate and verify a big endian .debug_str_offsets section. + +# RUN: yaml2obj -DENDIAN=ELFDATA2MSB --docnum=1 %s -o %t1.be.o +# RUN: llvm-readobj --sections --section-data %t1.be.o | \ +# RUN: FileCheck -DSIZE=48 -DADDRALIGN=1 %s --check-prefixes=SHDR,DWARF-BE + +# DWARF-BE-NEXT: SectionData ( +# DWARF-BE-NEXT: 0000: 0000000C 00050000 00001234 00005678 |...........4..Vx| +## ^------- unit_length (4-byte) +## ^--- version (2-byte) +## ^--- padding (2-byte) +## ^------- offsets[0] (4-byte) +## ^------- offsets[1] (4-byte) +# DWARF-BE-NEXT: 0010: FFFFFFFF 00000000 00000014 00050000 |................| +## ^------------------------- unit_length (12-byte) +## ^--- version (2-byte) +## ^--- padding (2-byte) +# DWARF-BE-NEXT: 0020: 00000000 00001234 00000000 00004321 |.......4......C!| +## ^---------------- offsets[0] (8-byte) +## ^---------------- offsets[1] (8-byte) +# DWARF-BE-NEXT: ) + +## c) Test that the length, version and padding fields can be overwritten. + +# RUN: yaml2obj --docnum=2 %s -o %t2.o +# RUN: llvm-readelf --hex-dump=.debug_str_offsets %t2.o | \ +# RUN: FileCheck %s --check-prefix=OVERWRITE + +# OVERWRITE: Hex dump of section '.debug_str_offsets': +# OVERWRITE-NEXT: 0x00000000 34120000 06001200 4....... +## ^------- unit_length (4-byte) +## ^--- version (2-byte) +## ^--- padding (2-bye) + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +DWARF: + debug_str_offsets: + - Length: 0x1234 + Version: 6 + Padding: 0x12 + +## d) Test that an empty 'Offsets' field is allowed. + +# RUN: yaml2obj --docnum=3 %s -o %t3.o +# RUN: llvm-readelf --hex-dump=.debug_str_offsets %t3.o | \ +# RUN: FileCheck %s --check-prefix=EMPTY-OFFSETS + +# EMPTY-OFFSETS: Hex dump of section '.debug_str_offsets': +# EMPTY-OFFSETS-NEXT: 0x00000000 04000000 05000000 ........ +## ^------- unit_length (4-byte) +## ^--- version (2-byte) +## ^--- padding (2-byte) + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +DWARF: + debug_str_offsets: + - Offsets: [] + +## e) Test that the .debug_str_offsets section is omitted if the "debug_str_offsets" is empty. + +# RUN: yaml2obj --docnum=4 %s -o %t4.o +# RUN: llvm-readelf -S %t4.o | FileCheck %s --check-prefix=OMIT + +# OMIT: There are 3 section headers, starting at offset 0x58: +# OMIT: Section Headers: +# OMIT-NEXT: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# OMIT-NEXT: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# OMIT-NEXT: [ 1] .strtab STRTAB 0000000000000000 000040 000001 00 0 0 1 +# OMIT-NEXT: [ 2] .shstrtab STRTAB 0000000000000000 000041 000013 00 0 0 1 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +DWARF: + debug_str_offsets: [] + +## f) Generate the .debug_str_offsets section from raw section content. + +# RUN: yaml2obj --docnum=5 %s -o %t5.o +# RUN: llvm-readobj --sections --section-data %t5.o | \ +# RUN: FileCheck %s -DADDRALIGN=0 -DSIZE=3 --check-prefixes=SHDR,ARBITRARY-CONTENT + +# ARBITRARY-CONTENT: SectionData ( +# ARBITRARY-CONTENT-NEXT: 0000: 112233 +# ARBITRARY-CONTENT-NEXT: ) + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debug_str_offsets + Type: SHT_PROGBITS + Content: "112233" + +## g) Generate the .debug_str_offsets section when the "Size" is specified. + +# RUN: yaml2obj --docnum=6 %s -o %t6.o +# RUN: llvm-readelf --hex-dump=.debug_str_offsets %t6.o | \ +# RUN: FileCheck %s --check-prefix=SIZE + +# SIZE: Hex dump of section '.debug_str_offsets': +# SIZE-NEXT: 0x00000000 00000000 00000000 00000000 00000000 ................ +# SIZE-EMPTY: + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debug_str_offsets + Type: SHT_PROGBITS + Size: 0x10 + +## h) Test that yaml2obj emits an error message when both the "Size" and the +## "debug_str_offsets" entry are specified at the same time. + +# RUN: not yaml2obj --docnum=7 %s 2>&1 | FileCheck %s --check-prefix=ERROR + +# ERROR: yaml2obj: error: cannot specify section '.debug_str_offsets' contents in the 'DWARF' entry and the 'Content' or 'Size' in the 'Sections' entry at the same time + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debug_str_offsets + Type: SHT_PROGBITS + Size: 0x10 +DWARF: + debug_str_offsets: + - Offsets: [] + +## i) Test that yaml2obj emits an error message when both the "Content" and the +## "debug_str_offsets" entry are specified at the same time. + +# RUN: not yaml2obj --docnum=8 %s 2>&1 | FileCheck %s --check-prefix=ERROR + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debug_str_offsets + Type: SHT_PROGBITS + Content: "00" +DWARF: + debug_str_offsets: + - Offsets: [] + +## j) Test that all the properties can be overridden by the section header when +## the "debug_str_offsets" entry doesn't exist. + +# RUN: yaml2obj --docnum=9 %s -o %t9.o +# RUN: llvm-readelf --sections %t9.o | FileCheck %s --check-prefix=OVERRIDDEN + +# OVERRIDDEN: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# OVERRIDDEN: [ 1] .debug_str_offsets STRTAB 0000000000002020 000050 000008 01 A 2 1 2 +# OVERRIDDEN-NEXT: [ 2] .sec STRTAB 0000000000000000 000058 000000 00 0 0 0 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debug_str_offsets + Type: SHT_STRTAB ## SHT_PROGBITS by default. + Flags: [SHF_ALLOC] ## 0 by default. + Link: .sec ## 0 by default. + EntSize: 1 ## 0 by default. + Info: 1 ## 0 by default. + AddressAlign: 2 ## 0 by default. + Address: 0x2020 ## 0x00 by default. + Offset: 0x50 ## 0x40 for the first section. + Size: 0x08 ## Set the "Size" so that we can reuse the check tag "OVERRIDDEN". + - Name: .sec ## Linked by .debug_str_offsets. + Type: SHT_STRTAB + +## k) Test that all the properties can be overridden by the section header when +## the "debug_str_offsets" entry exists. + +# RUN: yaml2obj --docnum=10 %s -o %t10.o +# RUN: llvm-readelf --sections %t10.o | FileCheck %s --check-prefix=OVERRIDDEN + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debug_str_offsets + Type: SHT_STRTAB ## SHT_PROGBITS by default. + Flags: [SHF_ALLOC] ## 0 by default. + Link: .sec ## 0 by default. + EntSize: 1 ## 0 by default. + Info: 1 ## 0 by default. + AddressAlign: 2 ## 1 by default. + Address: 0x2020 ## 0x00 by default. + Offset: 0x50 ## 0x40 for the first section. + - Name: .sec ## Linked by .debug_str_offsets. + Type: SHT_STRTAB +DWARF: + debug_str_offsets: + - Offsets: []