diff --git a/llvm/docs/CommandGuide/llvm-dwarfutil.rst b/llvm/docs/CommandGuide/llvm-dwarfutil.rst new file mode 100644 --- /dev/null +++ b/llvm/docs/CommandGuide/llvm-dwarfutil.rst @@ -0,0 +1,111 @@ +llvm-dwarfutil - A tool to copy and manipulate debug info. +============================================== + +.. program:: llvm-dwarfutil + +SYNOPSIS +-------- + +:program:`llvm-dwarfutil` [*options*] *input* --out=*output* + +DESCRIPTION +----------- + +:program:`llvm-dwarfutil` is a tool to copy and manipulate debug info. + + +In basic usage, it makes a semantic copy of the input to the output. If any options are +specified, the output may be modified along the way, e.g. by removing unused debug info. + +If "-" is specified for the input file, the input is read from the program's standard +input stream. If "-" is specified for the output file, the output is written to +the standard output stream of the program. + +The tool is still in active development. + +COMMAND-LINE OPTIONS +---------------------------------- + +.. option:: --do-garbage-collection, --gc + + Removes pieces of debug information related to the discarded sections. + When the linker does garbage collection the abandoned debug info is left + behind. That abandoned debug info references address ranges using + tombstone value. Thus, when this option is specified, the tool removes + debug info which marked with the tombstone value. That cures + "overlapping address ranges" errors reported by :program:`llvm-dwarfdump`. + + That option is set ON by default. + +.. option:: --do-odr-deduplication, --odr + + Remove duplicated types(if "One Definition Rule" is supported + by source language). Keeps first type definition and removes other + definitions. That significantly reduces the size of output debug info. + + That option is set ON by default. + +.. option:: --help, --h + + Print a summary of command line options. + +.. option:: --num-threads=, --j + + Specifies the maximum number (n) of simultaneous threads to use for processing. + +.. option:: --out=, -o + + Specifies the name of the output file. + +.. option:: --separate-debug-file, --sep + + Generate separate file containing output debug info. Using + :program:`llvm-dwarfutil` with that option equals to the + following set of commands: + + :program:`objcopy` --only-keep-debug in-file out-file.debug + :program:`objcopy` --strip-debug in-file out-file + :program:`objcopy` --add-gnu-debuglink=out-file.debug out-file + +.. option:: --tombstone= + + might have one of the following values: + + - bfd: zero for all addresses and [1,1] for dwarf4(or less) address ranges. + + - maxpc: -1 for all addresses and -2 for dwarf4(or less) address ranges. + + - universal: both "bfd" and "maxpc". + + The value 'universal' is used by default. + +.. option:: --verbose + + Enable verbose logging. + +.. option:: --verify + + Run the DWARF verifier on the output DWARF debug info. + +.. option:: --version + + Print the version of this program. + + +SUPPORTED FORMATS +----------------- + +The following formats are currently supported by :program:`llvm-dwarfutil`: + +ELF + +EXIT STATUS +----------- + +:program:`llvm-dwarfutil` exits with a non-zero exit code if there is an error. +Otherwise, it exits with code 0. + +BUGS +---- + +To report bugs, please visit . diff --git a/llvm/include/llvm/DWARFLinker/DWARFLinker.h b/llvm/include/llvm/DWARFLinker/DWARFLinker.h --- a/llvm/include/llvm/DWARFLinker/DWARFLinker.h +++ b/llvm/include/llvm/DWARFLinker/DWARFLinker.h @@ -29,7 +29,8 @@ enum class DwarfLinkerClient { Dsymutil, LLD, General }; /// The kind of accelerator tables we should emit. -enum class AccelTableKind { +enum class DwarfLinkerAccelTableKind : uint8_t { + None, Apple, ///< .apple_names, .apple_namespaces, .apple_types, .apple_objc. Dwarf, ///< DWARF v5 .debug_names. Default, ///< Dwarf for DWARF5 or later, Apple otherwise. @@ -63,28 +64,21 @@ public: virtual ~AddressesMap(); - /// Returns true if represented addresses are from linked file. - /// Returns false if represented addresses are from not-linked - /// object file. - virtual bool areRelocationsResolved() const = 0; - /// Checks that there are valid relocations against a .debug_info /// section. virtual bool hasValidRelocs() = 0; - /// Checks that the specified DIE has a DW_AT_Location attribute - /// that references into a live code section. - /// + /// Checks that the specified variable \p DIE references live code section. + /// Allowed kind of input die: DW_TAG_variable, DW_TAG_constant. /// \returns true and sets Info.InDebugMap if it is the case. - virtual bool hasLiveMemoryLocation(const DWARFDie &DIE, - CompileUnit::DIEInfo &Info) = 0; + virtual bool isLiveVariable(const DWARFDie &DIE, + CompileUnit::DIEInfo &Info) = 0; - /// Checks that the specified DIE has a DW_AT_Low_pc attribute - /// that references into a live code section. - /// + /// Checks that the specified subprogram \p DIE references live code section. + /// Allowed kind of input die: DW_TAG_subprogram, DW_TAG_label. /// \returns true and sets Info.InDebugMap if it is the case. - virtual bool hasLiveAddressRange(const DWARFDie &DIE, - CompileUnit::DIEInfo &Info) = 0; + virtual bool isLiveSubprogram(const DWARFDie &DIE, + CompileUnit::DIEInfo &Info) = 0; /// Apply the valid relocations to the buffer \p Data, taking into /// account that Data is at \p BaseOffset in the .debug_info section. @@ -300,7 +294,7 @@ void setNumThreads(unsigned NumThreads) { Options.Threads = NumThreads; } /// Set kind of accelerator tables to be generated. - void setAccelTableKind(AccelTableKind Kind) { + void setAccelTableKind(DwarfLinkerAccelTableKind Kind) { Options.TheAccelTableKind = Kind; } @@ -371,6 +365,8 @@ /// Given a DIE, update its incompleteness based on whether the DIEs it /// references are incomplete. UpdateRefIncompleteness, + /// Given a DIE, mark it as ODR Canonical if applicable. + MarkODRCanonicalDie, }; /// This class represents an item in the work list. The type defines what kind @@ -470,6 +466,10 @@ const DWARFFile &File, SmallVectorImpl &Worklist); + /// Mark context corresponding to the specified \p Die as having canonical + /// die, if applicable. + void markODRCanonicalDie(const DWARFDie &Die, CompileUnit &CU); + /// \defgroup FindRootDIEs Find DIEs corresponding to Address map entries. /// /// @{ @@ -811,7 +811,8 @@ unsigned Threads = 1; /// The accelerator table kind - AccelTableKind TheAccelTableKind = AccelTableKind::Default; + DwarfLinkerAccelTableKind TheAccelTableKind = + DwarfLinkerAccelTableKind::Default; /// Prepend path for the clang modules. std::string PrependPath; diff --git a/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h b/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h --- a/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h +++ b/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h @@ -74,6 +74,12 @@ /// Does DIE transitively refer an incomplete decl? bool Incomplete : 1; + + /// Is DIE in the clang module scope? + bool InModuleScope : 1; + + /// Is ODR marking done? + bool ODRMarkingDone : 1; }; CompileUnit(DWARFUnit &OrigUnit, unsigned ID, bool CanUseODR, diff --git a/llvm/include/llvm/DWARFLinker/DWARFLinkerDeclContext.h b/llvm/include/llvm/DWARFLinker/DWARFLinkerDeclContext.h --- a/llvm/include/llvm/DWARFLinker/DWARFLinkerDeclContext.h +++ b/llvm/include/llvm/DWARFLinker/DWARFLinkerDeclContext.h @@ -91,6 +91,10 @@ bool setLastSeenDIE(CompileUnit &U, const DWARFDie &Die); + void setHasCanonicalDIE() { HasCanonicalDIE = true; } + + bool hasCanonicalDIE() const { return HasCanonicalDIE; } + uint32_t getCanonicalDIEOffset() const { return CanonicalDIEOffset; } void setCanonicalDIEOffset(uint32_t Offset) { CanonicalDIEOffset = Offset; } @@ -113,6 +117,7 @@ DWARFDie LastSeenDIE; uint32_t LastSeenCompileUnitID = 0; uint32_t CanonicalDIEOffset = 0; + bool HasCanonicalDIE = false; }; /// This class gives a tree-like API to the DenseMap that stores the diff --git a/llvm/lib/DWARFLinker/DWARFLinker.cpp b/llvm/lib/DWARFLinker/DWARFLinker.cpp --- a/llvm/lib/DWARFLinker/DWARFLinker.cpp +++ b/llvm/lib/DWARFLinker/DWARFLinker.cpp @@ -275,8 +275,7 @@ InImportedModule(InImportedModule) {} }; -static bool updatePruning(const DWARFDie &Die, CompileUnit &CU, - uint64_t ModulesEndOffset) { +static bool updatePruning(const DWARFDie &Die, CompileUnit &CU) { CompileUnit::DIEInfo &Info = CU.getInfo(Die); // Prune this DIE if it is either a forward declaration inside a @@ -288,11 +287,7 @@ // Only prune forward declarations inside a DW_TAG_module for which a // definition exists elsewhere. - if (ModulesEndOffset == 0) - Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset(); - else - Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset() > 0 && - Info.Ctxt->getCanonicalDIEOffset() <= ModulesEndOffset; + Info.Prune &= Info.Ctxt && Info.Ctxt->hasCanonicalDIE(); return Info.Prune; } @@ -314,7 +309,7 @@ static bool analyzeContextInfo( const DWARFDie &DIE, unsigned ParentIdx, CompileUnit &CU, DeclContext *CurrentDeclContext, DeclContextTree &Contexts, - uint64_t ModulesEndOffset, swiftInterfacesMap *ParseableSwiftInterfaces, + swiftInterfacesMap *ParseableSwiftInterfaces, std::function ReportWarning, bool InImportedModule = false) { // LIFO work list. @@ -327,7 +322,7 @@ switch (Current.Type) { case ContextWorklistItemType::UpdatePruning: - updatePruning(Current.Die, CU, ModulesEndOffset); + updatePruning(Current.Die, CU); continue; case ContextWorklistItemType::UpdateChildPruning: updateChildPruning(Current.Die, CU, *Current.OtherInfo); @@ -361,16 +356,16 @@ } Info.ParentIdx = Current.ParentIdx; - bool InClangModule = CU.isClangModule() || Current.InImportedModule; - if (CU.hasODR() || InClangModule) { + Info.InModuleScope = CU.isClangModule() || Current.InImportedModule; + if (CU.hasODR() || Info.InModuleScope) { if (Current.Context) { auto PtrInvalidPair = Contexts.getChildDeclContext( - *Current.Context, Current.Die, CU, InClangModule); + *Current.Context, Current.Die, CU, Info.InModuleScope); Current.Context = PtrInvalidPair.getPointer(); Info.Ctxt = PtrInvalidPair.getInt() ? nullptr : PtrInvalidPair.getPointer(); if (Info.Ctxt) - Info.Ctxt->setDefinedInClangModule(InClangModule); + Info.Ctxt->setDefinedInClangModule(Info.InModuleScope); } else Info.Ctxt = Current.Context = nullptr; } @@ -440,10 +435,9 @@ // if the variable has a valid relocation, so that the DIEInfo is filled. // However, we don't want a static variable in a function to force us to keep // the enclosing function, unless requested explicitly. - const bool HasLiveMemoryLocation = - RelocMgr.hasLiveMemoryLocation(DIE, MyInfo); - if (!HasLiveMemoryLocation || ((Flags & TF_InFunctionScope) && - !LLVM_UNLIKELY(Options.KeepFunctionForStatic))) + const bool HasLiveVariable = RelocMgr.isLiveVariable(DIE, MyInfo); + if (!HasLiveVariable || ((Flags & TF_InFunctionScope) && + !LLVM_UNLIKELY(Options.KeepFunctionForStatic))) return Flags; if (Options.Verbose) { @@ -470,7 +464,7 @@ return Flags; assert(LowPc.hasValue() && "low_pc attribute is not an address."); - if (!RelocMgr.hasLiveAddressRange(DIE, MyInfo)) + if (!RelocMgr.isLiveSubprogram(DIE, MyInfo)) return Flags; if (Options.Verbose) { @@ -617,6 +611,27 @@ } } +static bool isODRCanonicalCandidate(const DWARFDie &Die, CompileUnit &CU) { + CompileUnit::DIEInfo &Info = CU.getInfo(Die); + + if (!Info.Ctxt || (Die.getTag() == dwarf::DW_TAG_namespace)) + return false; + + if (!CU.hasODR() && !Info.InModuleScope) + return false; + + return !Info.Incomplete && Info.Ctxt != CU.getInfo(Info.ParentIdx).Ctxt; +} + +void DWARFLinker::markODRCanonicalDie(const DWARFDie &Die, CompileUnit &CU) { + CompileUnit::DIEInfo &Info = CU.getInfo(Die); + + Info.ODRMarkingDone = true; + if (Info.Keep && isODRCanonicalCandidate(Die, CU) && + !Info.Ctxt->hasCanonicalDIE()) + Info.Ctxt->setHasCanonicalDIE(); +} + /// Look at DIEs referenced by the given DIE and decide whether they should be /// kept. All DIEs referenced though attributes should be kept. void DWARFLinker::lookForRefDIEsToKeep( @@ -646,8 +661,6 @@ if (auto RefDie = resolveDIEReference(File, Units, Val, Die, ReferencedCU)) { CompileUnit::DIEInfo &Info = ReferencedCU->getInfo(RefDie); - bool IsModuleRef = Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset() && - Info.Ctxt->isDefinedInClangModule(); // If the referenced DIE has a DeclContext that has already been // emitted, then do not keep the one in this CU. We'll link to // the canonical DIE in cloneDieReferenceAttribute. @@ -658,15 +671,14 @@ // // FIXME: compatibility with dsymutil-classic. There is no // reason not to unique ref_addr references. - if (AttrSpec.Form != dwarf::DW_FORM_ref_addr && (UseOdr || IsModuleRef) && - Info.Ctxt && - Info.Ctxt != ReferencedCU->getInfo(Info.ParentIdx).Ctxt && - Info.Ctxt->getCanonicalDIEOffset() && isODRAttribute(AttrSpec.Attr)) + if (AttrSpec.Form != dwarf::DW_FORM_ref_addr && + isODRAttribute(AttrSpec.Attr) && Info.Ctxt && + Info.Ctxt->hasCanonicalDIE()) continue; // Keep a module forward declaration if there is no definition. if (!(isODRAttribute(AttrSpec.Attr) && Info.Ctxt && - Info.Ctxt->getCanonicalDIEOffset())) + Info.Ctxt->hasCanonicalDIE())) Info.Prune = false; ReferencedDIEs.emplace_back(RefDie, *ReferencedCU); } @@ -757,6 +769,9 @@ lookForParentDIEsToKeep(Current.AncestorIdx, Current.CU, Current.Flags, Worklist); continue; + case WorklistItemType::MarkODRCanonicalDie: + markODRCanonicalDie(Current.Die, Current.CU); + continue; case WorklistItemType::LookForDIEsToKeep: break; } @@ -779,6 +794,16 @@ Current.Flags = shouldKeepDIE(AddressesMap, Ranges, Current.Die, File, Current.CU, MyInfo, Current.Flags); + // We need to mark context for the canonical die in the end of normal + // traversing(not TF_DependencyWalk) or after normal traversing if die + // was not marked as kept. + if (!(Current.Flags & TF_DependencyWalk) || + (MyInfo.ODRMarkingDone && !MyInfo.Keep)) { + if (Current.CU.hasODR() || MyInfo.InModuleScope) + Worklist.emplace_back(Current.Die, Current.CU, + WorklistItemType::MarkODRCanonicalDie); + } + // Finish by looking for child DIEs. Because of the LIFO worklist we need // to schedule that work before any subsequent items are added to the // worklist. @@ -876,7 +901,6 @@ DIE *NewRefDie = nullptr; CompileUnit *RefUnit = nullptr; - DeclContext *Ctxt = nullptr; DWARFDie RefDie = Linker.resolveDIEReference(File, CompileUnits, Val, InputDIE, RefUnit); @@ -889,14 +913,14 @@ // If we already have emitted an equivalent DeclContext, just point // at it. - if (isODRAttribute(AttrSpec.Attr)) { - Ctxt = RefInfo.Ctxt; - if (Ctxt && Ctxt->getCanonicalDIEOffset()) { - DIEInteger Attr(Ctxt->getCanonicalDIEOffset()); - Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), - dwarf::DW_FORM_ref_addr, Attr); - return U.getRefAddrByteSize(); - } + if (isODRAttribute(AttrSpec.Attr) && RefInfo.Ctxt && + RefInfo.Ctxt->getCanonicalDIEOffset()) { + assert(RefInfo.Ctxt->hasCanonicalDIE() && + "Offset to canonical die is set, but context is not marked"); + DIEInteger Attr(RefInfo.Ctxt->getCanonicalDIEOffset()); + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::DW_FORM_ref_addr, Attr); + return U.getRefAddrByteSize(); } if (!RefInfo.Clone) { @@ -926,7 +950,7 @@ // A forward reference. Note and fixup later. Attr = 0xBADDEF; Unit.noteForwardReference( - NewRefDie, RefUnit, Ctxt, + NewRefDie, RefUnit, RefInfo.Ctxt, Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_ref_addr, DIEInteger(Attr))); } @@ -1357,10 +1381,11 @@ assert(Die->getTag() == InputDIE.getTag()); Die->setOffset(OutOffset); - if ((Unit.hasODR() || Unit.isClangModule()) && !Info.Incomplete && - Die->getTag() != dwarf::DW_TAG_namespace && Info.Ctxt && - Info.Ctxt != Unit.getInfo(Info.ParentIdx).Ctxt && - !Info.Ctxt->getCanonicalDIEOffset()) { + if (isODRCanonicalCandidate(InputDIE, Unit) && Info.Ctxt && + (Info.Ctxt->getCanonicalDIEOffset() == 0)) { + if (!Info.Ctxt->hasCanonicalDIE()) + Info.Ctxt->setHasCanonicalDIE(); + // We are about to emit a DIE that is the root of its own valid // DeclContext tree. Make the current offset the canonical offset // for this context. @@ -1385,8 +1410,7 @@ DWARFDataExtractor(DIECopy, Data.isLittleEndian(), Data.getAddressSize()); // Modify the copy with relocated addresses. - if (ObjFile.Addresses->areRelocationsResolved() && - ObjFile.Addresses->applyValidRelocs(DIECopy, Offset, + if (ObjFile.Addresses->applyValidRelocs(DIECopy, Offset, Data.isLittleEndian())) { // If we applied relocations, we store the value of high_pc that was // potentially stored in the input DIE. If high_pc is an address @@ -1789,16 +1813,19 @@ void DWARFLinker::emitAcceleratorEntriesForUnit(CompileUnit &Unit) { switch (Options.TheAccelTableKind) { - case AccelTableKind::Apple: + case DwarfLinkerAccelTableKind::None: + // nothing to do. + break; + case DwarfLinkerAccelTableKind::Apple: emitAppleAcceleratorEntriesForUnit(Unit); break; - case AccelTableKind::Dwarf: + case DwarfLinkerAccelTableKind::Dwarf: emitDwarfAcceleratorEntriesForUnit(Unit); break; - case AccelTableKind::Pub: + case DwarfLinkerAccelTableKind::Pub: emitPubAcceleratorEntriesForUnit(Unit); break; - case AccelTableKind::Default: + case DwarfLinkerAccelTableKind::Default: llvm_unreachable("The default must be updated to a concrete value."); break; } @@ -2110,7 +2137,7 @@ Unit = std::make_unique(*CU, UnitID++, !Options.NoODR, ModuleName); analyzeContextInfo(CUDie, 0, *Unit, &ODRContexts.getRoot(), ODRContexts, - ModulesEndOffset, Options.ParseableSwiftInterfaces, + Options.ParseableSwiftInterfaces, [&](const Twine &Warning, const DWARFDie &DIE) { reportWarning(Warning, File, &DIE); }); @@ -2217,7 +2244,7 @@ } void DWARFLinker::updateAccelKind(DWARFContext &Dwarf) { - if (Options.TheAccelTableKind != AccelTableKind::Default) + if (Options.TheAccelTableKind != DwarfLinkerAccelTableKind::Default) return; auto &DwarfObj = Dwarf.getDWARFObj(); @@ -2343,11 +2370,11 @@ // would affect the decision. However, as they're built with the same // compiler and flags, it is safe to assume that they will follow the // decision made here. - if (Options.TheAccelTableKind == AccelTableKind::Default) { + if (Options.TheAccelTableKind == DwarfLinkerAccelTableKind::Default) { if (AtLeastOneDwarfAccelTable && !AtLeastOneAppleAccelTable) - Options.TheAccelTableKind = AccelTableKind::Dwarf; + Options.TheAccelTableKind = DwarfLinkerAccelTableKind::Dwarf; else - Options.TheAccelTableKind = AccelTableKind::Apple; + Options.TheAccelTableKind = DwarfLinkerAccelTableKind::Apple; } for (LinkContext &OptContext : ObjectContexts) { @@ -2458,7 +2485,7 @@ continue; analyzeContextInfo(CurrentUnit->getOrigUnit().getUnitDIE(), 0, *CurrentUnit, &ODRContexts.getRoot(), ODRContexts, - ModulesEndOffset, Options.ParseableSwiftInterfaces, + Options.ParseableSwiftInterfaces, [&](const Twine &Warning, const DWARFDie &DIE) { reportWarning(Warning, Context.File, &DIE); }); @@ -2526,19 +2553,22 @@ TheDwarfEmitter->emitAbbrevs(Abbreviations, MaxDwarfVersion); TheDwarfEmitter->emitStrings(OffsetsStringPool); switch (Options.TheAccelTableKind) { - case AccelTableKind::Apple: + case DwarfLinkerAccelTableKind::None: + // nothing to do. + break; + case DwarfLinkerAccelTableKind::Apple: TheDwarfEmitter->emitAppleNames(AppleNames); TheDwarfEmitter->emitAppleNamespaces(AppleNamespaces); TheDwarfEmitter->emitAppleTypes(AppleTypes); TheDwarfEmitter->emitAppleObjc(AppleObjc); break; - case AccelTableKind::Dwarf: + case DwarfLinkerAccelTableKind::Dwarf: TheDwarfEmitter->emitDebugNames(DebugNames); break; - case AccelTableKind::Pub: + case DwarfLinkerAccelTableKind::Pub: // Already emitted by emitPubAcceleratorEntriesForUnit. break; - case AccelTableKind::Default: + case DwarfLinkerAccelTableKind::Default: llvm_unreachable("Default should have already been resolved."); break; } diff --git a/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp b/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp --- a/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp +++ b/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp @@ -90,9 +90,12 @@ PatchLocation Attr; DeclContext *Ctxt; std::tie(RefDie, RefUnit, Ctxt, Attr) = Ref; - if (Ctxt && Ctxt->getCanonicalDIEOffset()) + if (Ctxt && Ctxt->hasCanonicalDIE()) { + assert(Ctxt->getCanonicalDIEOffset() && + "Canonical die offset is not set"); + Attr.set(Ctxt->getCanonicalDIEOffset()); - else + } else Attr.set(RefDie->getOffset() + RefUnit->getStartOffset()); } } diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt --- a/llvm/test/CMakeLists.txt +++ b/llvm/test/CMakeLists.txt @@ -80,6 +80,7 @@ llvm-dlltool dsymutil llvm-dwarfdump + llvm-dwarfutil llvm-dwp llvm-exegesis llvm-extract diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py --- a/llvm/test/lit.cfg.py +++ b/llvm/test/lit.cfg.py @@ -159,8 +159,8 @@ 'dsymutil', 'lli', 'lli-child-target', 'llvm-ar', 'llvm-as', 'llvm-addr2line', 'llvm-bcanalyzer', 'llvm-bitcode-strip', 'llvm-config', 'llvm-cov', 'llvm-cxxdump', 'llvm-cvtres', 'llvm-debuginfod-find', - 'llvm-diff', 'llvm-dis', 'llvm-dwarfdump', 'llvm-dlltool', 'llvm-exegesis', - 'llvm-extract', 'llvm-isel-fuzzer', 'llvm-ifs', + 'llvm-diff', 'llvm-dis', 'llvm-dwarfdump', 'llvm-dwarfutil', 'llvm-dlltool', + 'llvm-exegesis', 'llvm-extract', 'llvm-isel-fuzzer', 'llvm-ifs', 'llvm-install-name-tool', 'llvm-jitlink', 'llvm-opt-fuzzer', 'llvm-lib', 'llvm-link', 'llvm-lto', 'llvm-lto2', 'llvm-mc', 'llvm-mca', 'llvm-modextract', 'llvm-nm', 'llvm-objcopy', 'llvm-objdump', 'llvm-otool', diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/copy.test b/llvm/test/tools/llvm-dwarfutil/ELF/copy.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/copy.test @@ -0,0 +1,165 @@ +# RUN: yaml2obj %s -o %t.o + +# RUN: llvm-dwarfutil %t.o -o %t1 +# RUN: llvm-dwarfdump -a %t1 | FileCheck -check-prefix=CHECK-DEBUG %s + +# RUN: llvm-dwarfutil %t.o --sep -o %t1 +# RUN: llvm-objdump --headers %t1 | FileCheck -check-prefix=CHECK-NON-DEBUG %s +# RUN: llvm-dwarfdump -a %t1.debug | FileCheck -check-prefix=CHECK-DEBUG %s + +# RUN: llvm-dwarfutil %t.o --separate-debug-file -o %t1 +# RUN: llvm-objdump --headers %t1 | FileCheck -check-prefix=CHECK-NON-DEBUG %s +# RUN: llvm-dwarfdump -a %t1.debug | FileCheck -check-prefix=CHECK-DEBUG %s + +# RUN: llvm-dwarfutil --gc=0 %t.o -o %t1 +# RUN: llvm-dwarfdump -a %t1 | FileCheck -check-prefix=CHECK-DEBUG %s + +# RUN: llvm-dwarfutil --gc=0 %t.o --sep -o %t1 +# RUN: llvm-objdump --headers %t1 | FileCheck -check-prefix=CHECK-NON-DEBUG %s +# RUN: llvm-dwarfdump -a %t1.debug | FileCheck -check-prefix=CHECK-DEBUG %s + +# RUN: llvm-dwarfutil --gc=0 %t.o --separate-debug-file -o %t1 +# RUN: llvm-objdump --headers %t1 | FileCheck -check-prefix=CHECK-NON-DEBUG %s +# RUN: llvm-dwarfdump -a %t1.debug | FileCheck -check-prefix=CHECK-DEBUG %s + + +## This test checks that debug info containing in source file +## is fully copied in the destination file. + +# CHECK-NON-DEBUG-NOT: .debug_abbrev +# CHECK-NON-DEBUG-NOT: .debug_info +# CHECK-NON-DEBUG: .gnu_debuglink +# CHECK-NON-DEBUG-NOT: .debug_abbrev +# CHECK-NON-DEBUG-NOT: .debug_info + + +# CHECK-DEBUG: .debug_abbrev +# CHECK-DEBUG: DW_TAG_compile_unit +# CHECK-DEBUG: .debug_info +# CHECK-DEBUG: DW_TAG_compile_unit +# CHECK-DEBUG: DW_AT_producer{{.*}}"by_hand" +# CHECK-DEBUG: DW_AT_language{{.*}}DW_LANG_C_plus_plus +# CHECK-DEBUG: DW_AT_name{{.*}}"CU1" +# CHECK-DEBUG: DW_TAG_class_type +# CHECK-DEBUG: DW_AT_name{{.*}}"class1" +# CHECK-DEBUG: DW_TAG_base_type +# CHECK-DEBUG: DW_AT_name{{.*}}"int" +# CHECK-DEBUG: DW_AT_name{{.*}}"char" +# CHECK-DEBUG: DW_AT_name{{.*}}"float" +# CHECK-DEBUG: DW_TAG_pointer_type +# CHECK-DEBUG: DW_TAG_variable +# CHECK-DEBUG: DW_AT_name{{.*}}"var1" + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_string + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_member + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_template_type_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_pointer_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_const_value + Form: DW_FORM_data4 + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU1 + - Value: 0x1000 + - Value: 0x100 + - AbbrCode: 2 + Values: + - CStr: class1 + - AbbrCode: 3 + Values: + - Value: 0x00000052 + - CStr: member1 + - AbbrCode: 3 + Values: + - Value: 0x00000058 + - CStr: member2 + - AbbrCode: 0 + - AbbrCode: 7 + Values: + - CStr: int + - AbbrCode: 7 + Values: + - CStr: char + - AbbrCode: 7 + Values: + - CStr: float + - AbbrCode: 8 + Values: + - Value: 0x0000002a + - AbbrCode: 9 + Values: + - CStr: var1 + - Value: 0x00000000 + - Value: 0x0000005f + - AbbrCode: 0 +... diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/gc-bfd.test b/llvm/test/tools/llvm-dwarfutil/ELF/gc-bfd.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/gc-bfd.test @@ -0,0 +1,144 @@ +# RUN: yaml2obj %s -o %t.o + +# RUN: llvm-dwarfutil --tombstone=bfd %t.o -o - | llvm-dwarfdump -a - | FileCheck %s +# RUN: llvm-dwarfutil --tombstone=bfd --gc %t.o -o - | llvm-dwarfdump -a - | FileCheck %s +# RUN: llvm-dwarfutil --tombstone=universal --gc %t.o -o - | llvm-dwarfdump -a - | FileCheck %s + +## This test checks that debug info related to deleted code is removed. + +# CHECK: DW_TAG_compile_unit +# CHECK: DW_AT_name{{.*}}"CU1" +# CHECK: DW_TAG_class_type +# CHECK: DW_AT_name{{.*}}"class1" +# CHECK-NOT: DW_TAG_class_type +# CHECK-NOT: "class2" +# CHECK-NOT: "class3" +# CHECK: DW_TAG_subprogram +# CHECK: DW_AT_name{{.*}}"foo1" +# CHECK: DW_AT_low_pc{{.*}}0x0000000000001000 +# CHECK: DW_AT_high_pc{{.*}}0x0000000000001010 +# CHECK: DW_AT_type{{.*}}"class1" +# CHECK-NOT: DW_TAG_subprogram +# CHECK-NOT: "foo2" + + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_string + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_member + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_template_type_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU1 + - Value: 0x1000 + - Value: 0x100 + - AbbrCode: 3 + Values: + - CStr: class1 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class2 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class3 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 8 + Values: + - CStr: int + - AbbrCode: 2 + Values: + - CStr: foo1 + - Value: 0x1000 + - Value: 0x10 + - Value: 0x0000002a + - AbbrCode: 2 + Values: + - CStr: foo2 + - Value: 0x0 + - Value: 0x100 + - Value: 0x00000040 + - AbbrCode: 0 +... diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/gc-default.test b/llvm/test/tools/llvm-dwarfutil/ELF/gc-default.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/gc-default.test @@ -0,0 +1,143 @@ +# RUN: yaml2obj %s -o %t.o + +# RUN: llvm-dwarfutil %t.o -o - | llvm-dwarfdump -a - | FileCheck %s +# RUN: llvm-dwarfutil --gc %t.o -o - | llvm-dwarfdump -a - | FileCheck %s + +## This test checks that debug info related to deleted code is removed. + +# CHECK: DW_TAG_compile_unit +# CHECK: DW_AT_name{{.*}}"CU1" +# CHECK: DW_TAG_class_type +# CHECK: DW_AT_name{{.*}}"class1" +# CHECK-NOT: DW_TAG_class_type +# CHECK-NOT: "class2" +# CHECK-NOT: "class3" +# CHECK: DW_TAG_subprogram +# CHECK: DW_AT_name{{.*}}"foo1" +# CHECK: DW_AT_low_pc{{.*}}0x0000000000001000 +# CHECK: DW_AT_high_pc{{.*}}0x0000000000001010 +# CHECK: DW_AT_type{{.*}}"class1" +# CHECK-NOT: DW_TAG_subprogram +# CHECK-NOT: "foo2" + + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_string + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_member + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_template_type_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU1 + - Value: 0x1000 + - Value: 0x100 + - AbbrCode: 3 + Values: + - CStr: class1 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class2 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class3 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 8 + Values: + - CStr: int + - AbbrCode: 2 + Values: + - CStr: foo1 + - Value: 0x1000 + - Value: 0x10 + - Value: 0x0000002a + - AbbrCode: 2 + Values: + - CStr: foo2 + - Value: 0x0 + - Value: 0x100 + - Value: 0x00000040 + - AbbrCode: 0 +... diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/gc-maxpc.test b/llvm/test/tools/llvm-dwarfutil/ELF/gc-maxpc.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/gc-maxpc.test @@ -0,0 +1,144 @@ +# RUN: yaml2obj %s -o %t.o + +# RUN: llvm-dwarfutil --tombstone=maxpc %t.o -o - | llvm-dwarfdump -a - | FileCheck %s +# RUN: llvm-dwarfutil --tombstone=maxpc --gc %t.o -o - | llvm-dwarfdump -a - | FileCheck %s +# RUN: llvm-dwarfutil --tombstone=universal --gc %t.o -o - | llvm-dwarfdump -a - | FileCheck %s + +## This test checks that debug info related to deleted code is removed. + +# CHECK: DW_TAG_compile_unit +# CHECK: DW_AT_name{{.*}}"CU1" +# CHECK: DW_TAG_class_type +# CHECK: DW_AT_name{{.*}}"class1" +# CHECK-NOT: DW_TAG_class_type +# CHECK-NOT: "class2" +# CHECK-NOT: "class3" +# CHECK: DW_TAG_subprogram +# CHECK: DW_AT_name{{.*}}"foo1" +# CHECK: DW_AT_low_pc{{.*}}0x0000000000001000 +# CHECK: DW_AT_high_pc{{.*}}0x0000000000001010 +# CHECK: DW_AT_type{{.*}}"class1" +# CHECK-NOT: DW_TAG_subprogram +# CHECK-NOT: "foo2" + + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_string + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_member + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_template_type_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU1 + - Value: 0x1000 + - Value: 0x100 + - AbbrCode: 3 + Values: + - CStr: class1 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class2 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class3 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 8 + Values: + - CStr: int + - AbbrCode: 2 + Values: + - CStr: foo1 + - Value: 0x1000 + - Value: 0x10 + - Value: 0x0000002a + - AbbrCode: 2 + Values: + - CStr: foo2 + - Value: 0xFFFFFFFFFFFFFFFF + - Value: 0x100 + - Value: 0x00000040 + - AbbrCode: 0 +... diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/odr-class.test b/llvm/test/tools/llvm-dwarfutil/ELF/odr-class.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/odr-class.test @@ -0,0 +1,284 @@ +# RUN: yaml2obj %s -o %t.o +# RUN: llvm-dwarfutil %t.o -o - | llvm-dwarfdump -a - | FileCheck %s + +## This test checks debug info for the case when one compilation unit +## contains definition of the type and another compilation unit +## contains definition of the same type. The result should contain original +## definition in CU1 and removed definition from the CU2. + +## CU1: +## +## class class1 { +## char member1; +## float member2; +## }; +## +## class1 *var1; +## +## CU2: +## +## class class1 { +## char member1; +## float member2; +## }; +## +## class1 *var1; + +# CHECK: file format elf64-x86-64 +# CHECK: .debug_info contents: + +# CHECK: DW_TAG_compile_unit +# CHECK: DW_AT_name{{.*}}"CU1" + +# CHECK: 0x[[CU1_CLASS1:[0-9a-f]*]]: DW_TAG_class_type{{.*[[:space:]].*}}DW_AT_name{{.*}}"class1" +# CHECK: DW_TAG_member +# CHECK: DW_AT_type{{.*}}0x00000000[[CU1_CHAR:[0-9a-f]*]] "char" +# CHECK: DW_AT_name{{.*}}"member1" + +# CHECK: DW_TAG_member +# CHECK: DW_AT_type{{.*}}0x00000000[[CU1_FLOAT:[0-9a-f]*]] "float" +# CHECK: DW_AT_name{{.*}}"member2" + +# CHECK: 0x[[CU1_INT:[0-9a-f]*]]: DW_TAG_base_type +# CHECK: DW_AT_name{{.*}}"int" + +# CHECK: 0x[[CU1_CHAR]]: DW_TAG_base_type +# CHECK: DW_AT_name{{.*}}"char" + +# CHECK: 0x[[CU1_FLOAT]]: DW_TAG_base_type +# CHECK: DW_AT_name{{.*}}"float" + +# CHECK: 0x[[CU1_PTR_CLASS1:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU1_CLASS1]] "class1" + +# CHECK: DW_TAG_variable +# CHECK: DW_AT_name{{.*}}"var1" +# CHECK: DW_AT_type{{.*}}0x00000000[[CU1_PTR_CLASS1]] "class1 *" + +# CHECK: DW_TAG_compile_unit +# CHECK: DW_AT_name{{.*}}"CU2" + +# CHECK-NOT: DW_TAG_class_type +# CHECK-NOT: "class1" + +# CHECK: 0x[[CU2_PTR_CLASS1:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU1_CLASS1]] "class1" + +# CHECK: DW_TAG_variable +# CHECK: DW_AT_name{{.*}}"var1" +# CHECK: DW_AT_type{{.*}}0x00000000[[CU2_PTR_CLASS1]] "class1 *" + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_string + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_member + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_template_type_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_pointer_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_const_value + Form: DW_FORM_data4 + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Table: + - Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_string + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_member + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_template_type_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_pointer_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_const_value + Form: DW_FORM_data4 + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU1 + - Value: 0x1000 + - Value: 0x100 + - AbbrCode: 2 + Values: + - CStr: class1 + - AbbrCode: 3 + Values: + - Value: 0x00000052 + - CStr: member1 + - AbbrCode: 3 + Values: + - Value: 0x00000058 + - CStr: member2 + - AbbrCode: 0 + - AbbrCode: 7 + Values: + - CStr: int + - AbbrCode: 7 + Values: + - CStr: char + - AbbrCode: 7 + Values: + - CStr: float + - AbbrCode: 8 + Values: + - Value: 0x0000002a + - AbbrCode: 9 + Values: + - CStr: var1 + - Value: 0x00000000 + - Value: 0x0000005f + - AbbrCode: 0 + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU2 + - Value: 0x2000 + - Value: 0x100 + - AbbrCode: 2 + Values: + - CStr: class1 + - AbbrCode: 3 + Values: + - Value: 0x00000052 + - CStr: member1 + - AbbrCode: 3 + Values: + - Value: 0x00000058 + - CStr: member2 + - AbbrCode: 0 + - AbbrCode: 7 + Values: + - CStr: int + - AbbrCode: 7 + Values: + - CStr: char + - AbbrCode: 7 + Values: + - CStr: float + - AbbrCode: 8 + Values: + - Value: 0x0000002a + - AbbrCode: 9 + Values: + - CStr: var1 + - Value: 0x00000000 + - Value: 0x0000005f + - AbbrCode: 0 +... diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/odr-fwd-declaration.test b/llvm/test/tools/llvm-dwarfutil/ELF/odr-fwd-declaration.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/odr-fwd-declaration.test @@ -0,0 +1,330 @@ +# RUN: yaml2obj %s -o %t.o +# RUN: llvm-dwarfutil %t.o -o - | llvm-dwarfdump -a - | FileCheck %s + +## This test checks debug info for the case when one compilation unit +## contains forward declaration of the type and another compilation unit +## contains definition for that type. The result should contain original +## definition and declaration without modifications. + +## CU1: +## +## class class1; +## template class class2; +## +## class1 *var1; +## class2 *var2; +## +## CU2: +## +## class class1 { +## char member1; +## float member2; +## }; +## +## template class class2 { +## char member1; +## }; +## +## class1 *var1; +## class2 *var2; + +# CHECK: file format elf64-x86-64 +# CHECK: .debug_info contents: + +# CHECK: DW_TAG_compile_unit +# CHECK: DW_AT_name{{.*}}"CU1" + +# CHECK: 0x[[CU1_CLASS1:[0-9a-f]*]]: DW_TAG_class_type{{.*[[:space:]].*}}DW_AT_name{{.*}}"class1" +# CHECK: DW_AT_declaration (true) + +# CHECK: 0x[[CU1_CLASS2:[0-9a-f]*]]: DW_TAG_class_type{{.*[[:space:]].*}}DW_AT_name{{.*}}"class2" +# CHECK: DW_AT_declaration (true) +# CHECK: DW_TAG_template_type_parameter{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU1_INT:[0-9a-f]*]] "int" + +# CHECK: 0x[[CU1_INT]]: DW_TAG_base_type +# CHECK: DW_AT_name{{.*}}"int" + +# CHECK: 0x[[CU1_PTR_CLASS1:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU1_CLASS1]] "class1" + +# CHECK: 0x[[CU1_PTR_CLASS2:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU1_CLASS2]] "class2" + +# CHECK: DW_TAG_variable +# CHECK: DW_AT_name{{.*}}"var1" +# CHECK: DW_AT_type{{.*}}0x00000000[[CU1_PTR_CLASS1]] "class1 *" +# CHECK: DW_TAG_variable +# CHECK: DW_AT_name{{.*}}"var2" +# CHECK: DW_AT_type{{.*}}0x00000000[[CU1_PTR_CLASS2]] "class2 *" + +# CHECK: DW_TAG_compile_unit +# CHECK: DW_AT_name{{.*}}"CU2" + +# CHECK: 0x[[CU2_CLASS1:[0-9a-f]*]]: DW_TAG_class_type{{.*[[:space:]].*}}DW_AT_name{{.*}}"class1" +# CHECK: DW_TAG_member +# CHECK: DW_AT_type{{.*}}0x00000000[[CU2_CHAR:[0-9a-f]*]] "char" +# CHECK: DW_AT_name{{.*}}"member1" + +# CHECK: DW_TAG_member +# CHECK: DW_AT_type{{.*}}0x00000000[[CU2_FLOAT:[0-9a-f]*]] "float" +# CHECK: DW_AT_name{{.*}}"member2" + +# CHECK: 0x[[CU2_CLASS2:[0-9a-f]*]]: DW_TAG_class_type{{.*[[:space:]].*}}DW_AT_name{{.*}}"class2" +# CHECK: DW_TAG_template_type_parameter{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU2_INT:[0-9a-f]*]] "int" +# CHECK: DW_TAG_member +# CHECK: DW_AT_type{{.*}}0x00000000[[CU2_CHAR]] "char" +# CHECK: DW_AT_name{{.*}}"member1" + +# CHECK: 0x[[CU2_INT]]: DW_TAG_base_type +# CHECK: DW_AT_name{{.*}}"int" + +# CHECK: 0x[[CU2_CHAR]]: DW_TAG_base_type +# CHECK: DW_AT_name{{.*}}"char" + +# CHECK: 0x[[CU2_FLOAT]]: DW_TAG_base_type +# CHECK: DW_AT_name{{.*}}"float" + +# CHECK: 0x[[CU2_PTR_CLASS1:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU2_CLASS1]] "class1" + +# CHECK: 0x[[CU2_PTR_CLASS2:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU2_CLASS2]] "class2" + +# CHECK: DW_TAG_variable +# CHECK: DW_AT_name{{.*}}"var1" +# CHECK: DW_AT_type{{.*}}0x00000000[[CU2_PTR_CLASS1]] "class1 *" +# CHECK: DW_TAG_variable +# CHECK: DW_AT_name{{.*}}"var2" +# CHECK: DW_AT_type{{.*}}0x00000000[[CU2_PTR_CLASS2]] "class2 *" + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_string + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_member + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref_addr + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_template_type_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref_addr + - Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_pointer_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref_addr + - Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_const_value + Form: DW_FORM_data4 + - Attribute: DW_AT_type + Form: DW_FORM_ref_addr + - Table: + - Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_string + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_member + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref_addr + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_template_type_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref_addr + - Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_pointer_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref_addr + - Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_const_value + Form: DW_FORM_data4 + - Attribute: DW_AT_type + Form: DW_FORM_ref_addr + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU1 + - Value: 0x1000 + - Value: 0x100 + - AbbrCode: 4 + Values: + - CStr: class1 + - AbbrCode: 5 + Values: + - CStr: class2 + - AbbrCode: 6 + Values: + - Value: 0x00000040 + - AbbrCode: 0 + - AbbrCode: 7 + Values: + - CStr: int + - AbbrCode: 8 + Values: + - Value: 0x0000002a + - AbbrCode: 8 + Values: + - Value: 0x00000032 + - AbbrCode: 9 + Values: + - CStr: var1 + - Value: 0x00000000 + - Value: 0x00000045 + - AbbrCode: 9 + Values: + - CStr: var2 + - Value: 0x00000000 + - Value: 0x0000004a + - AbbrCode: 0 + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU2 + - Value: 0x2000 + - Value: 0x100 + - AbbrCode: 2 + Values: + - CStr: class1 + - AbbrCode: 3 + Values: + - Value: 0x000000d9 + - CStr: member1 + - AbbrCode: 3 + Values: + - Value: 0x000000df + - CStr: member2 + - AbbrCode: 0 + - AbbrCode: 2 + Values: + - CStr: class2 + - AbbrCode: 6 + Values: + - Value: 0x000000d4 + - AbbrCode: 3 + Values: + - Value: 0x000000d9 + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 7 + Values: + - CStr: int + - AbbrCode: 7 + Values: + - CStr: char + - AbbrCode: 7 + Values: + - CStr: float + - AbbrCode: 8 + Values: + - Value: 0x00000096 + - AbbrCode: 8 + Values: + - Value: 0x000000b9 + - AbbrCode: 9 + Values: + - CStr: var1 + - Value: 0x00000000 + - Value: 0x000000e6 + - AbbrCode: 9 + Values: + - CStr: var2 + - Value: 0x00000000 + - Value: 0x000000eb + - AbbrCode: 0 +... diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/verify.test b/llvm/test/tools/llvm-dwarfutil/ELF/verify.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/verify.test @@ -0,0 +1,141 @@ +# RUN: yaml2obj %s -o %t.o + +# RUN: llvm-dwarfutil %t.o -o %t1 --verify + +# RUN: llvm-dwarfutil %t.o --sep -o %t1 --verify + +# RUN: not llvm-dwarfutil --gc=0 %t.o -o %t1 --verify + +# RUN: not llvm-dwarfutil --gc=0 %t.o --sep -o %t1 --verify + +## This test checks that debug info containing in source file +## is properly verified by --verify after copying. If --gc=0 +## is specified then the verification should fail, otherwise +## the verification should succeed. + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_string + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data8 + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_member + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_name + Form: DW_FORM_string + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_class_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_declaration + Form: DW_FORM_flag_present + - Tag: DW_TAG_template_type_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU1 + - Value: 0x1000 + - Value: 0x100 + - AbbrCode: 3 + Values: + - CStr: class1 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class2 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class3 + - AbbrCode: 4 + Values: + - Value: 0x0000006c + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 8 + Values: + - CStr: int + - AbbrCode: 2 + Values: + - CStr: foo1 + - Value: 0x1000 + - Value: 0x10 + - Value: 0x0000002a + - AbbrCode: 2 + Values: + - CStr: foo2 + - Value: 0x0 + - Value: 0x100 + - Value: 0x00000040 + - AbbrCode: 2 + Values: + - CStr: foo3 + - Value: 0x0 + - Value: 0x100 + - Value: 0x00000040 + - AbbrCode: 0 +... diff --git a/llvm/test/tools/llvm-dwarfutil/help.test b/llvm/test/tools/llvm-dwarfutil/help.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/help.test @@ -0,0 +1,27 @@ +# RUN: llvm-dwarfutil | FileCheck %s +# RUN: llvm-dwarfutil -h | FileCheck %s +# RUN: llvm-dwarfutil --h | FileCheck %s + +## This test checks help message for llvm-dwarfutil utility. + +# CHECK: OVERVIEW: A tool for optimizing debug info. +# CHECK: USAGE: llvm-dwarfutil [options] +# CHECK: OPTIONS: +# CHECK: DwarfUtil Options: +# CHECK: --do-garbage-collection +# CHECK: --do-odr-deduplication +# CHECK: --gc +# CHECK: -h +# CHECK: -j - Alias for --num-threads. +# CHECK: --num-threads= +# CHECK: -o +# CHECK: --odr - Alias for --do-odr-deduplication. +# CHECK: --out= +# CHECK: --sep +# CHECK: --separate-debug-file +# CHECK: --tombstone= +# CHECK: -v +# CHECK: --verbose +# CHECK: --verify +# CHECK: --help +# CHECK: --version diff --git a/llvm/tools/dsymutil/DwarfLinkerForBinary.h b/llvm/tools/dsymutil/DwarfLinkerForBinary.h --- a/llvm/tools/dsymutil/DwarfLinkerForBinary.h +++ b/llvm/tools/dsymutil/DwarfLinkerForBinary.h @@ -139,8 +139,6 @@ } virtual ~AddressManager() override { clear(); } - virtual bool areRelocationsResolved() const override { return true; } - bool hasValidRelocs() override { return !ValidDebugInfoRelocs.empty() || !ValidDebugAddrRelocs.empty(); } @@ -171,10 +169,10 @@ uint64_t StartOffset, uint64_t EndOffset, CompileUnit::DIEInfo &Info); - bool hasLiveMemoryLocation(const DWARFDie &DIE, - CompileUnit::DIEInfo &Info) override; - bool hasLiveAddressRange(const DWARFDie &DIE, - CompileUnit::DIEInfo &Info) override; + bool isLiveVariable(const DWARFDie &DIE, + CompileUnit::DIEInfo &Info) override; + bool isLiveSubprogram(const DWARFDie &DIE, + CompileUnit::DIEInfo &Info) override; bool applyValidRelocs(MutableArrayRef Data, uint64_t BaseOffset, bool IsLittleEndian) override; diff --git a/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp b/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp --- a/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp +++ b/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp @@ -941,7 +941,7 @@ return std::make_pair(Offset, End); } -bool DwarfLinkerForBinary::AddressManager::hasLiveMemoryLocation( +bool DwarfLinkerForBinary::AddressManager::isLiveVariable( const DWARFDie &DIE, CompileUnit::DIEInfo &MyInfo) { const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); @@ -960,7 +960,7 @@ LocationEndOffset, MyInfo); } -bool DwarfLinkerForBinary::AddressManager::hasLiveAddressRange( +bool DwarfLinkerForBinary::AddressManager::isLiveSubprogram( const DWARFDie &DIE, CompileUnit::DIEInfo &MyInfo) { const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); @@ -1010,7 +1010,6 @@ /// \returns whether any reloc has been applied. bool DwarfLinkerForBinary::AddressManager::applyValidRelocs( MutableArrayRef Data, uint64_t BaseOffset, bool IsLittleEndian) { - assert(areRelocationsResolved()); std::vector Relocs = getRelocations( ValidDebugInfoRelocs, BaseOffset, BaseOffset + Data.size()); diff --git a/llvm/tools/dsymutil/LinkUtils.h b/llvm/tools/dsymutil/LinkUtils.h --- a/llvm/tools/dsymutil/LinkUtils.h +++ b/llvm/tools/dsymutil/LinkUtils.h @@ -56,7 +56,7 @@ OutputFileType FileType = OutputFileType::Object; /// The accelerator table kind - AccelTableKind TheAccelTableKind; + DwarfLinkerAccelTableKind TheAccelTableKind; /// -oso-prepend-path std::string PrependPath; diff --git a/llvm/tools/dsymutil/dsymutil.cpp b/llvm/tools/dsymutil/dsymutil.cpp --- a/llvm/tools/dsymutil/dsymutil.cpp +++ b/llvm/tools/dsymutil/dsymutil.cpp @@ -206,23 +206,24 @@ return Error::success(); } -static Expected getAccelTableKind(opt::InputArgList &Args) { +static Expected +getAccelTableKind(opt::InputArgList &Args) { if (opt::Arg *Accelerator = Args.getLastArg(OPT_accelerator)) { StringRef S = Accelerator->getValue(); if (S == "Apple") - return AccelTableKind::Apple; + return DwarfLinkerAccelTableKind::Apple; if (S == "Dwarf") - return AccelTableKind::Dwarf; + return DwarfLinkerAccelTableKind::Dwarf; if (S == "Pub") - return AccelTableKind::Pub; + return DwarfLinkerAccelTableKind::Pub; if (S == "Default") - return AccelTableKind::Default; + return DwarfLinkerAccelTableKind::Default; return make_error( "invalid accelerator type specified: '" + S + "'. Support values are 'Apple', 'Dwarf', 'Pub' and 'Default'.", inconvertibleErrorCode()); } - return AccelTableKind::Default; + return DwarfLinkerAccelTableKind::Default; } static Expected getVerifyKind(opt::InputArgList &Args) { @@ -282,7 +283,7 @@ if (Args.hasArg(OPT_gen_reproducer)) Options.ReproMode = ReproducerMode::Generate; - if (Expected AccelKind = getAccelTableKind(Args)) { + if (Expected AccelKind = getAccelTableKind(Args)) { Options.LinkOpts.TheAccelTableKind = *AccelKind; } else { return AccelKind.takeError(); diff --git a/llvm/tools/llvm-dwarfutil/CMakeLists.txt b/llvm/tools/llvm-dwarfutil/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-dwarfutil/CMakeLists.txt @@ -0,0 +1,18 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + DebugInfoDWARF + DWARFLinker + ObjCopy + MC + Object + Support + Target + AllTargetsCodeGens + AllTargetsDescs + AllTargetsInfos + ) + +add_llvm_tool(llvm-dwarfutil + llvm-dwarfutil.cpp + DebugInfoLinker.cpp + ) diff --git a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.h b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.h @@ -0,0 +1,28 @@ +//===-- DebugInfoLinker.h -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_DWARFUTIL_DEBUGINFOLINKER_H +#define LLVM_TOOLS_LLVM_DWARFUTIL_DEBUGINFOLINKER_H + +#include "Options.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ObjectFile.h" + +using namespace llvm; +using namespace object; + +namespace dwarfutil { + +bool linkDebugInfo(ObjectFile &file, const Options &Options, + raw_pwrite_stream &OutStream); + +} // end of namespace dwarfutil + +#endif // LLVM_TOOLS_LLVM_DWARFUTIL_DEBUGINFOLINKER_H diff --git a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp @@ -0,0 +1,224 @@ +//===-- DebugInfoLinker.cpp -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DebugInfoLinker.h" +#include "Error.h" +#include "llvm/DWARFLinker/DWARFLinker.h" +#include "llvm/DWARFLinker/DWARFStreamer.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFExpression.h" +#include "llvm/Object/ObjectFile.h" +#include +#include + +using namespace llvm; +using namespace object; + +namespace dwarfutil { + +static uint64_t isBFDDeadAddressRange(uint64_t LowPC, uint64_t HighPC, + uint16_t Version) { + if (LowPC == 0) + return true; + + if (Version > 4) + return false; + + return (LowPC == 1 && HighPC == 1); +} + +static uint64_t isMAXPCDeadAddressRange(uint64_t LowPC, uint64_t, + uint16_t Version, + uint8_t AddressByteSize) { + if (Version <= 4) + return LowPC == dwarf::computeTombstoneAddress(AddressByteSize) - 1; + + return LowPC == dwarf::computeTombstoneAddress(AddressByteSize); +} + +static bool isDeadAddressRange(uint64_t LowPC, uint64_t HighPC, + uint16_t Version, TombstoneKind Tombstone, + uint8_t AddressByteSize) { + switch (Tombstone) { + case TombstoneKind::BFD: + return isBFDDeadAddressRange(LowPC, HighPC, Version); + case TombstoneKind::MaxPC: + return isMAXPCDeadAddressRange(LowPC, HighPC, Version, AddressByteSize); + case TombstoneKind::Universal: + return isBFDDeadAddressRange(LowPC, HighPC, Version) || + isMAXPCDeadAddressRange(LowPC, HighPC, Version, AddressByteSize); + } + + llvm_unreachable("Unknown tombstone value"); +} + +static bool isDeadAddress(uint64_t LowPC, TombstoneKind Tombstone, + uint8_t AddressByteSize) { + switch (Tombstone) { + case TombstoneKind::BFD: + return LowPC == 0; + case TombstoneKind::MaxPC: + return LowPC == dwarf::computeTombstoneAddress(AddressByteSize); + case TombstoneKind::Universal: + return LowPC == 0 || + LowPC == dwarf::computeTombstoneAddress(AddressByteSize); + } + + llvm_unreachable("Unknown tombstone value"); +} + +class ObjFileAddressMap : public AddressesMap { +public: + ObjFileAddressMap(DWARFContext &Context, const Options &Options) + : Opts(Options) { + for (std::unique_ptr &CU : Context.compile_units()) { + Expected ARanges = + CU->getUnitDIE().getAddressRanges(); + if (ARanges) { + for (auto &Range : *ARanges) { + if (!isDeadAddressRange(Range.LowPC, Range.HighPC, CU->getVersion(), + Options.Tombstone, CU->getAddressByteSize())) + AddressRanges[{Range.LowPC}] = {Range.HighPC, 0}; + } + } + } + } + + // should be renamed into has valid address ranges + bool hasValidRelocs() override { return !AddressRanges.empty(); } + + bool isLiveSubprogram(const DWARFDie &DIE, + CompileUnit::DIEInfo &Info) override { + assert((DIE.getTag() == dwarf::DW_TAG_subprogram || + DIE.getTag() == dwarf::DW_TAG_label) && + "Wrong type of input die"); + + if (Optional LowPC = + dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc))) { + if (!isDeadAddress(*LowPC, Opts.Tombstone, + DIE.getDwarfUnit()->getAddressByteSize())) { + Info.AddrAdjust = 0; + Info.InDebugMap = true; + return true; + } + } + + return false; + } + + bool isLiveVariable(const DWARFDie &DIE, + CompileUnit::DIEInfo &Info) override { + assert((DIE.getTag() == dwarf::DW_TAG_variable || + DIE.getTag() == dwarf::DW_TAG_constant) && + "Wrong type of input die"); + + if (Expected Loc = + DIE.getLocations(dwarf::DW_AT_location)) { + DWARFUnit *U = DIE.getDwarfUnit(); + for (const auto &Entry : *Loc) { + DataExtractor Data(toStringRef(Entry.Expr), + U->getContext().isLittleEndian(), 0); + DWARFExpression Expression(Data, U->getAddressByteSize(), + U->getFormParams().Format); + bool HasLiveAddresses = + any_of(Expression, [&](const DWARFExpression::Operation &Op) { + // TODO: add handling of dwarf::DW_OP_addrx + return !Op.isError() && + (Op.getCode() == dwarf::DW_OP_addr && + !isDeadAddress(Op.getRawOperand(0), Opts.Tombstone, + DIE.getDwarfUnit()->getAddressByteSize())); + }); + + if (HasLiveAddresses) { + Info.AddrAdjust = 0; + Info.InDebugMap = true; + return true; + } + } + } else { + // FIXME: missing DW_AT_location is OK here, but other errors should be + // reported to the user. + consumeError(Loc.takeError()); + } + + return false; + } + + bool applyValidRelocs(MutableArrayRef, uint64_t, bool) override { + // no need to apply relocations to the linked binary. + return false; + } + + RangesTy &getValidAddressRanges() override { return AddressRanges; }; + + void clear() override { AddressRanges.clear(); } + + llvm::Expected relocateIndexedAddr(uint64_t, uint64_t) override { + // should not be called. + return createError("no relocations in linked binary"); + } + +private: + RangesTy AddressRanges; + const Options &Opts; +}; + +bool linkDebugInfo(ObjectFile &File, const Options &Options, + raw_pwrite_stream &OutStream) { + + auto ReportWarn = [&](const Twine &Message, StringRef Context, + const DWARFDie *) { + if (Context.empty()) + warning(Message); + else + warning(Context + ": " + Message); + }; + + // Create output streamer. + DwarfStreamer OutStreamer(OutputFileType::Object, OutStream, nullptr, + ReportWarn, ReportWarn); + if (!OutStreamer.init(File.makeTriple(), "")) + return false; + + // Create DWARF linker. + DWARFLinker DebugInfoLinker(&OutStreamer, DwarfLinkerClient::LLD); + + DebugInfoLinker.setEstimatedObjfilesAmount(1); + DebugInfoLinker.setAccelTableKind(DwarfLinkerAccelTableKind::None); + DebugInfoLinker.setErrorHandler(ReportWarn); + DebugInfoLinker.setWarningHandler(ReportWarn); + DebugInfoLinker.setNumThreads(Options.NumThreads); + DebugInfoLinker.setNoODR(!Options.DoODRDeduplication); + DebugInfoLinker.setVerbosity(Options.Verbose); + DebugInfoLinker.setUpdate(!Options.DoGarbageCollection); + + std::vector> ObjectsForLinking(1); + std::vector> AddresssMapForLinking(1); + std::vector EmptyWarnings; + + std::unique_ptr Context = DWARFContext::create(File); + + // Add object files to the DWARFLinker. + AddresssMapForLinking[0] = + std::make_unique(*Context, Options); + + ObjectsForLinking[0] = std::make_unique( + File.getFileName(), &*Context, AddresssMapForLinking[0].get(), + EmptyWarnings); + + for (size_t I = 0; I < ObjectsForLinking.size(); I++) + DebugInfoLinker.addObjectFile(*ObjectsForLinking[I]); + + // Link debug info. + DebugInfoLinker.link(); + OutStreamer.finish(); + return true; +} + +} // end of namespace dwarfutil diff --git a/llvm/tools/llvm-dwarfutil/Error.h b/llvm/tools/llvm-dwarfutil/Error.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-dwarfutil/Error.h @@ -0,0 +1,48 @@ +//===-- Error.h -----------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_DWARFUTIL_ERROR_H +#define LLVM_TOOLS_LLVM_DWARFUTIL_ERROR_H + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace dwarfutil { + +inline void error(StringRef Prefix, Error Err) { + handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) { + WithColor::error(errs(), Prefix) << Info.message() << '\n'; + }); +} + +inline void error(StringRef Prefix, const Twine &Message) { + WithColor::error(errs(), Prefix) << Message << '\n'; +} + +inline void warning(const Twine &Message) { + WithColor::warning(errs()) << Message << '\n'; +} + +inline void verbose(const Twine &Message, bool Verbose) { + if (Verbose) + outs() << Message << '\n'; +} + +} // end of namespace dwarfutil + +#endif // LLVM_TOOLS_LLVM_DWARFUTIL_ERROR_H diff --git a/llvm/tools/llvm-dwarfutil/Options.h b/llvm/tools/llvm-dwarfutil/Options.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-dwarfutil/Options.h @@ -0,0 +1,54 @@ +//===-- Options.h ---------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_DWARFUTIL_OPTIONS_H +#define LLVM_TOOLS_LLVM_DWARFUTIL_OPTIONS_H + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" + +using namespace llvm; + +namespace dwarfutil { + +/// The kind of tombstone value. +enum class TombstoneKind { + BFD, /// 0/[1:1]. Bfd default. + MaxPC, /// -1/-2. Assumed to match with + /// http://www.dwarfstd.org/ShowIssue.php?issue=200609.1. + Universal, /// both: BFD + MaxPC +}; + +struct Options { + StringRef InputFileName; + StringRef OutputFileName; + bool DoGarbageCollection = false; + bool DoODRDeduplication = false; + bool BuildSeparateDebugFile = false; + TombstoneKind Tombstone = TombstoneKind::Universal; + bool Verbose = false; + int NumThreads = 0; + bool Verify = false; + + StringRef getSeparateDebugFileName() const { + if (LinkedDebugFileName.empty()) { + LinkedDebugFileName = OutputFileName.str(); + LinkedDebugFileName += ".debug"; + } + + return LinkedDebugFileName; + } + +protected: + mutable std::string LinkedDebugFileName; +}; + +} // namespace dwarfutil + +#endif // LLVM_TOOLS_LLVM_DWARFUTIL_OPTIONS_H diff --git a/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp b/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp @@ -0,0 +1,532 @@ +//===-- dwarfutil.cpp - DWARF optimizing utility for llvm -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DebugInfoLinker.h" +#include "Error.h" +#include "Options.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFVerifier.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/ObjCopy/CommonConfig.h" +#include "llvm/ObjCopy/ConfigManager.h" +#include "llvm/ObjCopy/ObjCopy.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetSelect.h" + +using namespace llvm; +using namespace object; + +/// @} +/// Command line options. +/// @{ + +using namespace cl; + +namespace dwarfutil { + +OptionCategory DwarfUtilOptions("DwarfUtil Options"); + +static opt + BuildSeparateDebugFile("separate-debug-file", + desc("Create two output files: file w/o debug " + "tables and file with debug tables."), + init(false), cat(DwarfUtilOptions)); +static alias Sep("sep", desc("Alias for --separate-debug-file."), + aliasopt(BuildSeparateDebugFile), cat(DwarfUtilOptions), + cl::NotHidden); + +static opt + DoODRDeduplication("do-odr-deduplication", + desc("Do ODR deduplication for debug types."), + init(true), cat(DwarfUtilOptions)); +static alias Odr("odr", desc("Alias for --do-odr-deduplication."), + aliasopt(DoODRDeduplication), cat(DwarfUtilOptions), + cl::NotHidden); + +static opt + DoGarbageCollection("do-garbage-collection", + desc("Do garbage collection for debug info."), + init(true), cat(DwarfUtilOptions)); +static alias GC("gc", desc("Alias for --do-garbage-collection."), + aliasopt(DoGarbageCollection), cat(DwarfUtilOptions), + cl::NotHidden); + +static opt Help("h", desc("Alias for --help."), init(false), + cat(DwarfUtilOptions)); + +static cl::opt InputFilename(cl::Positional, + cl::desc("")); + +static cl::opt OutputFilename("out", desc(""), + cat(DwarfUtilOptions)); +static alias OF("o", desc("Alias for --out."), aliasopt(OutputFilename), + cat(DwarfUtilOptions), cl::NotHidden); + +static opt + NumThreads("num-threads", + desc("Specify the maximum number (n) of simultaneous threads " + "to use when optimizing input file.\nDefaults to the " + "number of cores on the current machine."), + value_desc("n"), init(0), cat(DwarfUtilOptions)); +static alias J("j", desc("Alias for --num-threads."), aliasopt(NumThreads), + cat(DwarfUtilOptions), cl::NotHidden); + +static opt Tombstone( + "tombstone", desc("Tombstone value used as a marker of invalid address."), + values(clEnumValN(TombstoneKind::BFD, "bfd", "BFD default value."), + clEnumValN(TombstoneKind::MaxPC, "maxpc", "Max PC values(-1/-2)."), + clEnumValN(TombstoneKind::Universal, "universal", + "Both: bfd and maxpc.")), + init(TombstoneKind::Universal), cat(DwarfUtilOptions)); + +static opt Verbose("verbose", + desc("Enable verbose logging and encoding details."), + cat(DwarfUtilOptions)); + +static opt + Verify("verify", + desc("Run the DWARF verifier on the resulting debug info."), + init(false), cat(DwarfUtilOptions)); + +static opt Version("v", desc("Alias for --version."), init(false), + cat(DwarfUtilOptions)); + +std::string ToolName; + +static mc::RegisterMCTargetOptionsFlags MOF; + +static bool isDebugSection(StringRef SecName) { + return SecName.startswith(".debug") || SecName.startswith(".zdebug") || + SecName == ".gdb_index"; +} + +static Error validateAndSetOptions(Options &Options) { + Options.BuildSeparateDebugFile = BuildSeparateDebugFile; + Options.InputFileName = InputFilename; + Options.DoODRDeduplication = DoODRDeduplication; + Options.NumThreads = NumThreads; + Options.OutputFileName = OutputFilename; + Options.DoGarbageCollection = DoGarbageCollection; + Options.Tombstone = Tombstone; + Options.Verbose = Verbose; + Options.Verify = Verify; + + if (Options.Verbose) { + if (Options.NumThreads != 1) + warning("--num-threads set to 1 because Verbose mode is specified"); + + Options.NumThreads = 1; + } + + if (Options.DoODRDeduplication && + DoODRDeduplication.getNumOccurrences() > 0 && + !Options.DoGarbageCollection) { + warning("switching on for --do-odr-deduplication without " + "--do-garbage-collection is not currently supported. switched off"); + Options.DoODRDeduplication = false; + } + + if (Options.BuildSeparateDebugFile && Options.OutputFileName == "-") { + warning( + "separate file with debug information would not be created. writing " + "to stdout"); + Options.BuildSeparateDebugFile = false; + } + + return Error::success(); +} + +static Error setConfigToAddNewDebugSections(objcopy::ConfigManager &Config, + ObjectFile &ObjFile) { + // Add new debug sections. + for (const SectionRef &Sec : ObjFile.sections()) { + Expected SecName = Sec.getName(); + if (!SecName) + return SecName.takeError(); + + if (isDebugSection(*SecName)) { + Expected SecData = Sec.getContents(); + if (!SecData) + return SecData.takeError(); + + Config.Common.AddSection.emplace_back(objcopy::NewSectionInfo( + *SecName, MemoryBuffer::getMemBuffer(*SecData, *SecName, false))); + } + } + + return Error::success(); +} + +static Expected verifyOutput(const Options &Opts) { + if (Opts.OutputFileName == "-") { + warning("verification skipped because writing to stdout"); + return true; + } + + Expected> BinOrErr = + createBinary(Opts.BuildSeparateDebugFile ? Opts.getSeparateDebugFileName() + : Opts.OutputFileName); + if (!BinOrErr) + return BinOrErr.takeError(); + + if (BinOrErr->getBinary()->isObject()) { + if (ObjectFile *Obj = static_cast(BinOrErr->getBinary())) { + verbose("Verifying DWARF...", Opts.Verbose); + std::unique_ptr DICtx = DWARFContext::create(*Obj); + DIDumpOptions DumpOpts; + return DICtx->verify(Opts.Verbose ? outs() : nulls(), + DumpOpts.noImplicitRecursion()); + } + } + + return createError("unsupported file"); +} + +class raw_crc_ostream : public raw_ostream { + raw_ostream &OS; + uint32_t CRC32 = 0; + + /// See raw_ostream::write_impl. + void write_impl(const char *Ptr, size_t Size) override { + CRC32 = crc32( + CRC32, ArrayRef(reinterpret_cast(Ptr), Size)); + OS.write(Ptr, Size); + } + + /// Return the current position within the stream, not counting the bytes + /// currently in the buffer. + uint64_t current_pos() const override { return OS.tell(); } + +public: + explicit raw_crc_ostream(raw_ostream &O) : OS(O) { SetUnbuffered(); } + + void reserveExtraSpace(uint64_t ExtraSize) override { + OS.reserveExtraSpace(ExtraSize); + } + + uint32_t getCRC32() { return CRC32; } +}; + +static Expected saveSeparateDebugInfo(const Options &Opts, + ObjectFile &InputFile) { + objcopy::ConfigManager Config; + Config.Common.InputFilename = Opts.InputFileName; + Config.Common.OutputFilename = Opts.getSeparateDebugFileName(); + Config.Common.OnlyKeepDebug = true; + uint32_t WrittenFileCRC32 = 0; + + if (Error Err = writeToOutput( + Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { + raw_crc_ostream CRCBuffer(OutFile); + if (Error Err = objcopy::executeObjcopyOnBinary(Config, InputFile, + CRCBuffer)) + return Err; + + WrittenFileCRC32 = CRCBuffer.getCRC32(); + return Error::success(); + })) + return std::move(Err); + + return WrittenFileCRC32; +} + +static Error saveNonDebugInfo(const Options &Opts, ObjectFile &InputFile, + uint32_t GnuDebugLinkCRC32) { + objcopy::ConfigManager Config; + Config.Common.InputFilename = Opts.InputFileName; + Config.Common.OutputFilename = Opts.OutputFileName; + Config.Common.StripDebug = true; + Config.Common.AddGnuDebugLink = + sys::path::filename(Opts.getSeparateDebugFileName()); + Config.Common.GnuDebugLinkCRC32 = GnuDebugLinkCRC32; + + if (Error Err = writeToOutput( + Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { + if (Error Err = + objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile)) + return Err; + + return Error::success(); + })) + return Err; + + return Error::success(); +} + +static Error splitDebugIntoSeparateFile(const Options &Opts, + ObjectFile &InputFile) { + Expected SeparateDebugFileCRC32OrErr = + saveSeparateDebugInfo(Opts, InputFile); + if (!SeparateDebugFileCRC32OrErr) + return SeparateDebugFileCRC32OrErr.takeError(); + + if (Error Err = + saveNonDebugInfo(Opts, InputFile, *SeparateDebugFileCRC32OrErr)) + return Err; + + return Error::success(); +} + +using DebugInfoBits = SmallString<100000>; + +static Error addSectionsFromLinkedData(objcopy::ConfigManager &Config, + ObjectFile &InputFile, + DebugInfoBits &LinkedDebugInfoBits) { + if (dyn_cast>(&InputFile)) { + Expected> MemFile = ELFObjectFile::create( + MemoryBufferRef(LinkedDebugInfoBits, "")); + if (!MemFile) + return MemFile.takeError(); + + if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile)) + return Err; + } else if (dyn_cast>(&InputFile)) { + Expected> MemFile = ELFObjectFile::create( + MemoryBufferRef(LinkedDebugInfoBits, "")); + if (!MemFile) + return MemFile.takeError(); + + if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile)) + return Err; + } else if (dyn_cast>(&InputFile)) { + + Expected> MemFile = ELFObjectFile::create( + MemoryBufferRef(LinkedDebugInfoBits, "")); + if (!MemFile) + return MemFile.takeError(); + + if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile)) + return Err; + } else if (dyn_cast>(&InputFile)) { + + Expected> MemFile = ELFObjectFile::create( + MemoryBufferRef(LinkedDebugInfoBits, "")); + if (!MemFile) + return MemFile.takeError(); + + if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile)) + return Err; + } else + return createError("unsupported file format"); + + return Error::success(); +} + +static Expected +saveSeparateLinkedDebugInfo(const Options &Opts, ObjectFile &InputFile, + DebugInfoBits LinkedDebugInfoBits) { + objcopy::ConfigManager Config; + Config.Common.InputFilename = Opts.InputFileName; + Config.Common.OutputFilename = Opts.getSeparateDebugFileName(); + Config.Common.StripDebug = true; + Config.Common.OnlyKeepDebug = true; + uint32_t WrittenFileCRC32 = 0; + + if (Error Err = + addSectionsFromLinkedData(Config, InputFile, LinkedDebugInfoBits)) + return std::move(Err); + + if (Error Err = writeToOutput( + Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { + raw_crc_ostream CRCBuffer(OutFile); + + if (Error Err = objcopy::executeObjcopyOnBinary(Config, InputFile, + CRCBuffer)) + return Err; + + WrittenFileCRC32 = CRCBuffer.getCRC32(); + return Error::success(); + })) + return std::move(Err); + + return WrittenFileCRC32; +} + +static Error saveSingleLinkedDebugInfo(const Options &Opts, + ObjectFile &InputFile, + DebugInfoBits LinkedDebugInfoBits) { + objcopy::ConfigManager Config; + + Config.Common.InputFilename = Opts.InputFileName; + Config.Common.OutputFilename = Opts.OutputFileName; + Config.Common.StripDebug = true; + if (Error Err = + addSectionsFromLinkedData(Config, InputFile, LinkedDebugInfoBits)) + return Err; + + if (Error Err = writeToOutput( + Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { + return objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile); + })) + return Err; + + return Error::success(); +} + +static Error saveLinkedDebugInfo(const Options &Opts, ObjectFile &InputFile, + DebugInfoBits LinkedDebugInfoBits) { + if (Opts.BuildSeparateDebugFile) { + Expected SeparateDebugFileCRC32OrErr = + saveSeparateLinkedDebugInfo(Opts, InputFile, + std::move(LinkedDebugInfoBits)); + if (!SeparateDebugFileCRC32OrErr) + return SeparateDebugFileCRC32OrErr.takeError(); + + if (Error Err = + saveNonDebugInfo(Opts, InputFile, *SeparateDebugFileCRC32OrErr)) + return Err; + } else { + if (Error Err = saveSingleLinkedDebugInfo(Opts, InputFile, + std::move(LinkedDebugInfoBits))) + return Err; + } + + return Error::success(); +} + +static Error saveCopyOfFile(const Options &Opts, ObjectFile &InputFile) { + objcopy::ConfigManager Config; + + Config.Common.InputFilename = Opts.InputFileName; + Config.Common.OutputFilename = Opts.OutputFileName; + + if (Error Err = writeToOutput( + Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error { + return objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile); + })) + return Err; + + return Error::success(); +} + +static Error applyCLOptions(const struct Options &Opts, ObjectFile &InputFile) { + if (Opts.DoGarbageCollection) { + verbose("Do garbage collection for debug info ...", Opts.Verbose); + + DebugInfoBits LinkedDebugInfo; + raw_svector_ostream OutStream(LinkedDebugInfo); + + if (linkDebugInfo(InputFile, Opts, OutStream)) { + if (Error Err = + saveLinkedDebugInfo(Opts, InputFile, std::move(LinkedDebugInfo))) + return Err; + } else + return createStringError(std::errc::invalid_argument, + "possible broken debug info"); + + return Error::success(); + } else if (Opts.BuildSeparateDebugFile) { + if (Error Err = splitDebugIntoSeparateFile(Opts, InputFile)) + return Err; + } else { + if (Error Err = saveCopyOfFile(Opts, InputFile)) + return Err; + } + + return Error::success(); +} + +} // end of namespace dwarfutil + +using namespace dwarfutil; + +int main(int Argc, char const *Argv[]) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(Argv[0]); + PrettyStackTraceProgram X(Argc, Argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + int NumArgs = Argc; + ToolName = Argv[0]; + + const char *Overview = "A tool for optimizing debug info.\n"; + HideUnrelatedOptions({&DwarfUtilOptions}); + cl::ParseCommandLineOptions(NumArgs, Argv, Overview); + + if (Help || (NumArgs == 1)) { + PrintHelpMessage(/*Hidden =*/false, /*Categorized =*/true); + return EXIT_SUCCESS; + } else if (Version) { + PrintVersionMessage(); + return EXIT_SUCCESS; + } else if (InputFilename.empty()) { + error(ToolName, createStringError(std::errc::invalid_argument, + "please specify input file")); + return EXIT_FAILURE; + } else if (InputFilename != "-" && !sys::fs::exists(InputFilename)) { + std::string Message; + Message = "specified input file \""; + Message += InputFilename; + Message += "\" does not exist."; + error(ToolName, + createStringError(std::errc::invalid_argument, Message.c_str())); + return EXIT_FAILURE; + } else if (OutputFilename.empty()) { + error(ToolName, createStringError(std::errc::invalid_argument, + "please specify output file")); + return EXIT_FAILURE; + } + + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllTargetInfos(); + InitializeAllAsmPrinters(); + InitializeAllAsmParsers(); + + ErrorOr> BuffOrErr = + MemoryBuffer::getFileOrSTDIN(InputFilename); + if (BuffOrErr.getError()) { + error(InputFilename, + createStringError(std::errc::invalid_argument, + BuffOrErr.getError().message().c_str())); + return EXIT_FAILURE; + } + + Expected> BinOrErr = + object::createBinary(**BuffOrErr); + if (!BinOrErr) { + error(InputFilename, BinOrErr.takeError()); + return EXIT_FAILURE; + } + + if (!(*BinOrErr)->isObject()) { + error(InputFilename, createStringError(std::errc::invalid_argument, + "corrupted input file")); + return EXIT_FAILURE; + } + + Options Opts; + if (Error Err = validateAndSetOptions(Opts)) { + error(InputFilename, std::move(Err)); + return EXIT_FAILURE; + } + + if (Error Err = + applyCLOptions(Opts, *static_cast((*BinOrErr).get()))) { + error(InputFilename, std::move(Err)); + return EXIT_FAILURE; + } + + if (Opts.Verify) { + Expected IsCorrect = verifyOutput(Opts); + if (!IsCorrect) { + error(InputFilename, IsCorrect.takeError()); + return EXIT_FAILURE; + } + + if (!*IsCorrect) { + error(InputFilename, createError("output verification failed")); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +}