diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -43,8 +43,8 @@ // For --icf={none,safe,all}. enum class ICFLevel { None, Safe, All }; -// For --strip-{all,debug}. -enum class StripPolicy { None, All, Debug }; +// For --strip-{debug-non-line,debug,all}. +enum class StripPolicy { None, DebugNonLine, Debug, All }; // For --unresolved-symbols. enum class UnresolvedPolicy { ReportError, Warn, Ignore }; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -578,12 +578,14 @@ if (Args.hasArg(OPT_relocatable)) return StripPolicy::None; - auto *Arg = Args.getLastArg(OPT_strip_all, OPT_strip_debug); + auto *Arg = Args.getLastArg(OPT_strip_all, OPT_strip_debug, OPT_strip_debug_non_line); if (!Arg) return StripPolicy::None; if (Arg->getOption().getID() == OPT_strip_all) return StripPolicy::All; - return StripPolicy::Debug; + if (Arg->getOption().getID() == OPT_strip_debug) + return StripPolicy::Debug; + return StripPolicy::DebugNonLine; } static uint64_t parseSectionAddress(StringRef S, const opt::Arg &Arg) { @@ -1832,9 +1834,30 @@ } // We do not want to emit debug sections if --strip-all - // or -strip-debug are given. - return Config->Strip != StripPolicy::None && - (S->Name.startswith(".debug") || S->Name.startswith(".zdebug")); + // or -strip-debug are given. If --strip-debug-non-line is specified, + // emit only the sections used for line info. + if (Config->Strip == StripPolicy::All || + Config->Strip == StripPolicy::Debug) { + return S->Name.startswith(".debug") || S->Name.startswith(".zdebug"); + } else if (Config->Strip == StripPolicy::DebugNonLine) { + // Only keep debug sections used to map program counter values + // to file : line numbers. + return StringSwitch(S->Name) + .EndsWith("debug_abbrev", false) + .EndsWith("debug_addr", false) + .EndsWith("debug_aranges", false) + .EndsWith("debug_info", false) + .EndsWith("debug_line_str", false) + .EndsWith("debug_line", false) + .EndsWith("debug_ranges", false) + .EndsWith("debug_rnglists", false) + .EndsWith("debug_str_offsets", false) + .EndsWith("debug_str", false) + .StartsWith(".debug", true) + .StartsWith(".zdebug", true) + .Default(false); + } + return false; }); // Now that the number of partitions is fixed, save a pointer to the main diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -338,6 +338,9 @@ def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">; +def strip_debug_non_line: F<"strip-debug-non-line">, + HelpText<"Emit only debug line number information">; + defm symbol_ordering_file: Eq<"symbol-ordering-file", "Layout sections to place symbols in the order specified by symbol ordering file">; diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h --- a/lld/ELF/OutputSections.h +++ b/lld/ELF/OutputSections.h @@ -17,6 +17,7 @@ #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/ELF.h" #include +#include namespace lld { namespace elf { @@ -100,6 +101,11 @@ void finalize(); template void writeTo(uint8_t *Buf); + void setReducedDebugData(SmallString<0> Data) { + ReducedDebugData = std::move(Data); + Size = ReducedDebugData.size(); + } + template void maybeCompress(); void sort(llvm::function_ref Order); @@ -107,6 +113,9 @@ void sortCtorsDtors(); private: + // Reduced .debug_info/.debug_abbrev/.debug_aranges for --strip-debug-non-line + SmallString<0> ReducedDebugData; + // Used for implementation of --compress-debug-sections option. std::vector ZDebugHeader; llvm::SmallVector CompressedData; diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -193,10 +193,22 @@ // Write section contents to a temporary buffer and compress it. std::vector Buf(Size); - writeTo(Buf.data()); - if (Error E = zlib::compress(toStringRef(Buf), CompressedData)) + if (ReducedDebugData.empty()) + writeTo(Buf.data()); + if (Error E = zlib::compress( + ReducedDebugData.empty() + ? toStringRef(Buf) + : StringRef(ReducedDebugData.data(), ReducedDebugData.size()), + CompressedData)) fatal("compress failed: " + llvm::toString(std::move(E))); + { + // Free memory. + using std::swap; + decltype(ReducedDebugData) tmp; + swap(ReducedDebugData, tmp); + } + // Update section headers. Size = sizeof(Elf_Chdr) + CompressedData.size(); Flags |= SHF_COMPRESSED; @@ -219,7 +231,7 @@ if (Type == SHT_NOBITS) return; - // If -compress-debug-section is specified and if this is a debug seciton, + // If --compress-debug-section is specified and if this is a debug section, // we've already compressed section contents. If that's the case, // just write it down. if (!CompressedData.empty()) { @@ -229,6 +241,14 @@ return; } + // If --strip-debug-non-line is specified and if this is a debug section, + // we've already reduced section contents. If that's the case, + // just write it down. + if (!ReducedDebugData.empty()) { + memcpy(Buf, ReducedDebugData.data(), ReducedDebugData.size()); + return; + } + // Write leading padding. std::vector Sections = getInputSections(this); std::array Filler = getFiller(); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -22,12 +22,19 @@ #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "lld/Common/Threads.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Support/SHA1.h" #include "llvm/Support/xxhash.h" #include +#include +#include using namespace llvm; using namespace llvm::ELF; @@ -60,6 +67,7 @@ void finalizeSections(); void checkExecuteOnly(); void setReservedSymbolSections(); + void reduceNonLineDwarfDebug(); std::vector createPhdrs(Partition &Part); void removeEmptyPTLoad(std::vector &PhdrEntry); @@ -584,6 +592,13 @@ Script->assignAddresses(); + // If --strip-debug-non-line is specified reduce the .debug_abbrev/.debug_info + // sections. Do it right now because: + // - it changes the size of output sections, + // - OutputSection::maybeCompress should compress the reduced sections. + if (Config->Strip == StripPolicy::DebugNonLine) + reduceNonLineDwarfDebug(); + // If -compressed-debug-sections is specified, we need to compress // .debug_* sections. Do it right now because it changes the size of // output sections. @@ -1054,6 +1069,222 @@ } } +template +void writeInt(raw_ostream &OS, value_type value, endianness endian) { + value = byte_swap(value, endian); + OS.write(reinterpret_cast(&value), sizeof(value_type)); +} + +template void Writer::reduceNonLineDwarfDebug() { + OutputSection *AbbrSec = nullptr; + OutputSection *ArangesSec = nullptr; + OutputSection *InfoSec = nullptr; + for (OutputSection *Sec : OutputSections) { + if (Sec->Name == ".debug_abbrev") + AbbrSec = Sec; + else if (Sec->Name == ".debug_info") + InfoSec = Sec; + else if (Sec->Name == ".debug_aranges") + ArangesSec = Sec; + } + + if (AbbrSec == nullptr || InfoSec == nullptr) + return; + + std::vector AbbrBuf(AbbrSec->Size); + AbbrSec->writeTo(AbbrBuf.data()); + std::vector InfoBuf(InfoSec->Size); + InfoSec->writeTo(InfoBuf.data()); + + auto Endian = ELFT::TargetEndianness; + auto MakeDataExtractor = [](StringRef Data) { + uint8_t AddressSize = Config->Is64 ? 8 : 4; + return llvm::DataExtractor(Data, Config->IsLE, AddressSize); + }; + + // map original -> reduced (AbbrevCode, AbbrOffset) + std::unordered_map, + std::pair, + pair_hash> + AbbrCodeOffMap; + SmallString<0> ReducedAbbr; + // Reduce .debug_abbrev + { + raw_svector_ostream OS(ReducedAbbr); + llvm::DataExtractor AbbrData = MakeDataExtractor(toStringRef(AbbrBuf)); + uint32_t AbbrOffset = 0; + uint64_t ReducedAbbrCode = 0; + while (AbbrOffset < AbbrData.getData().size()) { + uint32_t AbbrStart = AbbrOffset; + while (uint64_t AbbrCode = AbbrData.getULEB128(&AbbrOffset)) { + uint64_t AbbrType = AbbrData.getULEB128(&AbbrOffset); + // This would ordinarily be the has_children + // field of the abbreviation. But it's going to + // be false after reducing the information, so + // there's no point in storing it. + AbbrData.getU8(&AbbrOffset); + + // Read to the end of the current abbreviation. + // This is indicated by two zero unsigned LEBs + // in a row. We don't need to parse the data + // yet, so we just scan through the data looking + // for two consecutive 0 bytes indicating the + // end of the abbreviation. + uint32_t AbbrEnd = AbbrOffset; + while ((AbbrEnd + 1 < AbbrData.getData().size()) && + (AbbrData.getData()[AbbrEnd] || + AbbrData.getData()[AbbrEnd + 1])) + AbbrEnd++; + // Account for the two nulls and advance to the + // start of the next abbreviation. + AbbrEnd += 2; + + if (AbbrType == dwarf::DW_TAG_compile_unit) { + ReducedAbbrCode++; + encodeULEB128(ReducedAbbrCode, OS); + encodeULEB128(AbbrType, OS); + OS.write(0); // has_children: false + AbbrCodeOffMap[std::make_pair(AbbrCode, AbbrStart)] = + std::make_pair(ReducedAbbrCode, OS.str().size()); + OS << AbbrData.getData().slice(AbbrOffset, AbbrEnd); + } + AbbrOffset = AbbrEnd; + } + } + // Null terminate the list of abbreviations + OS.write(0); + } + + // map original -> reduced offsets to start of DIE + std::unordered_map InfoOffMap; + SmallString<0> ReducedInfo; + // Reduce .debug_info + { + llvm::DataExtractor InfoData = MakeDataExtractor(toStringRef(InfoBuf)); + llvm::DataExtractor ReducedAbbrData = + MakeDataExtractor(StringRef(ReducedAbbr.data(), ReducedAbbr.size())); + + raw_svector_ostream OS(ReducedInfo); + uint32_t InfoOffset = 0; + while (InfoOffset < InfoData.getData().size()) { + uint32_t InfoStart = InfoOffset; + uint32_t Length = InfoData.getU32(&InfoOffset); + dwarf::DwarfFormat Format = dwarf::DwarfFormat::DWARF32; + if (Length == 0xFFFFFFFF) { + Format = llvm::dwarf::DwarfFormat::DWARF64; + Length = InfoData.getU64(&InfoOffset); + } + bool IsDWARF64 = Format == llvm::dwarf::DwarfFormat::DWARF64; + uint32_t SizeOfLength = IsDWARF64 ? sizeof(uint64_t) : sizeof(uint32_t); + + uint32_t NextInfoOffset = InfoOffset + Length; + uint16_t Version = InfoData.getU16(&InfoOffset); + + uint8_t UnitType = 0; + uint32_t AbbrOffset = 0; + uint8_t AddrSize = 0; + if (Version >= 5) { + UnitType = InfoData.getU8(&InfoOffset); + AddrSize = InfoData.getU8(&InfoOffset); + AbbrOffset = InfoData.getUnsigned(&InfoOffset, SizeOfLength); + } else { + AbbrOffset = InfoData.getUnsigned(&InfoOffset, SizeOfLength); + AddrSize = InfoData.getU8(&InfoOffset); + } + uint64_t AbbrCode = InfoData.getULEB128(&InfoOffset); + + auto It = AbbrCodeOffMap.find(std::make_pair(AbbrCode, AbbrOffset)); + if (It == AbbrCodeOffMap.end()) { + warn("Will not reduce debug_abbrev/debug_info: failed to find " + "abbrev mapping for AbbrCode: " + + toString(AbbrCode) + " AbbrOffset: " + toString(AbbrOffset)); + return; + } + std::tie(AbbrCode, AbbrOffset) = It->second; + uint32_t AttrStart = InfoOffset; + do { + uint32_t Name = ReducedAbbrData.getULEB128(&AbbrOffset); + auto Form = + static_cast(ReducedAbbrData.getULEB128(&AbbrOffset)); + if (Name == 0 && Form == 0) + break; + DWARFFormValue::skipValue( + Form, InfoData, &InfoOffset, + dwarf::FormParams({Version, AddrSize, Format})); + } while (true); + + InfoOffMap[InfoStart] = OS.str().size(); + + Length = sizeof(Version) + ((Version >= 5) ? sizeof(UnitType) : 0) + + SizeOfLength + sizeof(AddrSize) + getULEB128Size(AbbrCode) + + (InfoOffset - AttrStart); + if (IsDWARF64) { + writeInt(OS, uint32_t(0xFFFFFFFF), Endian); + writeInt(OS, uint64_t(Length), Endian); + } else + writeInt(OS, Length, Endian); + writeInt(OS, Version, Endian); + if (Version >= 5) { + writeInt(OS, UnitType, Endian); + writeInt(OS, AddrSize, Endian); + if (IsDWARF64) + writeInt(OS, uint64_t(0), Endian); // Offset: 0 + else + writeInt(OS, uint32_t(0), Endian); // Offset: 0 + } else { + if (IsDWARF64) + writeInt(OS, uint64_t(0), Endian); // Offset: 0 + else + writeInt(OS, uint32_t(0), Endian); // Offset: 0 + writeInt(OS, AddrSize, Endian); + } + encodeULEB128(AbbrCode, OS); + OS << InfoData.getData().slice(AttrStart, InfoOffset); + InfoOffset = NextInfoOffset; + } + } + + AbbrSec->setReducedDebugData(std::move(ReducedAbbr)); + InfoSec->setReducedDebugData(std::move(ReducedInfo)); + if (!ArangesSec) + return; + + // Fixup references from .debug_aranges to reduced .debug_info + SmallString<0> ReducedAranges; + ReducedAranges.resize(ArangesSec->Size); + ArangesSec->writeTo(reinterpret_cast(&ReducedAranges[0])); + { + char *Aranges = &ReducedAranges[0]; + uint32_t ArangesOffset = 0; + auto endian = ELFT::TargetEndianness; + while (ArangesOffset < ReducedAranges.size()) { + uint32_t Length = endian::read32(Aranges + ArangesOffset, endian); + ArangesOffset += sizeof(uint32_t); + bool IsDWARF64 = false; + if (Length == 0xFFFFFFFF) { + IsDWARF64 = true; + Length = endian::read64(Aranges + ArangesOffset, endian); + ArangesOffset += sizeof(uint64_t); + } + uint32_t NextArangesOffset = ArangesOffset + Length; + + ArangesOffset += 2; // Skip over version (u16). + uint32_t InfoOffset = + IsDWARF64 ? endian::read64(Aranges + ArangesOffset, endian) + : endian::read32(Aranges + ArangesOffset, endian); + auto It = InfoOffMap.find(InfoOffset); + if (It != InfoOffMap.end()) { + if (IsDWARF64) + endian::write64(Aranges + ArangesOffset, It->second, endian); + else + endian::write32(Aranges + ArangesOffset, It->second, endian); + } + ArangesOffset = NextArangesOffset; + } + } + ArangesSec->setReducedDebugData(std::move(ReducedAranges)); +} + // This function generates assignments for predefined symbols (e.g. _end or // _etext) and inserts them into the commands sequence to be processed at the // appropriate time. This ensures that the value is going to be correct by the diff --git a/lld/test/ELF/Inputs/strip-debug-non-line-multi-cu-bar.s b/lld/test/ELF/Inputs/strip-debug-non-line-multi-cu-bar.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/strip-debug-non-line-multi-cu-bar.s @@ -0,0 +1,139 @@ +# Generated with: +# echo "int bar() { return 0; }" | \ +# clang++ -Os -g -S -x c - -Xclang -fdebug-compilation-dir -Xclang . -gdwarf-aranges -o - + + + .text + .file "-" + .globl bar +bar: +.Lfunc_begin0: + .file 1 "." "" + xorl %eax, %eax + retq +.Lfunc_end0: + .size bar, .Lfunc_end0-bar + + + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "clang version 9.0.0 (https://github.com/llvm/llvm-project 01a99c0aa5ae5be47ea62bd6c87ca6bb63f5a454)" +.Linfo_string1: + .asciz "-" +.Linfo_string2: + .asciz "." +.Linfo_string3: + .asciz "bar" +.Linfo_string4: + .asciz "int" + + + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 14 # DW_FORM_strp + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 27 # DW_AT_comp_dir + .byte 14 # DW_FORM_strp + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + + + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 4 # DWARF version number + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 8 # Address Size (in bytes) + .byte 1 # Abbrev [1] 0xb:0x40 DW_TAG_compile_unit + .long .Linfo_string0 # DW_AT_producer + .short 12 # DW_AT_language + .long .Linfo_string1 # DW_AT_name + .long .Lline_table_start0 # DW_AT_stmt_list + .long .Linfo_string2 # DW_AT_comp_dir + .quad .Lfunc_begin0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 2 # Abbrev [2] 0x2a:0x19 DW_TAG_subprogram + .quad .Lfunc_begin0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 87 + .long .Linfo_string3 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .long 67 # DW_AT_type + # DW_AT_external + .byte 3 # Abbrev [3] 0x43:0x7 DW_TAG_base_type + .long .Linfo_string4 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 0 # End Of Children Mark +.Ldebug_info_end0: + .text +.Lsec_end0: + + + .section .debug_aranges,"",@progbits + .long 44 # Length of ARange Set + .short 2 # DWARF Arange version number + .long .Lcu_begin0 # Offset Into Debug Info Section + .byte 8 # Address Size (in bytes) + .byte 0 # Segment Size (in bytes) + .zero 4,255 + .quad .Lfunc_begin0 + .quad .Lsec_end0-.Lfunc_begin0 + .quad 0 # ARange terminator + .quad 0 + + + .section .debug_macinfo,"",@progbits + .byte 0 # End Of Macro List Mark + + + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/lld/test/ELF/strip-debug-non-line-dwarf5.s b/lld/test/ELF/strip-debug-non-line-dwarf5.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/strip-debug-non-line-dwarf5.s @@ -0,0 +1,159 @@ +# REQUIRES: x86 +# RUN: llvm-mc -g -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t --strip-debug-non-line +# RUN: llvm-readobj --sections --symbols %t | FileCheck %s --check-prefix=CHECK-READOBJ +# RUN: llvm-dwarfdump --verify %t +# RUN: llvm-dwarfdump --debug-info %t | FileCheck %s --check-prefix=CHECK-DWARFDUMP-INFO +# RUN: llvm-dwarfdump --debug-abbrev %t | FileCheck %s --check-prefix=CHECK-DWARFDUMP-ABBREV +# RUN: llvm-dwarfdump --debug-aranges %t | FileCheck %s --check-prefix=CHECK-DWARFDUMP-ARANGES + +# CHECK-READOBJ: Name: .{{z?}}debug_str_offsets +# CHECK-READOBJ: Name: .{{z?}}debug_str +# CHECK-READOBJ: Name: .{{z?}}debug_abbrev +# CHECK-READOBJ: Name: .{{z?}}debug_info +# CHECK-READOBJ: Name: .{{z?}}debug_aranges +# CHECK-READOBJ: Name: .{{z?}}debug_addr +# CHECK-READOBJ: Name: .{{z?}}debug_line + +# CHECK-DWARFDUMP-INFO: .debug_info contents: +# CHECK-DWARFDUMP-INFO: 0x0000000c: DW_TAG_compile_unit + +# CHECK-DWARFDUMP-ABBREV: [1] DW_TAG_compile_unit DW_CHILDREN_no + +# CHECK-DWARFDUMP-ARANGES: Address Range Header: {{.*}} cu_offset = 0x00000000, + + + .text + .file "-" + .globl foo +foo: +.Lfunc_begin0: + .file 1 "" md5 0xfa8c32d051b37d67a98b03bc8cdbbf02 + xorl %eax, %eax + retq +.Lfunc_end0: + .size foo, .Lfunc_end0-foo + + .globl _start + .type _start,@function +_start: +.Lfunc_begin1: + retq +.Lfunc_end1: + .size _start, .Lfunc_end1-_start + + + .section .debug_str_offsets,"",@progbits + .long 28 + .short 5 + .short 0 +.Lstr_offsets_base0: + + + .section .debug_str,"MS",@progbits,1 +.Linfo_string4: + .asciz "int" +.Linfo_string5: + .asciz "_start" + .section .debug_str_offsets,"",@progbits + .long .Linfo_string4 + .long .Linfo_string5 + + + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 4 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + + + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 5 # DWARF version number + .byte 1 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 1 # Abbrev [1] 0xc:0x36 DW_TAG_compile_unit + .short 12 # DW_AT_language + .byte 2 # Abbrev [2] 0x23:0xf DW_TAG_subprogram + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .byte 3 # Abbrev [3] 0x32:0xb DW_TAG_subprogram + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .byte 4 # Abbrev [4] 0x3d:0x4 DW_TAG_base_type + .byte 4 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 0 # End Of Children Mark +.Ldebug_info_end0: + .text +.Lsec_end0: + + + .section .debug_aranges,"",@progbits + .long 44 # Length of ARange Set + .short 2 # DWARF Arange version number + .long .Lcu_begin0 # Offset Into Debug Info Section + .byte 8 # Address Size (in bytes) + .byte 0 # Segment Size (in bytes) + .zero 4,255 + .quad .Lfunc_begin0 + .quad .Lsec_end0-.Lfunc_begin0 + .quad 0 # ARange terminator + .quad 0 + + + .section .debug_macinfo,"",@progbits + .byte 0 # End Of Macro List Mark + + + .section .debug_addr,"",@progbits + .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution +.Ldebug_addr_start0: + .short 5 # DWARF version number + .byte 8 # Address size + .byte 0 # Segment selector size +.Laddr_table_base0: + .quad .Lfunc_begin0 + .quad .Lfunc_begin1 +.Ldebug_addr_end0: + + + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/lld/test/ELF/strip-debug-non-line-multi-cu.s b/lld/test/ELF/strip-debug-non-line-multi-cu.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/strip-debug-non-line-multi-cu.s @@ -0,0 +1,106 @@ +# REQUIRES: x86 +# RUN: llvm-mc -g -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: llvm-mc -g -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/strip-debug-non-line-multi-cu-bar.s -o %tbar.o +# RUN: ld.lld %t.o %tbar.o -o %t --strip-debug-non-line +# RUN: llvm-readobj --sections --symbols %t | FileCheck %s --check-prefix=CHECK-READOBJ +# RUN: llvm-dwarfdump --verify %t +# RUN: llvm-dwarfdump --debug-info %t | FileCheck %s --check-prefix=CHECK-DWARFDUMP-INFO +# RUN: llvm-dwarfdump --debug-abbrev %t | FileCheck %s --check-prefix=CHECK-DWARFDUMP-ABBREV +# RUN: llvm-dwarfdump --debug-aranges %t | FileCheck %s --check-prefix=CHECK-DWARFDUMP-ARANGES + +# CHECK-READOBJ: Name: .debug_str +# CHECK-READOBJ: Name: .{{z?}}debug_abbrev +# CHECK-READOBJ: Name: .{{z?}}debug_info +# CHECK-READOBJ: Name: .{{z?}}debug_aranges +# CHECK-READOBJ: Name: .{{z?}}debug_line + +# CHECK-DWARFDUMP-INFO: .debug_info contents: +# CHECK-DWARFDUMP-INFO: 0x0000000b: DW_TAG_compile_unit +# CHECK-DWARFDUMP-INFO: 0x00000019: DW_TAG_compile_unit + +# CHECK-DWARFDUMP-ABBREV: [1] DW_TAG_compile_unit DW_CHILDREN_no +# CHECK-DWARFDUMP-ABBREV: [2] DW_TAG_compile_unit DW_CHILDREN_no + +# CHECK-DWARFDUMP-ARANGES: Address Range Header: {{.*}} cu_offset = 0x00000000, +# CHECK-DWARFDUMP-ARANGES: Address Range Header: {{.*}} cu_offset = 0x0000000e, + + +# Generated with: +# echo "int bar(); void _start() { bar(); }" | \ +# clang++ -Os -g -S -x c - -Xclang -fdebug-compilation-dir -Xclang . -gdwarf-aranges -o - + + + .text + .file "-" + .globl _start +_start: +.Lfunc_begin0: + .file 1 "." "" + xorl %eax, %eax + jmp bar +.Lfunc_end0: + .size _start, .Lfunc_end0-_start + + + .section .debug_str,"MS",@progbits,1 +.Linfo_string3: + .asciz "_start" + + + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + + + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 4 # DWARF version number + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 8 # Address Size (in bytes) + .byte 1 # Abbrev [1] 0xb:0x35 DW_TAG_compile_unit + .short 12 # DW_AT_language + .byte 2 # Abbrev [2] 0x2a:0x15 DW_TAG_subprogram + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + # DW_AT_external + .byte 0 # End Of Children Mark +.Ldebug_info_end0: + .text +.Lsec_end0: + + + .section .debug_aranges,"",@progbits + .long 44 # Length of ARange Set + .short 2 # DWARF Arange version number + .long .Lcu_begin0 # Offset Into Debug Info Section + .byte 8 # Address Size (in bytes) + .byte 0 # Segment Size (in bytes) + .zero 4,255 + .quad .Lfunc_begin0 + .quad .Lsec_end0-.Lfunc_begin0 + .quad 0 # ARange terminator + .quad 0 + + + .section .debug_macinfo,"",@progbits + .byte 0 # End Of Macro List Mark + + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/lld/test/ELF/strip-debug-non-line.s b/lld/test/ELF/strip-debug-non-line.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/strip-debug-non-line.s @@ -0,0 +1,132 @@ +# REQUIRES: x86 +# RUN: llvm-mc -g -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t --strip-debug-non-line +# RUN: llvm-readobj --sections --symbols %t | FileCheck %s --check-prefix=CHECK-READOBJ +# RUN: llvm-dwarfdump --verify %t +# RUN: llvm-dwarfdump --debug-info %t | FileCheck %s --check-prefix=CHECK-DWARFDUMP-INFO +# RUN: llvm-dwarfdump --debug-abbrev %t | FileCheck %s --check-prefix=CHECK-DWARFDUMP-ABBREV +# RUN: llvm-dwarfdump --debug-aranges %t | FileCheck %s --check-prefix=CHECK-DWARFDUMP-ARANGES + +# CHECK-READOBJ: Name: .{{z?}}debug_str +# CHECK-READOBJ: Name: .{{z?}}debug_abbrev +# CHECK-READOBJ: Name: .{{z?}}debug_info +# CHECK-READOBJ: Name: .{{z?}}debug_aranges +# CHECK-READOBJ: Name: .{{z?}}debug_line + +# CHECK-DWARFDUMP-INFO: .debug_info contents: +# CHECK-DWARFDUMP-INFO: 0x0000000b: DW_TAG_compile_unit + +# CHECK-DWARFDUMP-ABBREV: [1] DW_TAG_compile_unit DW_CHILDREN_no + +# CHECK-DWARFDUMP-ARANGES: Address Range Header: {{.*}} cu_offset = 0x00000000, + + +# Generated with: +# echo "int foo() { return 0; } void _start() { foo(); }" | \ +# clang++ -Os -g -S -x c - -Xclang -fdebug-compilation-dir -Xclang . -gdwarf-aranges -o - + + .text + .globl foo +foo: +.Lfunc_begin0: + .file 1 "." "" + xorl %eax, %eax + retq +.Lfunc_end0: + .size foo, .Lfunc_end0-foo + + .globl _start +_start: + retq +.Lfunc_end1: + .size _start, .Lfunc_end1-_start + + + .section .debug_str,"MS",@progbits,1 +.Linfo_string4: + .asciz "int" + + + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 4 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + + + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 4 # DWARF version number + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 8 # Address Size (in bytes) + .byte 1 # Abbrev [1] 0xb:0x55 DW_TAG_compile_unit + .short 12 # DW_AT_language + .byte 2 # Abbrev [2] 0x2a:0x19 DW_TAG_subprogram + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .byte 3 # Abbrev [3] 0x43:0x15 DW_TAG_subprogram + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .byte 4 # Abbrev [4] 0x58:0x7 DW_TAG_base_type + .long .Linfo_string4 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 0 # End Of Children Mark +.Ldebug_info_end0: + .text +.Lsec_end0: + + + .section .debug_aranges,"",@progbits + .long 44 # Length of ARange Set + .short 2 # DWARF Arange version number + .long .Lcu_begin0 # Offset Into Debug Info Section + .byte 8 # Address Size (in bytes) + .byte 0 # Segment Size (in bytes) + .zero 4,255 + .quad .Lfunc_begin0 + .quad .Lsec_end0-.Lfunc_begin0 + .quad 0 # ARange terminator + .quad 0 + + + .section .debug_macinfo,"",@progbits + .byte 0 # End Of Macro List Mark + + + .section .debug_line,"",@progbits