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,123 @@ +llvm-dwarfutil - A tool to copy and manipulate debug info +========================================================= + +.. program:: llvm-dwarfutil + +SYNOPSIS +-------- + +:program:`llvm-dwarfutil` [*options*] *input* *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:: --garbage-collection + + Removes pieces of debug information related to discarded sections. + When the linker does section garbage collection the abandoned debug info + is left behind. Such abandoned debug info references address ranges using + tombstone values. Thus, when this option is specified, the tool removes + debug info which is marked with the tombstone value. + + That option is enabled by default. + +.. option:: --odr-deduplication + + Remove duplicated types (if "One Definition Rule" is supported by source + language). Keeps first type definition and removes other definitions, + potentially significantly reducing the size of output debug info. + + That option is enabled by default. + +.. option:: --help, -h + + Print a summary of command line options. + +.. option:: --no-garbage-collection + + Disable :option:`--garbage-collection`. + +.. option:: --no-odr-deduplication + + Disable :option:`--odr-deduplication`. + +.. option:: --no-separate-debug-file + + Disable :option:`--separate-debug-file`. + +.. option:: --num-threads=, -j + + Specifies the maximum number (`n`) of simultaneous threads to use + for processing. + +.. option:: --separate-debug-file + + Generate separate file containing output debug info. Using + :program:`llvm-dwarfutil` with that option equals to the + following set of commands: + +.. code-block:: console + + :program:`llvm-objcopy` --only-keep-debug in-file out-file.debug + :program:`llvm-objcopy` --strip-debug in-file out-file + :program:`llvm-objcopy` --add-gnu-debuglink=out-file.debug out-file + +.. option:: --tombstone= + + can be one of the following values: + + - `bfd`: zero for all addresses and [1,1] for DWARF v4 (or less) address + ranges. + + - `maxpc`: -1 for all addresses and -2 for DWARF v4 (or less) address ranges. + + - `universal`: both `bfd` and `maxpc`. + + - `exec`: match with address ranges of executable sections. + + 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, @@ -176,6 +182,10 @@ /// offset \p PCOffset. void addFunctionRange(uint64_t LowPC, uint64_t HighPC, int64_t PCOffset); + /// Check whether specified address range \p LowPC \p HighPC + /// overlaps with existing function ranges. + bool overlapsWithFunctionRanges(uint64_t LowPC, uint64_t HighPC); + /// Keep track of a DW_AT_range attribute that we will need to patch up later. void noteRangeAttribute(const DIE &Die, PatchLocation Attr); 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) { @@ -506,6 +500,15 @@ return Flags; } + if (Unit.overlapsWithFunctionRanges(*LowPc, *HighPc)) { + reportWarning( + formatv("Overlapping address range [{0:X}, {1:X}]. Range will " + "be discarded.\n", + *LowPc, *HighPc), + File, &DIE); + return Flags; + } + // Replace the debug map range with a more accurate one. Ranges[*LowPc] = ObjFileAddressRange(*HighPc, MyInfo.AddrAdjust); Unit.addFunctionRange(*LowPc, *HighPc, MyInfo.AddrAdjust); @@ -617,6 +620,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 +670,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 +680,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 +778,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 +803,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 +910,6 @@ DIE *NewRefDie = nullptr; CompileUnit *RefUnit = nullptr; - DeclContext *Ctxt = nullptr; DWARFDie RefDie = Linker.resolveDIEReference(File, CompileUnits, Val, InputDIE, RefUnit); @@ -889,14 +922,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 +959,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 +1390,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 +1419,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 +1822,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 +2146,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 +2253,7 @@ } void DWARFLinker::updateAccelKind(DWARFContext &Dwarf) { - if (Options.TheAccelTableKind != AccelTableKind::Default) + if (Options.TheAccelTableKind != DwarfLinkerAccelTableKind::Default) return; auto &DwarfObj = Dwarf.getDWARFObj(); @@ -2343,11 +2379,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 +2494,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 +2562,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()); } } @@ -112,6 +115,10 @@ this->HighPc = std::max(HighPc, FuncHighPc + PcOffset); } +bool CompileUnit::overlapsWithFunctionRanges(uint64_t LowPC, uint64_t HighPC) { + return Ranges.overlaps(LowPC, HighPC); +} + void CompileUnit::noteRangeAttribute(const DIE &Die, PatchLocation Attr) { if (Die.getTag() != dwarf::DW_TAG_compile_unit) RangeAttributes.push_back(Attr); 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/Inputs/archive.a b/llvm/test/tools/llvm-dwarfutil/ELF/Inputs/archive.a new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/Inputs/archive.a @@ -0,0 +1,7 @@ +! +// 20 ` +a-plus-b.a/ +c.txt/ + +/0 0 0 0 644 202 ` +/12 0 0 0 644 11 ` diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/copy-itself.test b/llvm/test/tools/llvm-dwarfutil/ELF/copy-itself.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/copy-itself.test @@ -0,0 +1,187 @@ +## This test checks that the debug info contained in the source file +## is fully copied to the destination file when the source and +## destination are the same file. + +## Check implicit --garbage-collection --no-separate-debug-file case. +# RUN: yaml2obj %s -o %t1 +# RUN: llvm-dwarfutil %t1 %t1 +# RUN: llvm-dwarfdump -a %t1 | FileCheck --check-prefix=CHECK-DEBUG %s + +## Check implicit --garbage-collection and explicit --separate-debug-file. +# RUN: yaml2obj %s -o %t1 +# RUN: llvm-dwarfutil %t1 --separate-debug-file %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 + +## Check explicit --no-garbage-collection and implicit --no-separate-debug-file. +# RUN: yaml2obj %s -o %t1 +# RUN: llvm-dwarfutil --no-garbage-collection %t4 %t4 +# RUN: llvm-dwarfdump -a %t1 | FileCheck --check-prefix=CHECK-DEBUG %s + +## Check explicit --no-garbage-collection and --separate-debug-file. +# RUN: yaml2obj %s -o %t1 +# RUN: llvm-dwarfutil --no-garbage-collection %t1 --separate-debug-file %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 + +# 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 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000000010 + Content: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +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 + - 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 + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU1 + - Value: 0x1000 + - Value: 0x1b + - 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: 10 + Values: + - CStr: foo1 + - Value: 0x1000 + - Value: 0x10 + - Value: 0x0000002a + - AbbrCode: 0 +... 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,194 @@ +## This test checks that debug info contained in the source file +## is fully copied to the destination file in case --no-garbage-collection +## is specified. + +# RUN: yaml2obj %s -o %t.o + +## Check that the resulting file contains debug info from source file. +# RUN: llvm-dwarfutil --no-garbage-collection %t.o %t1 +# RUN: llvm-dwarfdump -a %t1 | FileCheck --check-prefix=CHECK-DEBUG %s + +## Check that the first copy matches with the second. +# RUN: llvm-dwarfutil --no-garbage-collection %t1 %t2 +# RUN: llvm-dwarfdump -a %t2 | FileCheck --check-prefix=CHECK-DEBUG %s +# RUN: diff %t1 %t2 + +## Check that debug info is stored in separate file. +# RUN: llvm-dwarfutil --no-garbage-collection %t.o --separate-debug-file %t3 +# RUN: llvm-objdump --headers %t3 | FileCheck --check-prefix=CHECK-NON-DEBUG %s +# RUN: llvm-dwarfdump -a %t3.debug | FileCheck --check-prefix=CHECK-DEBUG %s +# RUN: cp %t3 %t4 +# RUN: cp %t3.debug %t4.debug + +## Check that debug info is stored in separate file (last --separate-debug-file wins). +# RUN: llvm-dwarfutil %t.o --no-garbage-collection --no-separate-debug-file --separate-debug-file %t3 +# RUN: diff %t4 %t3 +# RUN: diff %t4.debug %t3.debug + +## Check that the resulting file contains debug info from source file. +## (last --no-separate-debug-file wins) +# RUN: llvm-dwarfutil %t.o --no-garbage-collection --separate-debug-file --no-separate-debug-file %t5 +# RUN: diff %t1 %t5 + +# 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 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000000010 + Content: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +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 + - 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 + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU1 + - Value: 0x1000 + - Value: 0x1b + - 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: 10 + Values: + - CStr: foo1 + - Value: 0x1000 + - Value: 0x10 + - Value: 0x0000002a + - AbbrCode: 0 + - AbbrCode: 0 +... diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/error-corrupted-input-file.test b/llvm/test/tools/llvm-dwarfutil/ELF/error-corrupted-input-file.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/error-corrupted-input-file.test @@ -0,0 +1,6 @@ +## This test checks the error message displayed if input file +## has wrong format. + +# RUN: not llvm-dwarfutil --garbage-collection %p/Inputs/archive.a - 2>&1 | FileCheck %s + +# CHECK: archive.a: error: corrupted input file diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/error-separate-file-stdout.test b/llvm/test/tools/llvm-dwarfutil/ELF/error-separate-file-stdout.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/error-separate-file-stdout.test @@ -0,0 +1,134 @@ +## This test checks the error message displayed if the destination +## is stdout and separate debug info file is requested. + +# RUN: yaml2obj %s -o %t.o +# RUN: not llvm-dwarfutil --separate-debug-file %t.o - 2>&1 | FileCheck --check-prefix CHECK %s + +# CHECK: error: writing to stdout. --separate-debug-file should be disabled + +--- !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/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,151 @@ +## This test checks that debug info related to deleted code (marked with +## tombstone=bfd) is removed. + +# RUN: yaml2obj %s -o %t.o + +# RUN: llvm-dwarfutil --tombstone=bfd --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s +# RUN: llvm-dwarfutil --tombstone=universal --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s + +# 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 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000000010 + Content: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +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: 0x1b + - 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-class.test b/llvm/test/tools/llvm-dwarfutil/ELF/gc-class.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/gc-class.test @@ -0,0 +1,350 @@ +## This test checks debug info for the case when one compilation unit +## contains a definition of a type and another compilation unit +## contains a definition of the same type. When --garbage-collection +## is enabled the result should contain the original definition in CU1 +## and the definition from CU2 should have been removed. + +# RUN: yaml2obj %s -o %t.o + +## Check implicit --garbage-collection and --odr-deduplication. +# RUN: llvm-dwarfutil %t.o %t1 +# RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefixes=CHECK,CHECK-ODR + +## Check implicit --garbage-collection and explicit alias for --odr-deduplication. +# RUN: llvm-dwarfutil --odr %t.o %t2 +# RUN: diff %t1 %t2 + +## Check implicit --garbage-collection and explicit alias for --no-odr (last option wins). +# RUN: llvm-dwarfutil --odr --no-odr %t.o %t3 +# RUN: llvm-dwarfdump -a %t3 | FileCheck %s --check-prefixes=CHECK,CHECK-NOODR + +## Check implicit --garbage-collection and explicit --no-odr-deduplication (last option wins). +# RUN: llvm-dwarfutil --odr-deduplication --no-odr-deduplication %t.o %t4 +# RUN: diff %t3 %t4 + +## Check implicit --garbage-collection and explicit alias for --odr-deduplication (last option wins). +# RUN: llvm-dwarfutil --no-odr --odr %t.o %t5 +# RUN: diff %t1 %t5 + +## Check implicit --garbage-collection and explicit --odr-deduplication (last option wins). +# RUN: llvm-dwarfutil --no-odr-deduplication --odr-deduplication %t.o %t6 +# RUN: diff %t1 %t6 + +## 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-ODR: DW_AT_type{{.*}}0x00000000[[CU1_CHAR:[0-9a-f]*]] "char" +# CHECK-NOODR: DW_AT_type{{.*}}0x[[CU1_CHAR:[0-9a-f]*]] "char" +# CHECK: DW_AT_name{{.*}}"member1" + +# CHECK: DW_TAG_member +# CHECK-ODR: DW_AT_type{{.*}}0x00000000[[CU1_FLOAT:[0-9a-f]*]] "float" +# CHECK-NOODR: DW_AT_type{{.*}}0x[[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-ODR: 0x[[CU1_PTR_CLASS1:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU1_CLASS1]] "class1" +# CHECK-NOODR: 0x[[CU1_PTR_CLASS1:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x[[CU1_CLASS1]] "class1" + +# CHECK: DW_TAG_variable +# CHECK: DW_AT_name{{.*}}"var1" +# CHECK-ODR: DW_AT_type{{.*}}0x00000000[[CU1_PTR_CLASS1]] "class1 *" +# CHECK-NOODR: DW_AT_type{{.*}}0x[[CU1_PTR_CLASS1]] "class1 *" + +# CHECK: DW_TAG_compile_unit +# CHECK: DW_AT_name{{.*}}"CU2" + +# CHECK-ODR-NOT: DW_TAG_class_type +# CHECK-ODR-NOT: "class1" + +# CHECK-ODR: 0x[[CU2_PTR_CLASS1:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU1_CLASS1]] "class1" + +# CHECK-ODR: DW_TAG_variable +# CHECK-ODR: DW_AT_name{{.*}}"var1" +# CHECK-ODR: DW_AT_type{{.*}}0x00000000[[CU2_PTR_CLASS1]] "class1 *" + + +# CHECK-NOODR: 0x[[CU2_CLASS1:[0-9a-f]*]]: DW_TAG_class_type{{.*[[:space:]].*}}DW_AT_name{{.*}}"class1" +# CHECK-NOODR: DW_TAG_member +# CHECK-NOODR: DW_AT_type{{.*}}0x[[CU2_CHAR:[0-9a-f]*]] "char" +# CHECK-NOODR: DW_AT_name{{.*}}"member1" + +# CHECK-NOODR: DW_TAG_member +# CHECK-NOODR: DW_AT_type{{.*}}0x[[CU2_FLOAT:[0-9a-f]*]] "float" +# CHECK-NOODR: DW_AT_name{{.*}}"member2" + +# CHECK-NOODR: 0x[[CU2_INT:[0-9a-f]*]]: DW_TAG_base_type +# CHECK-NOODR: DW_AT_name{{.*}}"int" + +# CHECK-NOODR: 0x[[CU2_CHAR]]: DW_TAG_base_type +# CHECK-NOODR: DW_AT_name{{.*}}"char" + +# CHECK-NOODR: 0x[[CU2_FLOAT]]: DW_TAG_base_type +# CHECK-NOODR: DW_AT_name{{.*}}"float" + +# CHECK-NOODR: 0x[[CU2_PTR_CLASS1:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x[[CU2_CLASS1]] "class1" + +# CHECK-NOODR: DW_TAG_variable +# CHECK-NOODR: DW_AT_name{{.*}}"var1" +# CHECK-NOODR: DW_AT_type{{.*}}0x[[CU2_PTR_CLASS1]] "class1 *" + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000000010 + Content: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + - Name: .text2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x2000 + AddressAlign: 0x0000000000000010 + Content: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +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: 0x1b + - 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: 0x1b + - 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-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,163 @@ +## This test checks that debug info related to deleted code (marked with +## default tombstone value) is removed. + +# RUN: yaml2obj %s -o %t.o + +# RUN: llvm-dwarfutil %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC + +# RUN: llvm-dwarfutil --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC + +# RUN: llvm-dwarfutil --no-garbage-collection --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC + +# RUN: llvm-dwarfutil --garbage-collection --no-garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-NOGC + +# RUN: llvm-dwarfutil %t.o --tombstone=universal - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC + +# CHECK: DW_TAG_compile_unit +# CHECK: DW_AT_name{{.*}}"CU1" +# CHECK: DW_TAG_class_type +# CHECK: DW_AT_name{{.*}}"class1" +# CHECK-GC-NOT: DW_TAG_class_type +# CHECK-GC-NOT: "class2" +# CHECK-GC-NOT: "class3" +# CHECK-NOGC: DW_TAG_class_type +# CHECK-NOGC: "class2" +# CHECK-NOGC: "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-GC-NOT: DW_TAG_subprogram +# CHECK-GC-NOT: "foo2" +# CHECK-NOGC: DW_TAG_subprogram +# CHECK-NOGC: "foo2" + + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000000010 + Content: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +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: 0x1b + - 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-exec.test b/llvm/test/tools/llvm-dwarfutil/ELF/gc-exec.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/gc-exec.test @@ -0,0 +1,151 @@ +## This test checks that debug info related to deleted code (marked with +## tombstone=exec) is removed. + +# RUN: yaml2obj %s -o %t.o + +# RUN: llvm-dwarfutil --tombstone=exec --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s +# RUN: llvm-dwarfutil --tombstone=universal --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s + +# 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 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000000010 + Content: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +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: 0x1b + - 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-func-overlapping-address-ranges.test b/llvm/test/tools/llvm-dwarfutil/ELF/gc-func-overlapping-address-ranges.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/gc-func-overlapping-address-ranges.test @@ -0,0 +1,147 @@ +## This test checks that overlapping function address ranges +## are ignored (i.e. left unchanged) by --garbage-collection +## optimisation. "Overlapped address range" warning should +## be displayed. + +# RUN: yaml2obj %s -o %t.o +# RUN: llvm-dwarfutil --garbage-collection %t.o %t1 2>&1 | FileCheck --check-prefix=CHECK-WARNING %s +# RUN: llvm-dwarfdump -a %t1 | FileCheck %s + +# CHECK-WARNING: warning: {{.*}}: Overlapping address range [0x1004, 0x100B]. Range will be discarded. + +# CHECK: DW_TAG_compile_unit +# CHECK: DW_AT_name{{.*}}"CU1" +# CHECK: DW_TAG_class_type +# CHECK: DW_AT_name{{.*}}"class1" +# CHECK: DW_TAG_class_type +# CHECK: "class2" +# 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: DW_TAG_subprogram +# CHECK: "foo2" +# CHECK: DW_AT_low_pc{{.*}}0x0000000000001004 +# CHECK: DW_AT_high_pc{{.*}}0x000000000000100b +# CHECK: DW_AT_type{{.*}}"class2" + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000000010 + Content: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +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: 0x1b + - AbbrCode: 3 + Values: + - CStr: class1 + - AbbrCode: 4 + Values: + - Value: 0x00000056 + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class2 + - AbbrCode: 4 + Values: + - Value: 0x00000056 + - 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: 0x1004 + - Value: 0x7 + - 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,151 @@ +## This test checks that debug info related to deleted code (marked with +## tombstone=maxpc) is removed. + +## RUN: yaml2obj %s -o %t.o + +# RUN: llvm-dwarfutil --tombstone=maxpc --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s +# RUN: llvm-dwarfutil --tombstone=universal --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s + +# 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 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000000010 + Content: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +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: 0x1b + - 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/gc-no-garbage.test b/llvm/test/tools/llvm-dwarfutil/ELF/gc-no-garbage.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/gc-no-garbage.test @@ -0,0 +1,143 @@ +## This test checks that debug info removal optimization (--garbage-collection) +## does not affect files which do not have dead debug info. + +# RUN: yaml2obj %s -o %t.o +# RUN: llvm-dwarfutil --tombstone=maxpc --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s + +# CHECK: DW_TAG_compile_unit +# CHECK: DW_AT_name{{.*}}"CU1" +# CHECK: DW_TAG_class_type +# CHECK: DW_AT_name{{.*}}"class1" +# CHECK: DW_TAG_class_type +# CHECK: "class2" +# 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: DW_TAG_subprogram +# CHECK: "foo2" +# CHECK: DW_AT_low_pc{{.*}}0x0000000000001010 +# CHECK: DW_AT_high_pc{{.*}}0x000000000000101b +# CHECK: DW_AT_type{{.*}}"class2" + + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000000010 + Content: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +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: 0x1b + - AbbrCode: 3 + Values: + - CStr: class1 + - AbbrCode: 4 + Values: + - Value: 0x00000056 + - CStr: member1 + - AbbrCode: 0 + - AbbrCode: 3 + Values: + - CStr: class2 + - AbbrCode: 4 + Values: + - Value: 0x00000056 + - 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: 0x1010 + - Value: 0xb + - Value: 0x00000040 + - AbbrCode: 0 +... diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/gc-unit-overlapping-address-ranges.test b/llvm/test/tools/llvm-dwarfutil/ELF/gc-unit-overlapping-address-ranges.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/gc-unit-overlapping-address-ranges.test @@ -0,0 +1,247 @@ +## This test checks that overlapping compile unit address ranges +## are ignored (i.e. left unchanged) by --garbage-collection +## optimisation. + +# RUN: yaml2obj %s -o %t.o +# RUN: llvm-dwarfutil --garbage-collection %t.o %t1 +# RUN: llvm-dwarfdump -a %t1 | FileCheck %s + +# CHECK: DW_TAG_compile_unit +# CHECK: DW_AT_name{{.*}}"CU1" +# CHECK: DW_TAG_class_type +# CHECK: DW_AT_name{{.*}}"class1" +# CHECK: DW_TAG_subprogram +# CHECK: DW_AT_name{{.*}}"foo1" +# CHECK: DW_AT_low_pc{{.*}}0x0000000000001000 +# CHECK: DW_AT_high_pc{{.*}}0x0000000000001010 +# CHECK: DW_TAG_subprogram +# CHECK: DW_AT_name{{.*}}"foo2" +# CHECK: DW_AT_low_pc{{.*}}0x0000000000001000 +# CHECK: DW_AT_high_pc{{.*}}0x0000000000001010 +# CHECK: DW_AT_type{{.*}}"class2" + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000000010 + Content: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +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 + - 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: 0x1b + - 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: 0 + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: by_hand + - Value: 0x04 + - CStr: CU1 + - Value: 0x1000 + - Value: 0x1b + - 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: foo2 + - Value: 0x1000 + - Value: 0x10 + - Value: 0x00000040 + - 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,343 @@ +## This test checks debug info for the case when one compilation unit +## contains a forward declaration of a type and another compilation unit +## contains a definition for that type. The result should contain +## the original definition and the declaration without modifications. + +# RUN: yaml2obj %s -o %t.o +# RUN: llvm-dwarfutil %t.o - | llvm-dwarfdump -a - | FileCheck %s + +## 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 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000000010 + Content: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + - Name: .text2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x2000 + AddressAlign: 0x0000000000000010 + Content: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +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: 0x1b + - 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: 0x1b + - 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/verbose.test b/llvm/test/tools/llvm-dwarfutil/ELF/verbose.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/ELF/verbose.test @@ -0,0 +1,151 @@ +## This test checks output of llvm-dwarfutil when verbose mode +## is enabled. + +# RUN: yaml2obj %s -o %t.o + +## Check verbose output. +# RUN: llvm-dwarfutil %t.o %t1 -j 1 --verbose 2>&1 | FileCheck %s + +## Check warning displayed in multi-thread mode. +# RUN: llvm-dwarfutil %t.o %t1 --verbose 2>&1 | FileCheck %s --check-prefix=CHECK-THREAD-WARNING + +## Check warning displayed in multi-thread mode (--num-threads set explicitly). +# RUN: llvm-dwarfutil %t.o %t1 --verbose --num-threads 10 2>&1 | FileCheck %s --check-prefix=CHECK-THREAD-WARNING + +## Check warning displayed in multi-thread mode (use alias of --num-threads). +# RUN: llvm-dwarfutil %t.o %t1 --verbose -j 10 2>&1 | FileCheck %s --check-prefix=CHECK-THREAD-WARNING + +## Check verbose output. +# RUN: llvm-dwarfutil %t.o %t1 -j 1 --verbose --verify 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-VERIFY + +# CHECK-NOT: warning: --num-threads set to 1 because verbose mode is specified +# CHECK: Do garbage collection for debug info ... +# CHECK-THREAD-WARNING: warning: --num-threads set to 1 because verbose mode is specified +# CHECK-VERIFY: Verifying DWARF... + +--- !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/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,151 @@ +## This test checks that debug info contained in the source file is properly +## verified by --verify after copying. If --no-garbage-collection is +## specified then the verification should fail, otherwise the verification +## should succeed. + +# RUN: yaml2obj %s -o %t.o + +## Verify resulting debug info after --garbage-collection optimisation. +# RUN: llvm-dwarfutil %t.o %t1 --verify + +## Verify separate debug file after --garbage-collection optimisation. +# RUN: llvm-dwarfutil %t.o --separate-debug-file %t1 --verify + +## Verify not optimised resulting debug info. +# RUN: not llvm-dwarfutil --no-garbage-collection %t.o %t1 --verify 2>&1 | FileCheck %s + +## Verify not optimised resulting separate debug file. +# RUN: not llvm-dwarfutil --no-garbage-collection %t.o --separate-debug-file %t1 --verify 2>&1 | FileCheck %s + +## Check that verification is disabled when destination is stdout. +# RUN: llvm-dwarfutil %t.o - --verify 2>&1 | FileCheck %s --check-prefix=CHECK-STDOUT + +# CHECK: error: output verification failed +# CHECK-STDOUT: warning: verification skipped because writing to stdout + +--- !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/error-invalid-format.test b/llvm/test/tools/llvm-dwarfutil/error-invalid-format.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/error-invalid-format.test @@ -0,0 +1,6 @@ +## This test checks that llvm-dwarfutil displays an error message +## if an input file of unsupported format was specified. + +# RUN: not llvm-dwarfutil %s - 2>&1 | FileCheck %s + +# CHECK: error-invalid-format.test: error: The file was not recognized as a valid object file diff --git a/llvm/test/tools/llvm-dwarfutil/error-no-gc-odr.test b/llvm/test/tools/llvm-dwarfutil/error-no-gc-odr.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/error-no-gc-odr.test @@ -0,0 +1,6 @@ +## This test checks the error message displayed if ODR deduplication +## is enabled while debug info garbage collection is disabled. + +# RUN: not llvm-dwarfutil --no-garbage-collection --odr-deduplication - - 2>&1 | FileCheck --check-prefix CHECK %s + +# CHECK: error: cannot use --odr-deduplication without --garbage-collection diff --git a/llvm/test/tools/llvm-dwarfutil/error-no-input-file.test b/llvm/test/tools/llvm-dwarfutil/error-no-input-file.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/error-no-input-file.test @@ -0,0 +1,6 @@ +## This test checks the error message displayed if +## an input file does not exist. + +# RUN: not llvm-dwarfutil not-existed not-existed 2>&1 | FileCheck %s + +# CHECK: not-existed: error: {{[Nn]}}o such file or directory diff --git a/llvm/test/tools/llvm-dwarfutil/error-positional-args.test b/llvm/test/tools/llvm-dwarfutil/error-positional-args.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/error-positional-args.test @@ -0,0 +1,8 @@ +## This test checks the error message displayed if an incorrect +## number of positional arguments is specified. + +# RUN: not llvm-dwarfutil - 2>&1 | FileCheck --check-prefix CHECK1 %s +# RUN: not llvm-dwarfutil - - - 2>&1 | FileCheck --check-prefix CHECK3 %s + +# CHECK1: error: exactly two positional arguments expected, 1 provided +# CHECK3: error: exactly two positional arguments expected, 3 provided diff --git a/llvm/test/tools/llvm-dwarfutil/error-unknown-option.test b/llvm/test/tools/llvm-dwarfutil/error-unknown-option.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/error-unknown-option.test @@ -0,0 +1,6 @@ +## This test checks the error message displayed if an unknown +## option is specified. + +# RUN: not llvm-dwarfutil --unknown-option 2>&1 | FileCheck %s + +# CHECK: error: unknown option: --unknown-option diff --git a/llvm/test/tools/llvm-dwarfutil/error-unknown-tombstone.test b/llvm/test/tools/llvm-dwarfutil/error-unknown-tombstone.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfutil/error-unknown-tombstone.test @@ -0,0 +1,6 @@ +## This test checks the error message diplayed if an incorrect +## tombstone value is specified. + +# RUN: not llvm-dwarfutil --tombstone=unknown - - 2>&1 | FileCheck --check-prefix CHECK %s + +# CHECK: error: unknown tombstone value: 'unknown' 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,25 @@ +## This test checks the help message of llvm-dwarfutil. + +# RUN: llvm-dwarfutil | FileCheck %s +# RUN: llvm-dwarfutil -h | FileCheck %s +# RUN: llvm-dwarfutil --help | FileCheck %s + +# CHECK: OVERVIEW: llvm-dwarfutil is a tool to copy and manipulate debug info +# CHECK: USAGE: {{.*}}llvm-dwarfutil{{.*}} [options] +# CHECK: OPTIONS: +# CHECK: --garbage-collection +# CHECK: --help +# CHECK: -h +# CHECK: -j +# CHECK: --no-garbage-collection +# CHECK: --no-odr-deduplication +# CHECK: --no-odr +# CHECK: --no-separate-debug-file +# CHECK: --num-threads +# CHECK: --odr-deduplication +# CHECK: --separate-debug-file +# CHECK: --tombstone +# CHECK: --verbose +# CHECK: --verify +# CHECK: --version +# CHECK: -V 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,27 @@ +set(LLVM_TARGET_DEFINITIONS Options.td) +tablegen(LLVM Options.inc -gen-opt-parser-defs) +add_public_tablegen_target(DwarfutilTableGen) + +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + DebugInfoDWARF + DWARFLinker + MC + ObjCopy + Object + Option + Support + Target + AllTargetsCodeGens + AllTargetsDescs + AllTargetsInfos + ) + +add_llvm_tool(llvm-dwarfutil + llvm-dwarfutil.cpp + DebugInfoLinker.cpp + + DEPENDS + intrinsics_gen + ${tablegen_deps} + ) 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,26 @@ +//===- DebugInfoLinker.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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" + +namespace llvm { +namespace dwarfutil { + +bool linkDebugInfo(object::ObjectFile &file, const Options &Options, + raw_pwrite_stream &OutStream); + +} // end of namespace dwarfutil +} // end of namespace llvm + +#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,270 @@ +//=== DebugInfoLinker.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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 + +namespace llvm { +namespace dwarfutil { + +// ObjFileAddressMap allows to check whether specified DIE referencing +// dead addresses. It uses tombstone values to determine dead addresses. +// The concrete values of tombstone constants were discussed in +// https://reviews.llvm.org/D81784 and https://reviews.llvm.org/D84825. +// So we use following values as indicators of dead addresses: +// +// bfd: (LowPC == 0) or (LowPC == 1 and HighPC == 1 and DWARF v4 (or less)) +// or ([LowPC, HighPC] is not inside address ranges of .text sections). +// +// maxpc: (LowPC == -1) or (LowPC == -2 and DWARF v4 (or less)) +// That value is assumed to be compatible with +// http://www.dwarfstd.org/ShowIssue.php?issue=200609.1 +// +// exec: [LowPC, HighPC] is not inside address ranges of .text sections +// +// universal: maxpc and bfd +class ObjFileAddressMap : public AddressesMap { +public: + ObjFileAddressMap(DWARFContext &Context, const Options &Options, + object::ObjectFile &ObjFile) + : Opts(Options) { + // Remember addresses of existing text sections. + for (const object::SectionRef &Sect : ObjFile.sections()) { + if (!Sect.isText()) + continue; + const uint64_t Size = Sect.getSize(); + if (Size == 0) + continue; + const uint64_t StartAddr = Sect.getAddress(); + TextAddressRanges[{StartAddr}] = {StartAddr + Size, 0}; + } + + // Check CU address ranges for tombstone value. + 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())) + DWARFAddressRanges[{Range.LowPC}] = {Range.HighPC, 0}; + } + } + } + } + + // should be renamed into has valid address ranges + bool hasValidRelocs() override { return !DWARFAddressRanges.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, DIE.getDwarfUnit()->getVersion(), + 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), U->getVersion(), + 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 DWARFAddressRanges; }; + + void clear() override { DWARFAddressRanges.clear(); } + + llvm::Expected relocateIndexedAddr(uint64_t, uint64_t) override { + // should not be called. + return object::createError("no relocations in linked binary"); + } + +protected: + // returns true if specified address range is inside address ranges + // of executable sections. + bool isInsideExecutableSectionsAddressRange(uint64_t LowPC, + Optional HighPC) { + auto Range = TextAddressRanges.lower_bound(LowPC); + if ((Range == TextAddressRanges.end() || Range->first != LowPC) && + Range != TextAddressRanges.begin()) + --Range; + + if (Range != TextAddressRanges.end() && Range->first <= LowPC && + (HighPC ? Range->second.HighPC >= HighPC + : Range->second.HighPC >= LowPC)) + return true; + + return false; + } + + uint64_t isBFDDeadAddressRange(uint64_t LowPC, Optional HighPC, + uint16_t Version) { + if (LowPC == 0) + return true; + + if ((Version <= 4) && HighPC && (LowPC == 1 && *HighPC == 1)) + return true; + + return !isInsideExecutableSectionsAddressRange(LowPC, HighPC); + } + + uint64_t isMAXPCDeadAddressRange(uint64_t LowPC, Optional HighPC, + uint16_t Version, uint8_t AddressByteSize) { + if (Version <= 4 && HighPC) { + if (LowPC == (dwarf::computeTombstoneAddress(AddressByteSize) - 1)) + return true; + } else if (LowPC == dwarf::computeTombstoneAddress(AddressByteSize)) + return true; + + if (!isInsideExecutableSectionsAddressRange(LowPC, HighPC)) + warning("Address referencing invalid text section is not marked with " + "tombstone value"); + + return false; + } + + bool isDeadAddressRange(uint64_t LowPC, Optional 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); + case TombstoneKind::Exec: + return !isInsideExecutableSectionsAddressRange(LowPC, HighPC); + } + + llvm_unreachable("Unknown tombstone value"); + } + + bool isDeadAddress(uint64_t LowPC, uint16_t Version, TombstoneKind Tombstone, + uint8_t AddressByteSize) { + return isDeadAddressRange(LowPC, None, Version, Tombstone, AddressByteSize); + } + +private: + RangesTy DWARFAddressRanges; + RangesTy TextAddressRanges; + const Options &Opts; +}; + +bool linkDebugInfo(object::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, File); + + 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 +} // end of namespace llvm 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,47 @@ +//===- Error.h --------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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" + +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 +} // end of namespace llvm + +#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,46 @@ +//===- Options.h ------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_DWARFUTIL_OPTIONS_H +#define LLVM_TOOLS_LLVM_DWARFUTIL_OPTIONS_H + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" + +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 + Exec, /// match with address range of executable sections. +}; + +struct Options { + std::string InputFileName; + std::string OutputFileName; + bool DoGarbageCollection = false; + bool DoODRDeduplication = false; + bool BuildSeparateDebugFile = false; + TombstoneKind Tombstone = TombstoneKind::Universal; + bool Verbose = false; + int NumThreads = 0; + bool Verify = false; + + std::string getSeparateDebugFileName() const { + return OutputFileName + ".debug"; + } +}; + +} // namespace dwarfutil +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_DWARFUTIL_OPTIONS_H diff --git a/llvm/tools/llvm-dwarfutil/Options.td b/llvm/tools/llvm-dwarfutil/Options.td new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-dwarfutil/Options.td @@ -0,0 +1,59 @@ +include "llvm/Option/OptParser.td" + +multiclass BB { + def NAME: Flag<["--"], name>, HelpText; + def no_ # NAME: Flag<["--"], "no-" # name>, HelpText; +} + +def help : Flag<["--"], "help">, + HelpText<"Prints this help output">; + +def h : Flag<["-"], "h">, + Alias, + HelpText<"Alias for --help">; + +defm odr_deduplication : BB<"odr-deduplication", + "Do ODR deduplication for debug types(default)", + "Don`t do ODR deduplication for debug types">; + +def odr : Flag<["--"], "odr">, + Alias, + HelpText<"Alias for --odr-deduplication">; + +def no_odr : Flag<["--"], "no-odr">, + Alias, + HelpText<"Alias for --no-odr-deduplication">; + +defm garbage_collection : BB<"garbage-collection", + "Do garbage collection for debug info(default)", + "Don`t do garbage collection for debug info">; + +defm separate_debug_file : BB<"separate-debug-file", + "Create two output files: file w/o debug tables and file with debug tables", + "Create single output file, containing debug tables(default)">; + +def tombstone: Separate<["--", "-"], "tombstone">, + MetaVarName<"[bfd,maxpc,text,universal]">, + HelpText<"Tombstone value used as a marker of invalid address(default: universal)">; +def: Joined<["--", "-"], "tombstone=">, Alias; + +def threads: Separate<["--", "-"], "num-threads">, + MetaVarName<"">, + HelpText<"Number of available threads for multi-threaded execution. Defaults to the number of cores on the current machine">; + +def: Separate<["-"], "j">, + Alias, + HelpText<"Alias for --num-threads">; + +def verbose : Flag<["--"], "verbose">, + HelpText<"Enable verbose logging">; + +def verify : Flag<["--"], "verify">, + HelpText<"Run the DWARF verifier on the resulting debug info">; + +def version : Flag<["--"], "version">, + HelpText<"Print the version and exit">; + +def V : Flag<["-"], "V">, + Alias, + HelpText<"Alias for --version">; 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,516 @@ +//=== llvm-dwarfutil.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetSelect.h" + +using namespace llvm; +using namespace object; + +namespace { +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Options.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Options.inc" +#undef PREFIX + +const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Options.inc" +#undef OPTION +}; + +class DwarfutilOptTable : public opt::OptTable { +public: + DwarfutilOptTable() : OptTable(InfoTable) {} +}; +} // namespace + +namespace llvm { +namespace dwarfutil { + +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(opt::InputArgList &Args, Options &Options) { + auto UnknownArgs = Args.filtered(OPT_UNKNOWN); + if (!UnknownArgs.empty()) + return createError("unknown option: " + + (*UnknownArgs.begin())->getSpelling()); + + std::vector InputFiles = Args.getAllArgValues(OPT_INPUT); + if (InputFiles.size() != 2) + return createError(formatv("exactly two positional arguments expected, " + "{0} provided", + InputFiles.size())); + + Options.InputFileName = InputFiles[0]; + Options.OutputFileName = InputFiles[1]; + + Options.BuildSeparateDebugFile = + Args.hasFlag(OPT_separate_debug_file, OPT_no_separate_debug_file, false); + Options.DoODRDeduplication = + Args.hasFlag(OPT_odr_deduplication, OPT_no_odr_deduplication, true); + Options.DoGarbageCollection = + Args.hasFlag(OPT_garbage_collection, OPT_no_garbage_collection, true); + Options.Verbose = Args.hasArg(OPT_verbose); + Options.Verify = Args.hasArg(OPT_verify); + + if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads)) + Options.NumThreads = atoi(NumThreads->getValue()); + else + Options.NumThreads = 0; // Use all available hardware threads + + if (opt::Arg *Tombstone = Args.getLastArg(OPT_tombstone)) { + StringRef S = Tombstone->getValue(); + if (S == "bfd") + Options.Tombstone = TombstoneKind::BFD; + else if (S == "maxpc") + Options.Tombstone = TombstoneKind::MaxPC; + else if (S == "universal") + Options.Tombstone = TombstoneKind::Universal; + else if (S == "exec") + Options.Tombstone = TombstoneKind::Exec; + else + return createError("unknown tombstone value: '" + S + "'"); + } + + if (Options.Verbose) { + if (Options.NumThreads != 1) + warning("--num-threads set to 1 because verbose mode is specified"); + + Options.NumThreads = 1; + } + + if (Options.DoODRDeduplication && Args.hasArg(OPT_odr_deduplication) && + !Options.DoGarbageCollection) + return createError( + "cannot use --odr-deduplication without --garbage-collection"); + + if (Options.BuildSeparateDebugFile && Options.OutputFileName == "-") + return createError( + "writing to stdout. --separate-debug-file should be disabled"); + + return Error::success(); +} + +static Error setConfigToAddNewDebugSections(objcopy::ConfigManager &Config, + ObjectFile &ObjFile) { + // Add new debug sections. + for (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; + } + + std::string FileName = Opts.BuildSeparateDebugFile + ? Opts.getSeparateDebugFileName() + : Opts.OutputFileName; + Expected> BinOrErr = createBinary(FileName); + 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: '" + FileName + "'"); +} + +class raw_crc_ostream : public raw_ostream { +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; } + +protected: + 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(); } +}; + +static Expected saveSeparateDebugInfo(const Options &Opts, + ObjectFile &InputFile) { + objcopy::ConfigManager Config; + std::string OutputFilename = Opts.getSeparateDebugFileName(); + Config.Common.InputFilename = Opts.InputFileName; + Config.Common.OutputFilename = OutputFilename; + 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; + std::string OutputFilename = Opts.getSeparateDebugFileName(); + Config.Common.InputFilename = Opts.InputFileName; + Config.Common.OutputFilename = OutputFilename; + 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 +} // end of namespace llvm + +int main(int Argc, char const *Argv[]) { + using namespace dwarfutil; + + InitLLVM X(Argc, Argv); + ToolName = Argv[0]; + + // Parse arguments. + DwarfutilOptTable T; + unsigned MAI; + unsigned MAC; + ArrayRef ArgsArr = makeArrayRef(Argv + 1, Argc - 1); + opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC); + + if (Args.hasArg(OPT_help) || Args.size() == 0) { + T.printHelp( + outs(), (ToolName + " [options] ").c_str(), + "llvm-dwarfutil is a tool to copy and manipulate debug info", false); + return EXIT_SUCCESS; + } + + if (Args.hasArg(OPT_version)) { + cl::PrintVersionMessage(); + return EXIT_SUCCESS; + } + + Options Opts; + if (Error Err = validateAndSetOptions(Args, Opts)) { + error(dwarfutil::ToolName, std::move(Err)); + return EXIT_FAILURE; + } + + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllTargetInfos(); + InitializeAllAsmPrinters(); + InitializeAllAsmParsers(); + + ErrorOr> BuffOrErr = + MemoryBuffer::getFileOrSTDIN(Opts.InputFileName); + if (BuffOrErr.getError()) { + error(Opts.InputFileName, BuffOrErr.getError().message().c_str()); + return EXIT_FAILURE; + } + + Expected> BinOrErr = + object::createBinary(**BuffOrErr); + if (!BinOrErr) { + error(Opts.InputFileName, BinOrErr.takeError()); + return EXIT_FAILURE; + } + + if (!(*BinOrErr)->isObject()) { + error(Opts.InputFileName, "corrupted input file"); + return EXIT_FAILURE; + } + + if (Error Err = + applyCLOptions(Opts, *static_cast((*BinOrErr).get()))) { + error(Opts.InputFileName, std::move(Err)); + return EXIT_FAILURE; + } + + // TODO: Refactor restoreStatOnFile function out of llvm-objcopy and use it + // here. + // static Error restoreStatOnFile(StringRef OutFilename, + // const sys::fs::file_status &Stat, + // const ConfigManager &ConfigMgr); + BinOrErr->release(); + BuffOrErr->release(); + + if (Opts.Verify) { + Expected IsCorrect = verifyOutput(Opts); + if (!IsCorrect) { + error(Opts.InputFileName, IsCorrect.takeError()); + return EXIT_FAILURE; + } + + if (!*IsCorrect) { + error(Opts.InputFileName, "output verification failed"); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +}