diff --git a/llvm/include/llvm/CodeGen/DwarfStringPoolEntry.h b/llvm/include/llvm/CodeGen/DwarfStringPoolEntry.h --- a/llvm/include/llvm/CodeGen/DwarfStringPoolEntry.h +++ b/llvm/include/llvm/CodeGen/DwarfStringPoolEntry.h @@ -27,29 +27,34 @@ bool isIndexed() const { return Index != NotIndexed; } }; +/// DwarfStringPoolEntry with string keeping externally. +struct DwarfStringPoolEntryWithExtString : public DwarfStringPoolEntry { + StringRef String; +}; + /// DwarfStringPoolEntryRef: Dwarf string pool entry reference. /// /// Dwarf string pool entry keeps string value and its data. /// There are two variants how data are represented: /// -/// 1. By value - StringMapEntry. -/// 2. By pointer - StringMapEntry. +/// 1. String data in pool - StringMapEntry. +/// 2. External string data - DwarfStringPoolEntryWithExtString. /// -/// The "By pointer" variant allows for reducing memory usage for the case -/// when string pool entry does not have data: it keeps the null pointer -/// and so no need to waste space for the full DwarfStringPoolEntry. -/// It is recommended to use "By pointer" variant if not all entries -/// of dwarf string pool have corresponding DwarfStringPoolEntry. +/// The external data variant allows reducing memory usage for the case +/// when string pool entry does not have data: string entry does not +/// keep any data and so no need to waste space for the full +/// DwarfStringPoolEntry. It is recommended to use external variant if not all +/// entries of dwarf string pool have corresponding DwarfStringPoolEntry. class DwarfStringPoolEntryRef { /// Pointer type for "By value" string entry. using ByValStringEntryPtr = const StringMapEntry *; - /// Pointer type for "By pointer" string entry. - using ByPtrStringEntryPtr = const StringMapEntry *; + /// Pointer type for external string entry. + using ExtStringEntryPtr = const DwarfStringPoolEntryWithExtString *; /// Pointer to the dwarf string pool Entry. - PointerUnion MapEntry = nullptr; + PointerUnion MapEntry = nullptr; public: DwarfStringPoolEntryRef() = default; @@ -61,10 +66,8 @@ /// ASSUMPTION: DwarfStringPoolEntryRef keeps pointer to \p Entry, /// thus specified entry mustn`t be reallocated. - DwarfStringPoolEntryRef(const StringMapEntry &Entry) - : MapEntry(&Entry) { - assert(cast(MapEntry)->second != nullptr); - } + DwarfStringPoolEntryRef(const DwarfStringPoolEntryWithExtString &Entry) + : MapEntry(&Entry) {} explicit operator bool() const { return !MapEntry.isNull(); } @@ -88,7 +91,7 @@ if (isa(MapEntry)) return cast(MapEntry)->first(); - return cast(MapEntry)->first(); + return cast(MapEntry)->String; } /// \returns the entire string pool entry for convenience. @@ -96,7 +99,7 @@ if (isa(MapEntry)) return cast(MapEntry)->second; - return *cast(MapEntry)->second; + return *cast(MapEntry); } bool operator==(const DwarfStringPoolEntryRef &X) const { 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 @@ -233,9 +233,11 @@ /// and its address map. class DWARFFile { public: + using UnloadCallbackTy = std::function; DWARFFile(StringRef Name, std::unique_ptr Dwarf, std::unique_ptr Addresses, - const std::vector &Warnings) + const std::vector &Warnings, + UnloadCallbackTy = nullptr) : FileName(Name), Dwarf(std::move(Dwarf)), Addresses(std::move(Addresses)), Warnings(Warnings) {} diff --git a/llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h b/llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h --- a/llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h +++ b/llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h @@ -10,8 +10,10 @@ #define LLVM_DWARFLINKERPARALLEL_ADDRESSESMAP_H #include "llvm/ADT/AddressRanges.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/DebugInfo/DWARF/DWARFExpression.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include namespace llvm { @@ -62,6 +64,108 @@ /// Erases all data. virtual void clear() = 0; + + /// This function checks whether variable has DWARF expression containing + /// operation referencing live address(f.e. DW_OP_addr, DW_OP_addrx...). + /// \returns first is true if the expression has an operation referencing an + /// address. + /// second is the relocation adjustment value if the live address is + /// referenced. + std::pair> + getVariableRelocAdjustment(const DWARFDie &DIE) { + assert((DIE.getTag() == dwarf::DW_TAG_variable || + DIE.getTag() == dwarf::DW_TAG_constant) && + "Wrong type of input die"); + + const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); + + // Check if DIE has DW_AT_location attribute. + DWARFUnit *U = DIE.getDwarfUnit(); + std::optional LocationIdx = + Abbrev->findAttributeIndex(dwarf::DW_AT_location); + if (!LocationIdx) + return std::make_pair(false, std::nullopt); + + // Get offset to the DW_AT_location attribute. + uint64_t AttrOffset = + Abbrev->getAttributeOffsetFromIndex(*LocationIdx, DIE.getOffset(), *U); + + // Get value of the DW_AT_location attribute. + std::optional LocationValue = + Abbrev->getAttributeValueFromOffset(*LocationIdx, AttrOffset, *U); + if (!LocationValue) + return std::make_pair(false, std::nullopt); + + // Check that DW_AT_location attribute is of 'exprloc' class. + // Handling value of location expressions for attributes of 'loclist' + // class is not implemented yet. + std::optional> Expr = LocationValue->getAsBlock(); + if (!Expr) + return std::make_pair(false, std::nullopt); + + // Parse 'exprloc' expression. + DataExtractor Data(toStringRef(*Expr), U->getContext().isLittleEndian(), + U->getAddressByteSize()); + DWARFExpression Expression(Data, U->getAddressByteSize(), + U->getFormParams().Format); + + bool HasLocationAddress = false; + uint64_t CurExprOffset = 0; + for (DWARFExpression::iterator It = Expression.begin(); + It != Expression.end(); ++It) { + DWARFExpression::iterator NextIt = It; + ++NextIt; + + const DWARFExpression::Operation &Op = *It; + switch (Op.getCode()) { + case dwarf::DW_OP_const2u: + case dwarf::DW_OP_const4u: + case dwarf::DW_OP_const8u: + case dwarf::DW_OP_const2s: + case dwarf::DW_OP_const4s: + case dwarf::DW_OP_const8s: + if (NextIt == Expression.end() || !isTlsAddressCode(NextIt->getCode())) + break; + [[fallthrough]]; + case dwarf::DW_OP_addr: { + HasLocationAddress = true; + // Check relocation for the address. + if (std::optional RelocAdjustment = + getExprOpAddressRelocAdjustment(*U, Op, + AttrOffset + CurExprOffset, + AttrOffset + Op.getEndOffset())) + return std::make_pair(HasLocationAddress, *RelocAdjustment); + } break; + case dwarf::DW_OP_constx: + case dwarf::DW_OP_addrx: { + HasLocationAddress = true; + if (std::optional AddressOffset = + DIE.getDwarfUnit()->getIndexedAddressOffset( + Op.getRawOperand(0))) { + // Check relocation for the address. + if (std::optional RelocAdjustment = + getExprOpAddressRelocAdjustment( + *U, Op, *AddressOffset, + *AddressOffset + + DIE.getDwarfUnit()->getAddressByteSize())) + return std::make_pair(HasLocationAddress, *RelocAdjustment); + } + } break; + default: { + // Nothing to do. + } break; + } + CurExprOffset = Op.getEndOffset(); + } + + return std::make_pair(HasLocationAddress, std::nullopt); + } + +protected: + inline bool isTlsAddressCode(uint8_t DW_OP_Code) { + return DW_OP_Code == dwarf::DW_OP_form_tls_address || + DW_OP_Code == dwarf::DW_OP_GNU_push_tls_address; + } }; } // end of namespace dwarflinker_parallel diff --git a/llvm/include/llvm/DWARFLinkerParallel/DWARFFile.h b/llvm/include/llvm/DWARFLinkerParallel/DWARFFile.h --- a/llvm/include/llvm/DWARFLinkerParallel/DWARFFile.h +++ b/llvm/include/llvm/DWARFLinkerParallel/DWARFFile.h @@ -30,14 +30,7 @@ DWARFFile(StringRef Name, std::unique_ptr Dwarf, std::unique_ptr Addresses, const std::vector &Warnings, - UnloadCallbackTy UnloadFunc = nullptr) - : FileName(Name), Dwarf(std::move(Dwarf)), - Addresses(std::move(Addresses)), Warnings(Warnings), - UnloadFunc(UnloadFunc) { - if (this->Dwarf) - Endianess = this->Dwarf->isLittleEndian() ? support::endianness::little - : support::endianness::big; - } + UnloadCallbackTy UnloadFunc = nullptr); /// Object file name. StringRef FileName; @@ -51,9 +44,6 @@ /// Warnings for object file. const std::vector &Warnings; - /// Endiannes of source DWARF information. - support::endianness Endianess = support::endianness::little; - /// Callback to the module keeping object file to unload. UnloadCallbackTy UnloadFunc; diff --git a/llvm/include/llvm/DWARFLinkerParallel/StringPool.h b/llvm/include/llvm/DWARFLinkerParallel/StringPool.h --- a/llvm/include/llvm/DWARFLinkerParallel/StringPool.h +++ b/llvm/include/llvm/DWARFLinkerParallel/StringPool.h @@ -21,7 +21,7 @@ /// StringEntry keeps data of the string: the length, external offset /// and a string body which is placed right after StringEntry. -using StringEntry = StringMapEntry; +using StringEntry = StringMapEntry; class StringPoolEntryInfo { public: diff --git a/llvm/include/llvm/DWARFLinkerParallel/StringTable.h b/llvm/include/llvm/DWARFLinkerParallel/StringTable.h deleted file mode 100644 --- a/llvm/include/llvm/DWARFLinkerParallel/StringTable.h +++ /dev/null @@ -1,88 +0,0 @@ -//===- StringTable.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_DWARFLINKERPARALLEL_STRINGTABLE_H -#define LLVM_DWARFLINKERPARALLEL_STRINGTABLE_H - -#include "llvm/ADT/SmallVector.h" -#include "llvm/DWARFLinkerParallel/StringPool.h" - -namespace llvm { -namespace dwarflinker_parallel { - -using StringsVector = SmallVector; - -/// This class prepares strings for emission into .debug_str table: -/// translates string if necessary, assigns index and offset, keeps in order. -class StringTable { -public: - StringTable(StringPool &Strings, - std::function StringsTranslator) - : Strings(Strings), StringsTranslator(StringsTranslator) {} - ~StringTable() {} - - /// Add string to the vector of strings which should be emitted. - /// Translate input string if neccessary, assign index and offset. - /// \returns updated string entry. - StringEntry *add(StringEntry *String) { - // Translate string if necessary. - if (StringsTranslator) - String = Strings.insert(StringsTranslator(String->first())).first; - - // Store String for emission and assign index and offset. - if (String->getValue() == nullptr) { - DwarfStringPoolEntry *NewEntry = - Strings.getAllocatorRef().Allocate(); - - NewEntry->Symbol = nullptr; - NewEntry->Index = StringEntriesForEmission.size(); - - if (StringEntriesForEmission.empty()) - NewEntry->Offset = 0; - else { - StringEntry *PrevString = StringEntriesForEmission.back(); - NewEntry->Offset = - PrevString->getValue()->Offset + PrevString->getKeyLength() + 1; - } - - String->getValue() = NewEntry; - StringEntriesForEmission.push_back(String); - } - - return String; - } - - /// Erase contents of StringsForEmission. - void clear() { StringEntriesForEmission.clear(); } - - /// Enumerate all strings in sequential order and call \p Handler for each - /// string. - void forEach(function_ref Handler) const { - for (const StringEntry *Entry : StringEntriesForEmission) - Handler(*Entry); - } - - std::function getTranslator() { - return StringsTranslator; - } - -protected: - /// List of strings for emission. - StringsVector StringEntriesForEmission; - - /// String pool for the translated strings. - StringPool &Strings; - - /// Translator for the strings. - std::function StringsTranslator; -}; - -} // end of namespace dwarflinker_parallel -} // end namespace llvm - -#endif // LLVM_DWARFLINKERPARALLEL_STRINGTABLE_H diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugMacro.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugMacro.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugMacro.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugMacro.h @@ -22,6 +22,7 @@ class DWARFDebugMacro { friend DwarfStreamer; + friend dwarflinker_parallel::CompileUnit; /// DWARFv5 section 6.3.1 Macro Information Header. enum HeaderFlagMask { 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 @@ -475,8 +475,10 @@ const DWARFExpression::Operation &Op = *It; switch (Op.getCode()) { + case dwarf::DW_OP_const2u: case dwarf::DW_OP_const4u: case dwarf::DW_OP_const8u: + case dwarf::DW_OP_const2s: case dwarf::DW_OP_const4s: case dwarf::DW_OP_const8s: if (NextIt == Expression.end() || !isTlsAddressCode(NextIt->getCode())) 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 @@ -97,8 +97,10 @@ ++NextIt; switch (It->getCode()) { + case dwarf::DW_OP_const2u: case dwarf::DW_OP_const4u: case dwarf::DW_OP_const8u: + case dwarf::DW_OP_const2s: case dwarf::DW_OP_const4s: case dwarf::DW_OP_const8s: if (NextIt == Expression.end() || diff --git a/llvm/lib/DWARFLinkerParallel/ArrayList.h b/llvm/lib/DWARFLinkerParallel/ArrayList.h new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerParallel/ArrayList.h @@ -0,0 +1,92 @@ +//===- ArrayList.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_LIB_DWARFLINKERPARALLEL_ARRAYLIST_H +#define LLVM_LIB_DWARFLINKERPARALLEL_ARRAYLIST_H + +#include "DWARFLinkerGlobalData.h" +#include "llvm/Support/PerThreadBumpPtrAllocator.h" + +namespace llvm { +namespace dwarflinker_parallel { + +/// This class is a simple list of T structures. It keeps elements as +/// pre-allocated groups to save memory for each element's next pointer. +/// It allocates internal data using specified per-thread BumpPtrAllocator. +template class ArrayList { +public: + /// Copy specified \p Item into the list. + T ¬eItem(const T &Item) { + assert(Allocator != nullptr); + + ItemsGroup *CurGroup = LastGroup; + + if (CurGroup == nullptr) { + // Allocate first ItemsGroup. + LastGroup = Allocator->Allocate(); + LastGroup->ItemsCount = 0; + LastGroup->Next = nullptr; + GroupsHead = LastGroup; + CurGroup = LastGroup; + } + + if (CurGroup->ItemsCount == ItemsGroupSize) { + // Allocate next ItemsGroup if current one is full. + LastGroup = Allocator->Allocate(); + LastGroup->ItemsCount = 0; + LastGroup->Next = nullptr; + CurGroup->Next = LastGroup; + CurGroup = LastGroup; + } + + // Copy item into the next position inside current ItemsGroup. + CurGroup->Items[CurGroup->ItemsCount] = Item; + return CurGroup->Items[CurGroup->ItemsCount++]; + } + + using ItemHandlerTy = function_ref; + + /// Enumerate all items and apply specified \p Handler to each. + void forEach(ItemHandlerTy Handler) { + for (ItemsGroup *CurGroup = GroupsHead; CurGroup != nullptr; + CurGroup = CurGroup->Next) { + for (size_t Idx = 0; Idx < CurGroup->ItemsCount; Idx++) { + Handler(CurGroup->Items[Idx]); + } + } + } + + /// Check whether list is empty. + bool empty() { return GroupsHead == nullptr; } + + /// Erase list. + void erase() { + GroupsHead = nullptr; + LastGroup = nullptr; + } + + void setAllocator(parallel::PerThreadBumpPtrAllocator *Allocator) { + this->Allocator = Allocator; + } + +protected: + struct ItemsGroup { + std::array Items; + ItemsGroup *Next = nullptr; + size_t ItemsCount = 0; + }; + + ItemsGroup *GroupsHead = nullptr; + ItemsGroup *LastGroup = nullptr; + parallel::PerThreadBumpPtrAllocator *Allocator = nullptr; +}; + +} // end of namespace dwarflinker_parallel +} // end namespace llvm + +#endif // LLVM_LIB_DWARFLINKERPARALLEL_ARRAYLIST_H diff --git a/llvm/lib/DWARFLinkerParallel/CMakeLists.txt b/llvm/lib/DWARFLinkerParallel/CMakeLists.txt --- a/llvm/lib/DWARFLinkerParallel/CMakeLists.txt +++ b/llvm/lib/DWARFLinkerParallel/CMakeLists.txt @@ -1,7 +1,12 @@ add_llvm_component_library(LLVMDWARFLinkerParallel + DependencyTracker.cpp + DIEAttributeCloner.cpp DWARFEmitterImpl.cpp + DWARFFile.cpp DWARFLinker.cpp + DWARFLinkerCompileUnit.cpp DWARFLinkerImpl.cpp + DWARFLinkerUnit.cpp OutputSections.cpp StringPool.cpp diff --git a/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.h b/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.h new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.h @@ -0,0 +1,143 @@ +//===- DIEAttributeCloner.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_LIB_DWARFLINKERPARALLEL_DIEATTRIBUTECLONER_H +#define LLVM_LIB_DWARFLINKERPARALLEL_DIEATTRIBUTECLONER_H + +#include "ArrayList.h" +#include "DIEGenerator.h" +#include "DWARFLinkerCompileUnit.h" +#include "DWARFLinkerGlobalData.h" + +namespace llvm { +namespace dwarflinker_parallel { + +/// This class creates clones of input DIE attributes. +/// It enumerates attributes of input DIE, creates clone for each +/// attribute, adds cloned attribute to the output DIE. +class DIEAttributeCloner { +public: + DIEAttributeCloner(DIE *OutDIE, CompileUnit &CU, + const DWARFDebugInfoEntry *InputDieEntry, + DIEGenerator &Generator, + std::optional FuncAddressAdjustment, + std::optional VarAddressAdjustment, + bool HasLocationExpressionAddress) + : OutDIE(OutDIE), CU(CU), DebugInfoOutputSection(CU.getSectionDescriptor( + DebugSectionKind::DebugInfo)), + InputDieEntry(InputDieEntry), Generator(Generator), + FuncAddressAdjustment(FuncAddressAdjustment), + VarAddressAdjustment(VarAddressAdjustment), + HasLocationExpressionAddress(HasLocationExpressionAddress) { + InputDIEIdx = CU.getDIEIndex(InputDieEntry); + } + + /// Clone attributes of input DIE. + void clone(); + + /// Create abbreviations for the output DIE after all attributes are cloned. + unsigned finalizeAbbreviations(bool HasChildrenToClone); + + /// Information gathered and exchanged between the various + /// clone*Attr helpers about the attributes of a particular DIE. + /// + /// Cannot be used concurrently. + struct AttributesInfo { + /// Names. + StringEntry *Name = nullptr; + StringEntry *MangledName = nullptr; + StringEntry *NameWithoutTemplate = nullptr; + + /// Does the DIE have a low_pc attribute? + bool HasLowPc = false; + + /// Is this DIE only a declaration? + bool IsDeclaration = false; + + /// Does the DIE have a ranges attribute? + bool HasRanges = false; + }; + + AttributesInfo AttrInfo; + +protected: + /// Clone string attribute. + size_t + cloneStringAttr(const DWARFFormValue &Val, + const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec); + + /// Clone attribute referencing another DIE. + size_t + cloneDieRefAttr(const DWARFFormValue &Val, + const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec); + + /// Clone scalar attribute. + size_t + cloneScalarAttr(const DWARFFormValue &Val, + const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec); + + /// Clone block or exprloc attribute. + size_t + cloneBlockAttr(const DWARFFormValue &Val, + const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec); + + /// Clone address attribute. + size_t + cloneAddressAttr(const DWARFFormValue &Val, + const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec); + + /// Returns true if attribute should be skipped. + bool + shouldSkipAttribute(DWARFAbbreviationDeclaration::AttributeSpec AttrSpec); + + /// Update patches offsets with the size of abbreviation number. + void + updatePatchesWithSizeOfAbbreviationNumber(unsigned SizeOfAbbreviationNumber) { + for (uint64_t *OffsetPtr : PatchesOffsets) + *OffsetPtr += SizeOfAbbreviationNumber; + } + + /// Output DIE. + DIE *OutDIE = nullptr; + + /// Compile unit for the output DIE. + CompileUnit &CU; + + /// .debug_info section descriptor. + SectionDescriptor &DebugInfoOutputSection; + + /// Input DIE entry. + const DWARFDebugInfoEntry *InputDieEntry = nullptr; + + /// Input DIE index. + uint32_t InputDIEIdx = 0; + + /// Output DIE generator. + DIEGenerator &Generator; + + /// Relocation adjustment for the function address ranges. + std::optional FuncAddressAdjustment; + + /// Relocation adjustment for the variable locations. + std::optional VarAddressAdjustment; + + /// Indicates whether InputDieEntry has an location attribute + /// containg address expression. + bool HasLocationExpressionAddress = false; + + /// Output offset after all attributes. + unsigned AttrOutOffset = 0; + + /// Patches for the cloned attributes. + OffsetsPtrVector PatchesOffsets; +}; + +} // end of namespace dwarflinker_parallel +} // end namespace llvm + +#endif // LLVM_LIB_DWARFLINKERPARALLEL_DIEATTRIBUTECLONER_H diff --git a/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.cpp b/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.cpp @@ -0,0 +1,539 @@ +//=== DIEAttributeCloner.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 "DIEAttributeCloner.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h" + +namespace llvm { +namespace dwarflinker_parallel { + +void DIEAttributeCloner::clone() { + DWARFUnit &U = CU.getOrigUnit(); + + // Extract and clone every attribute. + DWARFDataExtractor Data = U.getDebugInfoExtractor(); + + uint64_t Offset = InputDieEntry->getOffset(); + // Point to the next DIE (generally there is always at least a NULL + // entry after the current one). If this is a lone + // DW_TAG_compile_unit without any children, point to the next unit. + uint64_t NextOffset = (InputDIEIdx + 1 < U.getNumDIEs()) + ? U.getDIEAtIndex(InputDIEIdx + 1).getOffset() + : U.getNextUnitOffset(); + + // We could copy the data only if we need to apply a relocation to it. After + // testing, it seems there is no performance downside to doing the copy + // unconditionally, and it makes the code simpler. + SmallString<40> DIECopy(Data.getData().substr(Offset, NextOffset - Offset)); + Data = + DWARFDataExtractor(DIECopy, Data.isLittleEndian(), Data.getAddressSize()); + + // Modify the copy with relocated addresses. + CU.getContaingFile().Addresses->applyValidRelocs(DIECopy, Offset, + Data.isLittleEndian()); + + // Reset the Offset to 0 as we will be working on the local copy of + // the data. + Offset = 0; + + const auto *Abbrev = InputDieEntry->getAbbreviationDeclarationPtr(); + Offset += getULEB128Size(Abbrev->getCode()); + + // Set current output offset. + AttrOutOffset = OutDIE->getOffset(); + for (const auto &AttrSpec : Abbrev->attributes()) { + // Check whether current attribute should be skipped. + if (shouldSkipAttribute(AttrSpec)) { + DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, + U.getFormParams()); + continue; + } + + // Note patches for attributes referencing other sections. + switch (AttrSpec.Attr) { + case dwarf::DW_AT_stmt_list: { + DebugInfoOutputSection.notePatchWithOffsetUpdate( + DebugOffsetPatch{AttrOutOffset, &CU.getSectionDescriptor( + DebugSectionKind::DebugLine)}, + PatchesOffsets); + } break; + case dwarf::DW_AT_macros: { + DebugInfoOutputSection.notePatchWithOffsetUpdate( + DebugOffsetPatch{AttrOutOffset, &CU.getSectionDescriptor( + DebugSectionKind::DebugMacro)}, + PatchesOffsets); + } break; + case dwarf::DW_AT_macro_info: { + DebugInfoOutputSection.notePatchWithOffsetUpdate( + DebugOffsetPatch{AttrOutOffset, &CU.getSectionDescriptor( + DebugSectionKind::DebugMacinfo)}, + PatchesOffsets); + } break; + default: { + } break; + } + + DWARFFormValue Val = AttrSpec.getFormValue(); + Val.extractValue(Data, &Offset, U.getFormParams(), &U); + + // Clone current attribute. + switch (AttrSpec.Form) { + case dwarf::DW_FORM_strp: + case dwarf::DW_FORM_line_strp: + case dwarf::DW_FORM_string: + case dwarf::DW_FORM_strx: + case dwarf::DW_FORM_strx1: + case dwarf::DW_FORM_strx2: + case dwarf::DW_FORM_strx3: + case dwarf::DW_FORM_strx4: + AttrOutOffset += cloneStringAttr(Val, AttrSpec); + break; + case dwarf::DW_FORM_ref_addr: + case dwarf::DW_FORM_ref1: + case dwarf::DW_FORM_ref2: + case dwarf::DW_FORM_ref4: + case dwarf::DW_FORM_ref8: + case dwarf::DW_FORM_ref_udata: + AttrOutOffset += cloneDieRefAttr(Val, AttrSpec); + break; + case dwarf::DW_FORM_data1: + case dwarf::DW_FORM_data2: + case dwarf::DW_FORM_data4: + case dwarf::DW_FORM_data8: + case dwarf::DW_FORM_udata: + case dwarf::DW_FORM_sdata: + case dwarf::DW_FORM_sec_offset: + case dwarf::DW_FORM_flag: + case dwarf::DW_FORM_flag_present: + case dwarf::DW_FORM_rnglistx: + case dwarf::DW_FORM_loclistx: + case dwarf::DW_FORM_implicit_const: + AttrOutOffset += cloneScalarAttr(Val, AttrSpec); + break; + case dwarf::DW_FORM_block: + case dwarf::DW_FORM_block1: + case dwarf::DW_FORM_block2: + case dwarf::DW_FORM_block4: + case dwarf::DW_FORM_exprloc: + AttrOutOffset += cloneBlockAttr(Val, AttrSpec); + break; + case dwarf::DW_FORM_addr: + case dwarf::DW_FORM_addrx: + case dwarf::DW_FORM_addrx1: + case dwarf::DW_FORM_addrx2: + case dwarf::DW_FORM_addrx3: + case dwarf::DW_FORM_addrx4: + AttrOutOffset += cloneAddressAttr(Val, AttrSpec); + break; + default: + CU.warn("unsupported attribute form " + + dwarf::FormEncodingString(AttrSpec.Form) + + " in DieAttributeCloner::clone(). Dropping.", + InputDieEntry); + } + } +} + +bool DIEAttributeCloner::shouldSkipAttribute( + DWARFAbbreviationDeclaration::AttributeSpec AttrSpec) { + switch (AttrSpec.Attr) { + default: + return false; + case dwarf::DW_AT_low_pc: + case dwarf::DW_AT_high_pc: + case dwarf::DW_AT_ranges: + if (CU.getGlobalData().getOptions().UpdateIndexTablesOnly) + return false; + + // Skip address attribute if we are in function scope and function does not + // reference live address. + return CU.getDIEInfo(InputDIEIdx).getIsInFunctionScope() && + !FuncAddressAdjustment.has_value(); + case dwarf::DW_AT_addr_base: + // In case !Update the .debug_addr table is not generated/preserved. + return !CU.getGlobalData().getOptions().UpdateIndexTablesOnly; + case dwarf::DW_AT_rnglists_base: + // In case !Update the .debug_addr table is not generated/preserved. + // Thus instead of DW_FORM_rnglistx the DW_FORM_sec_offset is used. + // Since DW_AT_rnglists_base is used for only DW_FORM_rnglistx the + // DW_AT_rnglists_base is removed. + return !CU.getGlobalData().getOptions().UpdateIndexTablesOnly; + case dwarf::DW_AT_str_offsets_base: + // DWARFLinker does not generate or keep .debug_str_offsets table. + return true; + case dwarf::DW_AT_loclists_base: + // In case !Update the .debug_addr table is not generated/preserved. + // Thus instead of DW_FORM_loclistx the DW_FORM_sec_offset is used. + // Since DW_AT_loclists_base is used for only DW_FORM_loclistx the + // DW_AT_loclists_base is removed. + return !CU.getGlobalData().getOptions().UpdateIndexTablesOnly; + case dwarf::DW_AT_location: + case dwarf::DW_AT_frame_base: + if (CU.getGlobalData().getOptions().UpdateIndexTablesOnly) + return false; + + // When location expression contains an address: skip this attribute + // if it does not reference live address. + if (HasLocationExpressionAddress) + return !VarAddressAdjustment.has_value(); + + // Skip location attribute if we are in function scope and function does not + // reference live address. + return CU.getDIEInfo(InputDIEIdx).getIsInFunctionScope() && + !FuncAddressAdjustment.has_value(); + } +} + +size_t DIEAttributeCloner::cloneStringAttr( + const DWARFFormValue &Val, + const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec) { + std::optional String = dwarf::toString(Val); + if (!String) { + CU.warn("cann't read string attribute."); + return 0; + } + + StringEntry *StringInPool = + CU.getGlobalData().getStringPool().insert(*String).first; + if (AttrSpec.Form == dwarf::DW_FORM_line_strp) { + DebugInfoOutputSection.notePatchWithOffsetUpdate( + DebugLineStrPatch{{AttrOutOffset}, StringInPool}, PatchesOffsets); + return Generator + .addStringPlaceholderAttribute(AttrSpec.Attr, dwarf::DW_FORM_line_strp) + .second; + } + + DebugInfoOutputSection.notePatchWithOffsetUpdate( + DebugStrPatch{{AttrOutOffset}, StringInPool}, PatchesOffsets); + + // Update attributes info. + if (AttrSpec.Attr == dwarf::DW_AT_name) + AttrInfo.Name = StringInPool; + else if (AttrSpec.Attr == dwarf::DW_AT_MIPS_linkage_name || + AttrSpec.Attr == dwarf::DW_AT_linkage_name) + AttrInfo.MangledName = StringInPool; + + return Generator + .addStringPlaceholderAttribute(AttrSpec.Attr, dwarf::DW_FORM_strp) + .second; +} + +size_t DIEAttributeCloner::cloneDieRefAttr( + const DWARFFormValue &Val, + const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec) { + if (AttrSpec.Attr == dwarf::DW_AT_sibling) + return 0; + + std::optional> RefDiePair = + CU.resolveDIEReference(Val); + if (!RefDiePair) { + // If the referenced DIE is not found, drop the attribute. + CU.warn("cann't find referenced DIE.", InputDieEntry); + return 0; + } + assert(RefDiePair->first->getStage() >= CompileUnit::Stage::Loaded); + assert(RefDiePair->second != 0); + + // Get output offset for referenced DIE. + uint64_t OutDieOffset = + RefDiePair->first->getDieOutOffset(RefDiePair->second); + + // Examine whether referenced DIE is in current compile unit. + bool IsLocal = CU.getUniqueID() == RefDiePair->first->getUniqueID(); + + // Set attribute form basing on the kind of referenced DIE(local or not?). + dwarf::Form NewForm = IsLocal ? dwarf::DW_FORM_ref4 : dwarf::DW_FORM_ref_addr; + + // Check whether current attribute references already cloned DIE inside + // the same compilation unit. If true - write the already known offset value. + if (IsLocal && (OutDieOffset != 0)) + return Generator.addScalarAttribute(AttrSpec.Attr, NewForm, OutDieOffset) + .second; + + // If offset value is not known at this point then create patch for the + // reference value and write dummy value into the attribute. + DebugInfoOutputSection.notePatchWithOffsetUpdate( + DebugDieRefPatch{AttrOutOffset, &CU, RefDiePair->first, + RefDiePair->second}, + PatchesOffsets); + return Generator.addScalarAttribute(AttrSpec.Attr, NewForm, 0xBADDEF).second; +} + +size_t DIEAttributeCloner::cloneScalarAttr( + const DWARFFormValue &Val, + const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec) { + uint64_t Value; + // Check for the offset to the macro table. If offset is incorrect then we + // need to remove the attribute. + if (AttrSpec.Attr == dwarf::DW_AT_macro_info) { + if (std::optional Offset = Val.getAsSectionOffset()) { + const DWARFDebugMacro *Macro = + CU.getContaingFile().Dwarf->getDebugMacinfo(); + if (Macro == nullptr || !Macro->hasEntryForOffset(*Offset)) + return 0; + } + } + + if (AttrSpec.Attr == dwarf::DW_AT_macros) { + if (std::optional Offset = Val.getAsSectionOffset()) { + const DWARFDebugMacro *Macro = + CU.getContaingFile().Dwarf->getDebugMacro(); + if (Macro == nullptr || !Macro->hasEntryForOffset(*Offset)) + return 0; + } + } + + if (CU.getGlobalData().getOptions().UpdateIndexTablesOnly) { + if (auto OptionalValue = Val.getAsUnsignedConstant()) + Value = *OptionalValue; + else if (auto OptionalValue = Val.getAsSignedConstant()) + Value = *OptionalValue; + else if (auto OptionalValue = Val.getAsSectionOffset()) + Value = *OptionalValue; + else { + CU.warn("unsupported scalar attribute form. Dropping attribute.", + InputDieEntry); + return 0; + } + + if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value) + AttrInfo.IsDeclaration = true; + + if (AttrSpec.Form == dwarf::DW_FORM_loclistx) + return Generator.addLocListAttribute(AttrSpec.Attr, AttrSpec.Form, Value) + .second; + + return Generator.addScalarAttribute(AttrSpec.Attr, AttrSpec.Form, Value) + .second; + } + + dwarf::Form ResultingForm = AttrSpec.Form; + if (AttrSpec.Form == dwarf::DW_FORM_rnglistx) { + // DWARFLinker does not generate .debug_addr table. Thus we need to change + // all "addrx" related forms to "addr" version. Change DW_FORM_rnglistx + // to DW_FORM_sec_offset here. + std::optional Index = Val.getAsSectionOffset(); + if (!Index) { + CU.warn("cann't read the attribute. Dropping.", InputDieEntry); + return 0; + } + std::optional Offset = CU.getOrigUnit().getRnglistOffset(*Index); + if (!Offset) { + CU.warn("cann't read the attribute. Dropping.", InputDieEntry); + return 0; + } + + Value = *Offset; + ResultingForm = dwarf::DW_FORM_sec_offset; + } else if (AttrSpec.Form == dwarf::DW_FORM_loclistx) { + // DWARFLinker does not generate .debug_addr table. Thus we need to change + // all "addrx" related forms to "addr" version. Change DW_FORM_loclistx + // to DW_FORM_sec_offset here. + std::optional Index = Val.getAsSectionOffset(); + if (!Index) { + CU.warn("cann't read the attribute. Dropping.", InputDieEntry); + return 0; + } + std::optional Offset = CU.getOrigUnit().getLoclistOffset(*Index); + if (!Offset) { + CU.warn("cann't read the attribute. Dropping.", InputDieEntry); + return 0; + } + + Value = *Offset; + ResultingForm = dwarf::DW_FORM_sec_offset; + } else if (AttrSpec.Attr == dwarf::DW_AT_high_pc && + InputDieEntry->getTag() == dwarf::DW_TAG_compile_unit) { + std::optional LowPC = CU.getLowPc(); + if (!LowPC) + return 0; + // Dwarf >= 4 high_pc is an size, not an address. + Value = CU.getHighPc() - *LowPC; + } else if (AttrSpec.Form == dwarf::DW_FORM_sec_offset) + Value = *Val.getAsSectionOffset(); + else if (AttrSpec.Form == dwarf::DW_FORM_sdata) + Value = *Val.getAsSignedConstant(); + else if (auto OptionalValue = Val.getAsUnsignedConstant()) + Value = *OptionalValue; + else { + CU.warn("unsupported scalar attribute form. Dropping attribute.", + InputDieEntry); + return 0; + } + + if (AttrSpec.Attr == dwarf::DW_AT_ranges || + AttrSpec.Attr == dwarf::DW_AT_start_scope) { + // Create patch for the range offset value. + DebugInfoOutputSection.notePatchWithOffsetUpdate( + DebugRangePatch{{AttrOutOffset}, + InputDieEntry->getTag() == dwarf::DW_TAG_compile_unit}, + PatchesOffsets); + AttrInfo.HasRanges = true; + } else if (DWARFAttribute::mayHaveLocationList(AttrSpec.Attr) && + dwarf::doesFormBelongToClass(AttrSpec.Form, + DWARFFormValue::FC_SectionOffset, + CU.getOrigUnit().getVersion())) { + int64_t AddrAdjustmentValue = 0; + if (VarAddressAdjustment) + AddrAdjustmentValue = *VarAddressAdjustment; + else if (FuncAddressAdjustment) + AddrAdjustmentValue = *FuncAddressAdjustment; + + // Create patch for the location offset value. + DebugInfoOutputSection.notePatchWithOffsetUpdate( + DebugLocPatch{{AttrOutOffset}, AddrAdjustmentValue}, PatchesOffsets); + } else if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value) + AttrInfo.IsDeclaration = true; + + return Generator.addScalarAttribute(AttrSpec.Attr, ResultingForm, Value) + .second; +} + +size_t DIEAttributeCloner::cloneBlockAttr( + const DWARFFormValue &Val, + const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec) { + + size_t NumberOfPatchesAtStart = PatchesOffsets.size(); + + // If the block is a DWARF Expression, clone it into the temporary + // buffer using cloneExpression(), otherwise copy the data directly. + SmallVector Buffer; + ArrayRef Bytes = *Val.getAsBlock(); + if (DWARFAttribute::mayHaveLocationExpr(AttrSpec.Attr) && + (Val.isFormClass(DWARFFormValue::FC_Block) || + Val.isFormClass(DWARFFormValue::FC_Exprloc))) { + DWARFUnit &OrigUnit = CU.getOrigUnit(); + DataExtractor Data(StringRef((const char *)Bytes.data(), Bytes.size()), + OrigUnit.isLittleEndian(), + OrigUnit.getAddressByteSize()); + DWARFExpression Expr(Data, OrigUnit.getAddressByteSize(), + OrigUnit.getFormParams().Format); + + CU.cloneDieAttrExpression(Expr, Buffer, DebugInfoOutputSection, + VarAddressAdjustment, PatchesOffsets); + Bytes = Buffer; + } + + // The expression location data might be updated and exceed the original size. + // Check whether the new data fits into the original form. + dwarf::Form ResultForm = AttrSpec.Form; + if ((ResultForm == dwarf::DW_FORM_block1 && Bytes.size() > UINT8_MAX) || + (ResultForm == dwarf::DW_FORM_block2 && Bytes.size() > UINT16_MAX) || + (ResultForm == dwarf::DW_FORM_block4 && Bytes.size() > UINT32_MAX)) + ResultForm = dwarf::DW_FORM_block; + + size_t FinalAttributeSize; + if (AttrSpec.Form == dwarf::DW_FORM_exprloc) + FinalAttributeSize = + Generator.addLocationAttribute(AttrSpec.Attr, ResultForm, Bytes).second; + else + FinalAttributeSize = + Generator.addBlockAttribute(AttrSpec.Attr, ResultForm, Bytes).second; + + // Update patches offsets with the size of length field for Bytes. + for (size_t Idx = NumberOfPatchesAtStart; Idx < PatchesOffsets.size(); + Idx++) { + assert(FinalAttributeSize > Bytes.size()); + *PatchesOffsets[Idx] += + (AttrOutOffset + (FinalAttributeSize - Bytes.size())); + } + + return FinalAttributeSize; +} + +size_t DIEAttributeCloner::cloneAddressAttr( + const DWARFFormValue &Val, + const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec) { + if (AttrSpec.Attr == dwarf::DW_AT_low_pc) + AttrInfo.HasLowPc = true; + + if (CU.getGlobalData().getOptions().UpdateIndexTablesOnly) + return Generator + .addScalarAttribute(AttrSpec.Attr, AttrSpec.Form, Val.getRawUValue()) + .second; + + // Cloned Die may have address attributes relocated to a + // totally unrelated value. This can happen: + // - If high_pc is an address (Dwarf version == 2), then it might have been + // relocated to a totally unrelated value (because the end address in the + // object file might be start address of another function which got moved + // independently by the linker). + // - If address relocated in an inline_subprogram that happens at the + // beginning of its inlining function. + // To avoid above cases and to not apply relocation twice (in + // applyValidRelocs and here), read address attribute from InputDIE and apply + // Info.PCOffset here. + + std::optional AddrAttribute = + CU.find(InputDieEntry, AttrSpec.Attr); + if (!AddrAttribute) + llvm_unreachable("Cann't find attribute"); + + std::optional Addr = AddrAttribute->getAsAddress(); + if (!Addr) { + CU.warn("cann't read address attribute value."); + Addr = 0; + } + + if (InputDieEntry->getTag() == dwarf::DW_TAG_compile_unit && + AttrSpec.Attr == dwarf::DW_AT_low_pc) { + if (std::optional LowPC = CU.getLowPc()) + Addr = *LowPC; + else + return 0; + } else if (InputDieEntry->getTag() == dwarf::DW_TAG_compile_unit && + AttrSpec.Attr == dwarf::DW_AT_high_pc) { + if (uint64_t HighPc = CU.getHighPc()) + Addr = HighPc; + else + return 0; + } else { + if (VarAddressAdjustment) + *Addr += *VarAddressAdjustment; + else if (FuncAddressAdjustment) + *Addr += *FuncAddressAdjustment; + } + + dwarf::Form ResultingForm; + + switch (AttrSpec.Form) { + case dwarf::DW_FORM_addrx: + case dwarf::DW_FORM_addrx1: + case dwarf::DW_FORM_addrx2: + case dwarf::DW_FORM_addrx3: + case dwarf::DW_FORM_addrx4: { + // DWARFLinker does not use addrx forms since it generates relocated + // addresses. Replace DW_FORM_addrx* with DW_FORM_addr here. + ResultingForm = dwarf::DW_FORM_addr; + break; + } + default: + ResultingForm = AttrSpec.Form; + break; + } + + return Generator.addScalarAttribute(AttrSpec.Attr, ResultingForm, *Addr) + .second; +} + +unsigned DIEAttributeCloner::finalizeAbbreviations(bool HasChildrenToClone) { + size_t SizeOfAbbreviationNumber = + Generator.finalizeAbbreviations(HasChildrenToClone); + + // We need to update patches offsets after we know the size of the + // abbreviation number. + updatePatchesWithSizeOfAbbreviationNumber(SizeOfAbbreviationNumber); + + // Add the size of the abbreviation number to the output offset. + AttrOutOffset += SizeOfAbbreviationNumber; + + return AttrOutOffset; +} + +} // end of namespace dwarflinker_parallel +} // namespace llvm diff --git a/llvm/lib/DWARFLinkerParallel/DIEGenerator.h b/llvm/lib/DWARFLinkerParallel/DIEGenerator.h new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerParallel/DIEGenerator.h @@ -0,0 +1,157 @@ +//===- DIEGenerator.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_LIB_DWARFLINKERPARALLEL_DIEGENERATOR_H +#define LLVM_LIB_DWARFLINKERPARALLEL_DIEGENERATOR_H + +#include "DWARFLinkerGlobalData.h" +#include "DWARFLinkerUnit.h" +#include "llvm/CodeGen/DIE.h" +#include "llvm/Support/LEB128.h" + +namespace llvm { +namespace dwarflinker_parallel { + +/// This class is a helper to create output DIE tree. +class DIEGenerator { +public: + DIEGenerator(BumpPtrAllocator &Allocator, DwarfUnit &CU) + : Allocator(Allocator), CU(CU) {} + + /// Creates a DIE of specified tag \p DieTag and \p OutOffset. + DIE *createDIE(dwarf::Tag DieTag, uint32_t OutOffset) { + OutputDIE = DIE::get(Allocator, DieTag); + + OutputDIE->setOffset(OutOffset); + + return OutputDIE; + } + + /// Adds a specified \p Child to the current DIE. + void addChild(DIE *Child) { + assert(Child != nullptr); + assert(OutputDIE != nullptr); + + OutputDIE->addChild(Child); + } + + /// Adds specified scalar attribute to the current DIE. + std::pair addScalarAttribute(dwarf::Attribute Attr, + dwarf::Form AttrForm, + uint64_t Value) { + return addAttribute(Attr, AttrForm, DIEInteger(Value)); + } + + /// Adds specified location attribute to the current DIE. + std::pair addLocationAttribute(dwarf::Attribute Attr, + dwarf::Form AttrForm, + ArrayRef Bytes) { + DIELoc *Loc = new (Allocator) DIELoc; + for (auto Byte : Bytes) + static_cast(Loc)->addValue( + Allocator, static_cast(0), dwarf::DW_FORM_data1, + DIEInteger(Byte)); + Loc->setSize(Bytes.size()); + + return addAttribute(Attr, AttrForm, Loc); + } + + /// Adds specified block or exprloc attribute to the current DIE. + std::pair addBlockAttribute(dwarf::Attribute Attr, + dwarf::Form AttrForm, + ArrayRef Bytes) { + // The expression location data might be updated and exceed the original + // size. Check whether the new data fits into the original form. + assert((AttrForm == dwarf::DW_FORM_block) || + (AttrForm == dwarf::DW_FORM_exprloc) || + (AttrForm == dwarf::DW_FORM_block1 && Bytes.size() <= UINT8_MAX) || + (AttrForm == dwarf::DW_FORM_block2 && Bytes.size() <= UINT16_MAX) || + (AttrForm == dwarf::DW_FORM_block4 && Bytes.size() <= UINT32_MAX)); + + DIEBlock *Block = new (Allocator) DIEBlock; + for (auto Byte : Bytes) + static_cast(Block)->addValue( + Allocator, static_cast(0), dwarf::DW_FORM_data1, + DIEInteger(Byte)); + Block->setSize(Bytes.size()); + + return addAttribute(Attr, AttrForm, Block); + } + + /// Adds specified location list attribute to the current DIE. + std::pair addLocListAttribute(dwarf::Attribute Attr, + dwarf::Form AttrForm, + uint64_t Value) { + return addAttribute(Attr, AttrForm, DIELocList(Value)); + } + + /// Adds string attribute with dummy offset to the current DIE. + std::pair + addStringPlaceholderAttribute(dwarf::Attribute Attr, dwarf::Form AttrForm) { + assert(AttrForm == dwarf::DW_FORM_strp || + AttrForm == dwarf::DW_FORM_line_strp); + return addAttribute(Attr, AttrForm, DIEInteger(0xBADDEF)); + } + + /// Adds inplace string attribute to the current DIE. + std::pair addInplaceString(dwarf::Attribute Attr, + StringRef String) { + DIEBlock *Block = new (Allocator) DIEBlock; + for (auto Byte : String.bytes()) + static_cast(Block)->addValue( + Allocator, static_cast(0), dwarf::DW_FORM_data1, + DIEInteger(Byte)); + + static_cast(Block)->addValue( + Allocator, static_cast(0), dwarf::DW_FORM_data1, + DIEInteger(0)); + Block->setSize(String.size() + 1); + + DIEValue &ValueRef = + *OutputDIE->addValue(Allocator, Attr, dwarf::DW_FORM_string, Block); + return std::pair(ValueRef, String.size() + 1); + } + + /// Creates appreviations for the current DIE. Returns value of + /// abbreviation number. + size_t finalizeAbbreviations(bool CHILDREN_yes) { + // Create abbreviations for output DIE. + DIEAbbrev NewAbbrev = OutputDIE->generateAbbrev(); + if (CHILDREN_yes) + NewAbbrev.setChildrenFlag(dwarf::DW_CHILDREN_yes); + + CU.assignAbbrev(NewAbbrev); + OutputDIE->setAbbrevNumber(NewAbbrev.getNumber()); + + return getULEB128Size(OutputDIE->getAbbrevNumber()); + } + +protected: + template + std::pair addAttribute(dwarf::Attribute Attr, + dwarf::Form AttrForm, T &&Value) { + DIEValue &ValueRef = + *OutputDIE->addValue(Allocator, Attr, AttrForm, std::forward(Value)); + unsigned ValueSize = ValueRef.sizeOf(CU.getFormParams()); + return std::pair(ValueRef, ValueSize); + } + + // Allocator for output DIEs and values. + BumpPtrAllocator &Allocator; + + // Unit for the output DIE. + DwarfUnit &CU; + + // OutputDIE. + DIE *OutputDIE = nullptr; +}; + +} // end of namespace dwarflinker_parallel +} // end namespace llvm + +#endif // LLVM_LIB_DWARFLINKERPARALLEL_DIEGENERATOR_H diff --git a/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.h b/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.h --- a/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.h +++ b/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.h @@ -14,7 +14,6 @@ #include "llvm/CodeGen/AccelTable.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/DWARFLinkerParallel/DWARFLinker.h" -#include "llvm/DWARFLinkerParallel/StringTable.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCInstrInfo.h" @@ -40,186 +39,79 @@ namespace dwarflinker_parallel { -struct UnitStartSymbol { - unsigned UnitID = 0; - MCSymbol *Symbol = 0; -}; -using UnitStartSymbolsTy = SmallVector; -using Offset2UnitMapTy = DenseMap; - -struct RangeAttrPatch; -struct LocAttrPatch; - -/// The Dwarf emission logic. -/// -/// All interactions with the MC layer that is used to build the debug -/// information binary representation are handled in this class. +/// This class emits DWARF data to the output stream. It emits already +/// generated section data and specific data, which could not be generated +/// by CompileUnit. class DwarfEmitterImpl : public ExtraDwarfEmitter { public: DwarfEmitterImpl(DWARFLinker::OutputFileType OutFileType, - raw_pwrite_stream &OutFile, - std::function Translator, - DWARFLinker::MessageHandlerTy Warning) - : OutFile(OutFile), OutFileType(OutFileType), Translator(Translator), - WarningHandler(Warning) {} + raw_pwrite_stream &OutFile) + : OutFile(OutFile), OutFileType(OutFileType) {} + /// Initialize AsmPrinter data. Error init(Triple TheTriple, StringRef Swift5ReflectionSegmentName); + /// Returns triple of output stream. + const Triple &getTargetTriple() { return MC->getTargetTriple(); } + /// Dump the file to the disk. void finish() override { MS->finish(); } + /// Returns AsmPrinter. AsmPrinter &getAsmPrinter() const override { return *Asm; } - /// Set the current output section to debug_info and change - /// the MC Dwarf version to \p DwarfVersion. - void switchToDebugInfoSection(unsigned DwarfVersion) {} - /// Emit the swift_ast section stored in \p Buffer. - void emitSwiftAST(StringRef Buffer) override {} + void emitSwiftAST(StringRef Buffer) override; /// Emit the swift reflection section stored in \p Buffer. void emitSwiftReflectionSection( llvm::binaryformat::Swift5ReflectionSectionKind ReflSectionKind, - StringRef Buffer, uint32_t Alignment, uint32_t Size) override {} + StringRef Buffer, uint32_t Alignment, uint32_t) override; - void emitPaperTrailWarningsDie(DIE &Die) {} + /// Emit specified section data. + void emitSectionContents(StringRef SecData, StringRef SecName) override; - void emitSectionContents(StringRef SecData, StringRef SecName) override {} - - MCSymbol *emitTempSym(StringRef SecName, StringRef SymName) override { - return nullptr; - } + /// Emit temporary symbol. + MCSymbol *emitTempSym(StringRef SecName, StringRef SymName) override; + /// Emit abbreviations. void emitAbbrevs(const SmallVector> &Abbrevs, - unsigned DwarfVersion) {} - - void emitStrings(const StringTable &Strings) {} - - void emitLineStrings(const StringTable &Strings) {} - - void emitDebugNames(AccelTable &, - UnitStartSymbolsTy &UnitOffsets) {} - - void emitAppleNamespaces(AccelTable &) {} - - void emitAppleNames(AccelTable &) {} - - void emitAppleObjc(AccelTable &) {} - - void emitAppleTypes(AccelTable &) {} - - MCSymbol *emitDwarfDebugRangeListHeader(const CompileUnit &Unit) { - return nullptr; - } - - void emitDwarfDebugRangeListFragment(const CompileUnit &Unit, - const AddressRanges &LinkedRanges, - RangeAttrPatch &Patch) {} - - void emitDwarfDebugRangeListFooter(const CompileUnit &Unit, - MCSymbol *EndLabel) {} - - MCSymbol *emitDwarfDebugLocListHeader(const CompileUnit &Unit) { - return nullptr; - } - - void emitDwarfDebugLocListFragment( - const CompileUnit &Unit, - const DWARFLocationExpressionsVector &LinkedLocationExpression, - LocAttrPatch &Patch) {} - - void emitDwarfDebugLocListFooter(const CompileUnit &Unit, - MCSymbol *EndLabel) {} - - void emitDwarfDebugArangesTable(const CompileUnit &Unit, - const AddressRanges &LinkedRanges) {} + unsigned DwarfVersion); - void translateLineTable(DataExtractor LineData, uint64_t Offset) {} + /// Emit zero string into the .debug_str table. + void emitZeroString(); - void emitLineTableForUnit(MCDwarfLineTableParams Params, - StringRef PrologueBytes, unsigned MinInstLength, - std::vector &Rows, - unsigned AdddressSize) {} + /// Emit strings into the .debug_str section. + void emitStrings(ArrayList &StringPatches, + const StringEntryToDwarfStringPoolEntryMap &Strings, + uint64_t &NextOffset); - void emitLineTableForUnit(const DWARFDebugLine::LineTable &LineTable, - const CompileUnit &Unit, const StringTable &Strings, - const StringTable &LineTableStrings) {} + /// Emit strings into the .debug_line_str section. + void emitLineStrings(ArrayList &StringPatches, + const StringEntryToDwarfStringPoolEntryMap &Strings, + uint64_t &NextOffset); - void emitPubNamesForUnit(const CompileUnit &Unit) {} + /// Emit compile unit header. + void emitCompileUnitHeader(DwarfUnit &Unit); - void emitPubTypesForUnit(const CompileUnit &Unit) {} - - void emitCIE(StringRef CIEBytes) {} - - void emitFDE(uint32_t CIEOffset, uint32_t AddreSize, uint64_t Address, - StringRef Bytes) {} - - void emitCompileUnitHeader(CompileUnit &Unit, unsigned DwarfVersion) {} - - void emitDIE(DIE &Die) {} - - void emitMacroTables(DWARFContext *Context, - const Offset2UnitMapTy &UnitMacroMap, - StringTable &Strings) {} - - /// Returns size of generated .debug_line section. - uint64_t getDebugLineSectionSize() const { return LineSectionSize; } - - /// Returns size of generated .debug_frame section. - uint64_t getDebugFrameSectionSize() const { return FrameSectionSize; } - - /// Returns size of generated .debug_ranges section. - uint64_t getDebugRangesSectionSize() const { return RangesSectionSize; } - - /// Returns size of generated .debug_rnglists section. - uint64_t getDebugRngListsSectionSize() const { return RngListsSectionSize; } + /// Emit DIE recursively. + void emitDIE(DIE &Die); /// Returns size of generated .debug_info section. uint64_t getDebugInfoSectionSize() const { return DebugInfoSectionSize; } - /// Returns size of generated .debug_macinfo section. - uint64_t getDebugMacInfoSectionSize() const { return MacInfoSectionSize; } - - /// Returns size of generated .debug_macro section. - uint64_t getDebugMacroSectionSize() const { return MacroSectionSize; } - - /// Returns size of generated .debug_loc section. - uint64_t getDebugLocSectionSize() const { return LocSectionSize; } - - /// Returns size of generated .debug_loclists section. - uint64_t getDebugLocListsSectionSize() const { return LocListsSectionSize; } - private: - inline void warn(const Twine &Warning, StringRef Context = "") { - if (WarningHandler) - WarningHandler(Warning, Context, nullptr); - } - - void emitMacroTableImpl(const DWARFDebugMacro *MacroTable, - const Offset2UnitMapTy &UnitMacroMap, - StringPool &StringPool, uint64_t &OutOffset) {} - - /// Emit piece of .debug_ranges for \p LinkedRanges. - void emitDwarfDebugRangesTableFragment(const CompileUnit &Unit, - const AddressRanges &LinkedRanges, - RangeAttrPatch &Patch) {} - - /// Emit piece of .debug_rnglists for \p LinkedRanges. - void emitDwarfDebugRngListsTableFragment(const CompileUnit &Unit, - const AddressRanges &LinkedRanges, - RangeAttrPatch &Patch) {} - - /// Emit piece of .debug_loc for \p LinkedRanges. - void emitDwarfDebugLocTableFragment( - const CompileUnit &Unit, - const DWARFLocationExpressionsVector &LinkedLocationExpression, - LocAttrPatch &Patch) {} - - /// Emit piece of .debug_loclists for \p LinkedRanges. - void emitDwarfDebugLocListsTableFragment( - const CompileUnit &Unit, - const DWARFLocationExpressionsVector &LinkedLocationExpression, - LocAttrPatch &Patch) {} + // Enumerate all string patches and write them into the destination section. + // Order of patches is the same as in original input file. To avoid emitting + // the same string twice we accumulate NextOffset value. Thus if string + // offset smaller than NextOffset value then the patch is skipped (as that + // string was emitted earlier). + template + void emitStringsImpl(ArrayList &StringPatches, + const StringEntryToDwarfStringPoolEntryMap &Strings, + uint64_t &NextOffset, MCSection *OutSection); + + MCSection *switchSection(StringRef SecName); /// \defgroup MCObjects MC layer objects constructed by the streamer /// @{ @@ -240,32 +132,8 @@ /// The output file we stream the linked Dwarf to. raw_pwrite_stream &OutFile; DWARFLinker::OutputFileType OutFileType = DWARFLinker::OutputFileType::Object; - std::function Translator; - uint64_t RangesSectionSize = 0; - uint64_t RngListsSectionSize = 0; - uint64_t LocSectionSize = 0; - uint64_t LocListsSectionSize = 0; - uint64_t LineSectionSize = 0; - uint64_t FrameSectionSize = 0; uint64_t DebugInfoSectionSize = 0; - uint64_t MacInfoSectionSize = 0; - uint64_t MacroSectionSize = 0; - - /// Keep track of emitted CUs and their Unique ID. - struct EmittedUnit { - unsigned ID; - MCSymbol *LabelBegin; - }; - std::vector EmittedUnitsTy; - - /// Emit the pubnames or pubtypes section contribution for \p - /// Unit into \p Sec. The data is provided in \p Names. - void emitPubSectionForUnit(MCSection *Sec, StringRef Name, - const CompileUnit &Unit, - const std::vector &Names); - - DWARFLinker::MessageHandlerTy WarningHandler = nullptr; }; } // end namespace dwarflinker_parallel diff --git a/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.cpp b/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.cpp --- a/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.cpp +++ b/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.cpp @@ -114,18 +114,154 @@ TripleName.c_str()); Asm->setDwarfUsesRelocationsAcrossSections(false); - RangesSectionSize = 0; - RngListsSectionSize = 0; - LocSectionSize = 0; - LocListsSectionSize = 0; - LineSectionSize = 0; - FrameSectionSize = 0; DebugInfoSectionSize = 0; - MacInfoSectionSize = 0; - MacroSectionSize = 0; return Error::success(); } +void DwarfEmitterImpl::emitSwiftAST(StringRef Buffer) { + MCSection *SwiftASTSection = MOFI->getDwarfSwiftASTSection(); + SwiftASTSection->setAlignment(Align(32)); + MS->switchSection(SwiftASTSection); + MS->emitBytes(Buffer); +} + +/// Emit the swift reflection section stored in \p Buffer. +void DwarfEmitterImpl::emitSwiftReflectionSection( + llvm::binaryformat::Swift5ReflectionSectionKind ReflSectionKind, + StringRef Buffer, uint32_t Alignment, uint32_t) { + MCSection *ReflectionSection = + MOFI->getSwift5ReflectionSection(ReflSectionKind); + if (ReflectionSection == nullptr) + return; + ReflectionSection->setAlignment(Align(Alignment)); + MS->switchSection(ReflectionSection); + MS->emitBytes(Buffer); +} + +void DwarfEmitterImpl::emitSectionContents(StringRef SecData, + StringRef SecName) { + if (MCSection *Section = switchSection(SecName)) { + MS->switchSection(Section); + + MS->emitBytes(SecData); + } +} + +MCSection *DwarfEmitterImpl::switchSection(StringRef SecName) { + return StringSwitch(SecName) + .Case("debug_info", MC->getObjectFileInfo()->getDwarfInfoSection()) + .Case("debug_abbrev", MC->getObjectFileInfo()->getDwarfAbbrevSection()) + .Case("debug_line", MC->getObjectFileInfo()->getDwarfLineSection()) + .Case("debug_loc", MC->getObjectFileInfo()->getDwarfLocSection()) + .Case("debug_ranges", MC->getObjectFileInfo()->getDwarfRangesSection()) + .Case("debug_frame", MC->getObjectFileInfo()->getDwarfFrameSection()) + .Case("debug_aranges", MC->getObjectFileInfo()->getDwarfARangesSection()) + .Case("debug_rnglists", + MC->getObjectFileInfo()->getDwarfRnglistsSection()) + .Case("debug_loclists", + MC->getObjectFileInfo()->getDwarfLoclistsSection()) + .Case("debug_macro", MC->getObjectFileInfo()->getDwarfMacroSection()) + .Case("debug_macinfo", MC->getObjectFileInfo()->getDwarfMacinfoSection()) + .Case("debug_addr", MC->getObjectFileInfo()->getDwarfAddrSection()) + .Default(nullptr); +} + +MCSymbol *DwarfEmitterImpl::emitTempSym(StringRef SecName, StringRef SymName) { + if (MCSection *Section = switchSection(SecName)) { + MS->switchSection(Section); + MCSymbol *Res = Asm->createTempSymbol(SymName); + Asm->OutStreamer->emitLabel(Res); + return Res; + } + + return nullptr; +} + +void DwarfEmitterImpl::emitAbbrevs( + const SmallVector> &Abbrevs, + unsigned DwarfVersion) { + MS->switchSection(MOFI->getDwarfAbbrevSection()); + MC->setDwarfVersion(DwarfVersion); + Asm->emitDwarfAbbrevs(Abbrevs); +} + +void DwarfEmitterImpl::emitZeroString() { + Asm->OutStreamer->switchSection(MOFI->getDwarfStrSection()); + + // Emit a null terminator for zero String. + Asm->emitInt8(0); +} + +template +void DwarfEmitterImpl::emitStringsImpl( + ArrayList &StringPatches, + const StringEntryToDwarfStringPoolEntryMap &Strings, uint64_t &NextOffset, + MCSection *OutSection) { + Asm->OutStreamer->switchSection(OutSection); + + // Enumerate all string patches and write them into the destination section. + StringPatches.forEach([&](const PatchTy &Patch) { + DwarfStringPoolEntryWithExtString *StringToEmit = + Strings.getExistingEntry(Patch.String); + assert(StringToEmit->isIndexed()); + + // Patches can refer the same strings. We use accumulated NextOffset + // to understand whether corresponding string is already emitted. + // Skip patch if string is already emitted. + if (StringToEmit->Offset >= NextOffset) { + NextOffset = StringToEmit->Offset + StringToEmit->String.size() + 1; + // Emit the string itself. + Asm->OutStreamer->emitBytes(StringToEmit->String); + // Emit a null terminator. + Asm->emitInt8(0); + } + }); +} + +void DwarfEmitterImpl::emitStrings( + ArrayList &StringPatches, + const StringEntryToDwarfStringPoolEntryMap &Strings, uint64_t &NextOffset) { + emitStringsImpl(StringPatches, Strings, NextOffset, + MOFI->getDwarfStrSection()); +} + +void DwarfEmitterImpl::emitLineStrings( + ArrayList &StringPatches, + const StringEntryToDwarfStringPoolEntryMap &Strings, uint64_t &NextOffset) { + emitStringsImpl(StringPatches, Strings, NextOffset, + MOFI->getDwarfLineStrSection()); +} + +void DwarfEmitterImpl::emitCompileUnitHeader(DwarfUnit &Unit) { + MS->switchSection(MOFI->getDwarfInfoSection()); + MC->setDwarfVersion(Unit.getVersion()); + + // Emit size of content not including length itself. The size has already + // been computed in CompileUnit::computeOffsets(). Subtract 4 to that size to + // account for the length field. + Asm->emitInt32(Unit.getHeaderSize() + Unit.getOutUnitDIE()->getSize() - 4); + Asm->emitInt16(Unit.getVersion()); + + if (Unit.getVersion() >= 5) { + Asm->emitInt8(dwarf::DW_UT_compile); + Asm->emitInt8(Unit.getFormParams().AddrSize); + // Proper offset to the abbreviations table will be set later. + Asm->emitInt32(0); + DebugInfoSectionSize += 12; + } else { + // Proper offset to the abbreviations table will be set later. + Asm->emitInt32(0); + Asm->emitInt8(Unit.getFormParams().AddrSize); + DebugInfoSectionSize += 11; + } +} + +void DwarfEmitterImpl::emitDIE(DIE &Die) { + MS->switchSection(MOFI->getDwarfInfoSection()); + Asm->emitDwarfDIE(Die); + DebugInfoSectionSize += Die.getSize(); +} + } // end of namespace dwarflinker_parallel } // namespace llvm diff --git a/llvm/lib/DWARFLinkerParallel/DWARFFile.cpp b/llvm/lib/DWARFLinkerParallel/DWARFFile.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerParallel/DWARFFile.cpp @@ -0,0 +1,18 @@ +//=== DWARFFile.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 "llvm/DWARFLinkerParallel/DWARFFile.h" +#include "DWARFLinkerGlobalData.h" + +llvm::dwarflinker_parallel::DWARFFile::DWARFFile( + StringRef Name, std::unique_ptr Dwarf, + std::unique_ptr Addresses, + const std::vector &Warnings, + DWARFFile::UnloadCallbackTy UnloadFunc) + : FileName(Name), Dwarf(std::move(Dwarf)), Addresses(std::move(Addresses)), + Warnings(Warnings), UnloadFunc(UnloadFunc) {} diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.h b/llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.h --- a/llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.h +++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.h @@ -11,53 +11,322 @@ #include "DWARFLinkerUnit.h" #include "llvm/DWARFLinkerParallel/DWARFFile.h" -#include "llvm/DWARFLinkerParallel/DWARFLinker.h" #include namespace llvm { namespace dwarflinker_parallel { -struct LinkContext; -class DWARFFile; +using OffsetToUnitTy = function_ref; /// Stores all information related to a compile unit, be it in its original /// instance of the object file or its brand new cloned and generated DIE tree. class CompileUnit : public DwarfUnit { public: - CompileUnit(LinkContext &, unsigned ID, StringRef ClangModuleName, - DWARFFile &File, - DWARFLinker::SwiftInterfacesMapTy *, - UnitMessageHandlerTy WarningHandler) - : DwarfUnit(ID, ClangModuleName, WarningHandler), ContaingFile(File) { - FormParams.Version = 4; - FormParams.Format = dwarf::DWARF32; - FormParams.AddrSize = 4; - UnitName = ContaingFile.FileName; - } - - CompileUnit(LinkContext &, DWARFUnit &OrigUnit, unsigned ID, + /// The stages of new compile unit processing. + enum class Stage : uint8_t { + /// Created, linked with input DWARF file. + CreatedNotLoaded = 0, + + /// Input DWARF is loaded. + Loaded, + + /// Input DWARF is analysed(DIEs pointing to the real code section are + /// discovered, type names are assigned if ODR is requested). + LivenessAnalysisDone, + + /// Output DWARF is generated. + Cloned, + + /// Offsets inside patch records are updated. + PatchesUpdated, + + /// Resources(Input DWARF, Output DWARF tree) are released. + Cleaned, + }; + + CompileUnit(LinkingGlobalData &GlobalData, unsigned ID, + StringRef ClangModuleName, DWARFFile &File, + OffsetToUnitTy UnitFromOffset) + : DwarfUnit(GlobalData, ID, ClangModuleName), File(File), + getUnitFromOffset(UnitFromOffset), Stage(Stage::CreatedNotLoaded) { + UnitName = File.FileName; + } + + CompileUnit(LinkingGlobalData &GlobalData, DWARFUnit &OrigUnit, unsigned ID, StringRef ClangModuleName, DWARFFile &File, - UnitMessageHandlerTy WarningHandler) - : DwarfUnit(ID, ClangModuleName, WarningHandler), - ContaingFile(File), OrigUnit(&OrigUnit) { + OffsetToUnitTy UnitFromOffset) + : DwarfUnit(GlobalData, ID, ClangModuleName), File(File), + OrigUnit(&OrigUnit), getUnitFromOffset(UnitFromOffset), + Stage(Stage::CreatedNotLoaded) { DWARFDie CUDie = OrigUnit.getUnitDIE(); if (!CUDie) return; - if (File.Dwarf) - Endianess = File.Dwarf->isLittleEndian() ? support::endianness::little - : support::endianness::big; - - FormParams.Version = OrigUnit.getVersion(); - FormParams.Format = dwarf::DWARF32; - FormParams.AddrSize = OrigUnit.getAddressByteSize(); + setOutputFormat(OrigUnit); Language = dwarf::toUnsigned(CUDie.find(dwarf::DW_AT_language), 0); - - UnitName = ContaingFile.FileName; + if (const char *CUName = CUDie.getName(DINameKind::ShortName)) + UnitName = CUName; + else + UnitName = File.FileName; SysRoot = dwarf::toStringRef(CUDie.find(dwarf::DW_AT_LLVM_sysroot)).str(); } + /// Returns stage of overall processing. + Stage getStage() const { return Stage; } + + /// Set stage of overall processing. + void setStage(Stage Stage) { this->Stage = Stage; } + + /// Loads unit line table. + void loadLineTable(); + + /// Returns name of the file for the \p FileIdx + /// from the unit`s line table. + StringEntry *getFileName(unsigned FileIdx, StringPool &GlobalStrings); + + /// Returns DWARFFile containing this compile unit. + const DWARFFile &getContaingFile() const { return File; } + + /// Load DIEs of input compilation unit. \returns true if input DIEs + /// successfully loaded. + bool loadInputDIEs(); + + /// Reset compile units data(results of liveness analysis, clonning) + /// if current stage greater than Stage::Loaded. We need to reset data + /// as we are going to repeat stages. + void maybeResetToLoadedStage(); + + /// Collect references to parseable Swift interfaces in imported + /// DW_TAG_module blocks. + void analyzeImportedModule(const DWARFDebugInfoEntry *DieEntry); + + /// Navigate DWARF tree and set die properties. + void analyzeDWARFStructure() { + analyzeDWARFStructureRec(getUnitDIE().getDebugInfoEntry(), false, false); + } + + /// Cleanup unneeded resources after compile unit is cloned. + void cleanupDataAfterClonning(); + + /// After cloning stage the output DIEs offsets are deallocated. + /// This method copies output offsets for referenced DIEs into DIEs patches. + void updateDieRefPatchesWithClonedOffsets(); + + /// Kinds of placement for the output die. + enum DieOutputPlacement : uint8_t { + NotSet = 0, + + /// Corresponding DIE goes to the type table only. + /// NOTE: Not used yet. + TypeTable = 1, + + /// Corresponding DIE goes to the plain dwarf only. + PlainDwarf = 2, + + /// Corresponding DIE goes to type table and to plain dwarf. + /// NOTE: Not used yet. + Both = 3, + + /// Corresponding DIE needs to examine parent to determine + /// the point of placement. + /// NOTE: Not used yet. + Parent = 4 + }; + + /// Information gathered about source DIEs. + struct DIEInfo { + DIEInfo() = default; + DIEInfo(const DIEInfo &Other) { Flags = Other.Flags.load(); } + DIEInfo &operator=(const DIEInfo &Other) { + Flags = Other.Flags.load(); + return *this; + } + + /// Data member keeping various flags. + std::atomic Flags = {0}; + + /// \returns Placement kind for the corresponding die. + DieOutputPlacement getPlacement() const { + return DieOutputPlacement(Flags & 0x7); + } + + /// Sets Placement kind for the corresponding die. + void setPlacement(DieOutputPlacement Placement) { + auto InputData = Flags.load(); + while (!Flags.compare_exchange_weak(InputData, + ((InputData & ~0x7) | Placement))) { + } + } + + /// Unsets Placement kind for the corresponding die. + void unsetPlacement() { + auto InputData = Flags.load(); + while (!Flags.compare_exchange_weak(InputData, (InputData & ~0x7))) { + } + } + + /// Sets Placement kind for the corresponding die. + bool setPlacementIfUnset(DieOutputPlacement Placement) { + auto InputData = Flags.load(); + if ((InputData & 0x7) == NotSet) + if (Flags.compare_exchange_weak(InputData, (InputData | Placement))) + return true; + + return false; + } + +#define SINGLE_FLAG_METHODS_SET(Name, Value) \ + bool get##Name() const { return Flags & Value; } \ + void set##Name() { \ + auto InputData = Flags.load(); \ + while (!Flags.compare_exchange_weak(InputData, InputData | Value)) { \ + } \ + } \ + void unset##Name() { \ + auto InputData = Flags.load(); \ + while (!Flags.compare_exchange_weak(InputData, InputData & ~Value)) { \ + } \ + } + + /// DIE is a part of the linked output. + SINGLE_FLAG_METHODS_SET(Keep, 0x08) + + /// DIE has children which are part of the linked output. + SINGLE_FLAG_METHODS_SET(KeepChildren, 0x10) + + /// DIE is referenced by other DIE. + SINGLE_FLAG_METHODS_SET(ReferrencedBy, 0x20) + + /// DIE is in module scope. + SINGLE_FLAG_METHODS_SET(IsInMouduleScope, 0x40) + + /// DIE is in function scope. + SINGLE_FLAG_METHODS_SET(IsInFunctionScope, 0x80) + + void unsetFlagsWhichSetDuringLiveAnalysis() { + auto InputData = Flags.load(); + while (!Flags.compare_exchange_weak( + InputData, InputData & ~(0x7 | 0x8 | 0x10 | 0x20))) { + } + } + + /// Erase all flags. + void eraseData() { Flags = 0; } + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + LLVM_DUMP_METHOD void dump(); +#endif + }; + + /// \defgroup Group of functions returning DIE info. + /// + /// @{ + + /// \p Idx index of the DIE. + /// \returns DieInfo descriptor. + DIEInfo &getDIEInfo(unsigned Idx) { return DieInfoArray[Idx]; } + + /// \p Idx index of the DIE. + /// \returns DieInfo descriptor. + const DIEInfo &getDIEInfo(unsigned Idx) const { return DieInfoArray[Idx]; } + + /// \p Idx index of the DIE. + /// \returns DieInfo descriptor. + DIEInfo &getDIEInfo(const DWARFDebugInfoEntry *Entry) { + return DieInfoArray[getOrigUnit().getDIEIndex(Entry)]; + } + + /// \p Idx index of the DIE. + /// \returns DieInfo descriptor. + const DIEInfo &getDIEInfo(const DWARFDebugInfoEntry *Entry) const { + return DieInfoArray[getOrigUnit().getDIEIndex(Entry)]; + } + + /// \p Die + /// \returns PlainDieInfo descriptor. + DIEInfo &getDIEInfo(const DWARFDie &Die) { + return DieInfoArray[getOrigUnit().getDIEIndex(Die)]; + } + + /// \p Die + /// \returns PlainDieInfo descriptor. + const DIEInfo &getDIEInfo(const DWARFDie &Die) const { + return DieInfoArray[getOrigUnit().getDIEIndex(Die)]; + } + + /// \p Idx index of the DIE. + /// \returns DieInfo descriptor. + uint64_t getDieOutOffset(uint32_t Idx) { + return reinterpret_cast *>(&OutDieOffsetArray[Idx]) + ->load(); + } + + /// \p Idx index of the DIE. + /// \returns DieInfo descriptor. + void rememberDieOutOffset(uint32_t Idx, uint64_t Offset) { + reinterpret_cast *>(&OutDieOffsetArray[Idx]) + ->store(Offset); + } + + /// @} + + /// Returns value of DW_AT_low_pc attribute. + std::optional getLowPc() const { return LowPc; } + + /// Returns value of DW_AT_high_pc attribute. + uint64_t getHighPc() const { return HighPc; } + + /// Returns true if there is a label corresponding to the specified \p Addr. + bool hasLabelAt(uint64_t Addr) const { return Labels.count(Addr); } + + /// Add the low_pc of a label that is relocated by applying + /// offset \p PCOffset. + void addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset); + + /// Resolve the DIE attribute reference that has been extracted in \p + /// RefValue. The resulting DIE might be in another CompileUnit. + /// \returns referenced die and corresponding compilation unit. + /// compilation unit is null if reference could not be resolved. + std::optional> + resolveDIEReference(const DWARFFormValue &RefValue); + /// @} + + /// Add a function range [\p LowPC, \p HighPC) that is relocated by applying + /// offset \p PCOffset. + void addFunctionRange(uint64_t LowPC, uint64_t HighPC, int64_t PCOffset); + + /// Returns function ranges of this unit. + const RangesTy &getFunctionRanges() const { return Ranges; } + + /// Clone and emit this compilation unit. + Error cloneAndEmit(std::optional TargetTriple); + + /// Clone and emit debug locations(.debug_loc/.debug_loclists). + Error cloneAndEmitDebugLocations(); + + /// Clone and emit ranges. + Error cloneAndEmitRanges(); + + /// Clone and emit debug macros(.debug_macinfo/.debug_macro). + Error cloneAndEmitDebugMacro(); + + // Clone input DIE entry. + DIE *cloneDIE(const DWARFDebugInfoEntry *InputDieEntry, uint64_t OutOffset, + std::optional FuncAddressAdjustment, + std::optional VarAddressAdjustment, + BumpPtrAllocator &Allocator); + + // Clone and emit line table. + Error cloneAndEmitLineTable(Triple &TargetTriple); + + /// Clone attribute location axpression. + void cloneDieAttrExpression(const DWARFExpression &InputExpression, + SmallVectorImpl &OutputExpression, + SectionDescriptor &Section, + std::optional VarAddressAdjustment, + OffsetsPtrVector &PatchesOffsets); + /// \defgroup Helper methods to access OrigUnit. /// /// @{ @@ -142,12 +411,137 @@ /// @} + /// \defgroup Methods used for reporting warnings and errors: + /// + /// @{ + + void warn(const Twine &Warning, const DWARFDie *DIE = nullptr) { + GlobalData.warn(Warning, getUnitName(), DIE); + } + + void warn(Error Warning, const DWARFDie *DIE = nullptr) { + handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) { + GlobalData.warn(Info.message(), getUnitName(), DIE); + }); + } + + void warn(const Twine &Warning, const DWARFDebugInfoEntry *DieEntry) { + if (DieEntry != nullptr) { + DWARFDie DIE(&getOrigUnit(), DieEntry); + GlobalData.warn(Warning, getUnitName(), &DIE); + return; + } + + GlobalData.warn(Warning, getUnitName()); + } + + void error(const Twine &Err, const DWARFDie *DIE = nullptr) { + GlobalData.warn(Err, getUnitName(), DIE); + } + + void error(Error Err, const DWARFDie *DIE = nullptr) { + handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) { + GlobalData.error(Info.message(), getUnitName(), DIE); + }); + } + + /// @} + private: + /// Navigate DWARF tree recursively and set die properties. + void analyzeDWARFStructureRec(const DWARFDebugInfoEntry *DieEntry, + bool IsInModule, bool IsInFunction); + + struct LinkedLocationExpressionsWithOffsetPatches { + DWARFLocationExpression Expression; + OffsetsPtrVector Patches; + }; + using LinkedLocationExpressionsVector = + SmallVector; + + /// Emit debug locations. + void emitLocations(SectionDescriptor &DebugInfoSection, + SectionDescriptor &OutLocationSection); + + /// Emit location list header. + uint64_t emitLocListHeader(SectionDescriptor &OutLocationSection); + + /// Emit location list fragment. + uint64_t emitLocListFragment( + const LinkedLocationExpressionsVector &LinkedLocationExpression, + SectionDescriptor &OutLocationSection); + + /// Emit .debug_aranges. + void emitAranges(SectionDescriptor &DebugInfoSection, + SectionDescriptor &OutArangesSection, + AddressRanges &LinkedFunctionRanges); + + /// Clone and emit .debug_ranges/.debug_rnglists. + void cloneAndEmitRangeList(SectionDescriptor &DebugInfoSection, + SectionDescriptor &OutRangeSection, + AddressRanges &LinkedFunctionRanges); + + /// Emit range list header. + uint64_t emitRangeListHeader(SectionDescriptor &OutRangeSection); + + /// Emit range list fragment. + void emitRangeListFragment(const AddressRanges &LinkedRanges, + SectionDescriptor &OutRangeSection); + + /// Insert the new line info sequence \p Seq into the current + /// set of already linked line info \p Rows. + void insertLineSequence(std::vector &Seq, + std::vector &Rows); + + /// Emits body for both macro sections. + void emitMacroTableImpl(const DWARFDebugMacro *MacroTable, + uint64_t OffsetToMacroTable, + SectionDescriptor &Section, bool hasDWARFv5Header); + /// DWARFFile containing this compile unit. - DWARFFile &ContaingFile; + DWARFFile &File; /// Pointer to the paired compile unit from the input DWARF. DWARFUnit *OrigUnit = nullptr; + + /// Line table for this unit. + const DWARFDebugLine::LineTable *LineTablePtr = nullptr; + + /// Cached resolved paths from the line table. + /// The key is . + using ResolvedPathsMap = DenseMap; + ResolvedPathsMap ResolvedFullPaths; + StringMap ResolvedParentPaths; + + /// This field instructs compile unit to store DIE name with stripped + /// template parameters into the accelerator table. + bool CanStripTemplateName = false; + + /// \defgroup Data Members accessed asinchroniously. + /// + /// @{ + OffsetToUnitTy getUnitFromOffset; + + std::optional LowPc; + uint64_t HighPc = 0; + + /// The ranges in that map are the PC ranges for functions in this unit, + /// associated with the PC offset to apply to the addresses to get + /// the linked address. + RangesTy Ranges; + std::mutex RangesMutex; + + /// The DW_AT_low_pc of each DW_TAG_label. + SmallDenseMap Labels; + std::mutex LabelsMutex; + + /// This field keeps current stage of overall compile unit processing. + std::atomic Stage; + + /// DIE info indexed by DIE index. + SmallVector DieInfoArray; + SmallVector OutDieOffsetArray; + /// @} }; } // end of namespace dwarflinker_parallel diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.cpp b/llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.cpp @@ -0,0 +1,1286 @@ +//=== DWARFLinkerCompileUnit.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 "DWARFLinkerCompileUnit.h" +#include "DIEAttributeCloner.h" +#include "DIEGenerator.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::dwarflinker_parallel; + +void CompileUnit::loadLineTable() { + LineTablePtr = File.Dwarf->getLineTableForUnit(&getOrigUnit()); +} + +void CompileUnit::maybeResetToLoadedStage() { + if (getStage() <= Stage::Loaded) + return; + + for (DIEInfo &DieInfo : DieInfoArray) + DieInfo.unsetFlagsWhichSetDuringLiveAnalysis(); + + LowPc = std::nullopt; + HighPc = 0; + Labels.clear(); + Ranges.clear(); + + if (getStage() < Stage::Cloned) { + setStage(Stage::Loaded); + return; + } + + AbbreviationsSet.clear(); + Abbreviations.clear(); + OutUnitDIE = nullptr; + + for (uint64_t &Offset : OutDieOffsetArray) + Offset = 0; + eraseSections(); + + setStage(Stage::CreatedNotLoaded); +} + +bool CompileUnit::loadInputDIEs() { + DWARFDie InputUnitDIE = getUnitDIE(false); + if (!InputUnitDIE) + return false; + + // load input dies, resize Info structures array. + DieInfoArray.resize(getOrigUnit().getNumDIEs()); + OutDieOffsetArray.resize(getOrigUnit().getNumDIEs(), 0); + return true; +} + +void CompileUnit::analyzeDWARFStructureRec(const DWARFDebugInfoEntry *DieEntry, + bool IsInModule, bool IsInFunction) { + for (const DWARFDebugInfoEntry *CurChild = getFirstChildEntry(DieEntry); + CurChild && CurChild->getAbbreviationDeclarationPtr(); + CurChild = getSiblingEntry(CurChild)) { + CompileUnit::DIEInfo &ChildInfo = getDIEInfo(CurChild); + + if (IsInModule) + ChildInfo.setIsInMouduleScope(); + if (IsInFunction) + ChildInfo.setIsInFunctionScope(); + + switch (CurChild->getTag()) { + case dwarf::DW_TAG_module: + ChildInfo.setIsInMouduleScope(); + if (DieEntry->getTag() == dwarf::DW_TAG_compile_unit && + dwarf::toString(find(CurChild, dwarf::DW_AT_name), "") != + getClangModuleName()) + analyzeImportedModule(CurChild); + break; + case dwarf::DW_TAG_subprogram: + ChildInfo.setIsInFunctionScope(); + break; + default: + break; + } + + if (IsInModule) + ChildInfo.setIsInMouduleScope(); + if (IsInFunction) + ChildInfo.setIsInFunctionScope(); + + if (CurChild->hasChildren()) + analyzeDWARFStructureRec(CurChild, ChildInfo.getIsInMouduleScope(), + ChildInfo.getIsInFunctionScope()); + } +} + +StringEntry *CompileUnit::getFileName(unsigned FileIdx, + StringPool &GlobalStrings) { + if (LineTablePtr) { + if (LineTablePtr->hasFileAtIndex(FileIdx)) { + // Cache the resolved paths based on the index in the line table, + // because calling realpath is expensive. + ResolvedPathsMap::const_iterator It = ResolvedFullPaths.find(FileIdx); + if (It == ResolvedFullPaths.end()) { + std::string OrigFileName; + bool FoundFileName = LineTablePtr->getFileNameByIndex( + FileIdx, getOrigUnit().getCompilationDir(), + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, + OrigFileName); + (void)FoundFileName; + assert(FoundFileName && "Must get file name from line table"); + + // Second level of caching, this time based on the file's parent + // path. + StringRef FileName = sys::path::filename(OrigFileName); + StringRef ParentPath = sys::path::parent_path(OrigFileName); + + // If the ParentPath has not yet been resolved, resolve and cache it for + // future look-ups. + StringMap::iterator ParentIt = + ResolvedParentPaths.find(ParentPath); + if (ParentIt == ResolvedParentPaths.end()) { + SmallString<256> RealPath; + sys::fs::real_path(ParentPath, RealPath); + ParentIt = + ResolvedParentPaths + .insert({ParentPath, GlobalStrings.insert(RealPath).first}) + .first; + } + + // Join the file name again with the resolved path. + SmallString<256> ResolvedPath(ParentIt->second->first()); + sys::path::append(ResolvedPath, FileName); + + It = ResolvedFullPaths + .insert(std::make_pair( + FileIdx, GlobalStrings.insert(ResolvedPath).first)) + .first; + } + + return It->second; + } + } + + return nullptr; +} + +void CompileUnit::cleanupDataAfterClonning() { + AbbreviationsSet.clear(); + ResolvedFullPaths.shrink_and_clear(); + ResolvedParentPaths.clear(); + DieInfoArray = SmallVector(); + OutDieOffsetArray = SmallVector(); + getOrigUnit().clear(); +} + +/// Collect references to parseable Swift interfaces in imported +/// DW_TAG_module blocks. +void CompileUnit::analyzeImportedModule(const DWARFDebugInfoEntry *DieEntry) { + if (getLanguage() != dwarf::DW_LANG_Swift) + return; + + if (!GlobalData.getOptions().ParseableSwiftInterfaces) + return; + + StringRef Path = + dwarf::toStringRef(find(DieEntry, dwarf::DW_AT_LLVM_include_path)); + if (!Path.endswith(".swiftinterface")) + return; + // Don't track interfaces that are part of the SDK. + StringRef SysRoot = + dwarf::toStringRef(find(DieEntry, dwarf::DW_AT_LLVM_sysroot)); + if (SysRoot.empty()) + SysRoot = getSysRoot(); + if (!SysRoot.empty() && Path.startswith(SysRoot)) + return; + if (std::optional Val = find(DieEntry, dwarf::DW_AT_name)) { + Expected Name = Val->getAsCString(); + if (!Name) { + warn(Name.takeError()); + return; + } + + auto &Entry = (*GlobalData.getOptions().ParseableSwiftInterfaces)[*Name]; + // The prepend path is applied later when copying. + SmallString<128> ResolvedPath; + if (sys::path::is_relative(Path)) + sys::path::append( + ResolvedPath, + dwarf::toString(getUnitDIE().find(dwarf::DW_AT_comp_dir), "")); + sys::path::append(ResolvedPath, Path); + if (!Entry.empty() && Entry != ResolvedPath) { + DWARFDie Die = getDIE(DieEntry); + warn(Twine("conflicting parseable interfaces for Swift Module ") + *Name + + ": " + Entry + " and " + Path + ".", + &Die); + } + Entry = std::string(ResolvedPath.str()); + } +} + +void CompileUnit::updateDieRefPatchesWithClonedOffsets() { + SectionDescriptor &DebugInfoSection = + getSectionDescriptor(DebugSectionKind::DebugInfo); + + DebugInfoSection.ListDebugDieRefPatch.forEach([](DebugDieRefPatch &Patch) { + Patch.RefDieIdxOrClonedOffset = Patch.RefCU.getPointer()->getDieOutOffset( + Patch.RefDieIdxOrClonedOffset); + }); + + DebugInfoSection.ListDebugULEB128DieRefPatch.forEach( + [](DebugULEB128DieRefPatch &Patch) { + Patch.RefDieIdxOrClonedOffset = + Patch.RefCU.getPointer()->getDieOutOffset( + Patch.RefDieIdxOrClonedOffset); + }); + + SectionDescriptor &DebugLocSection = + getSectionDescriptor(DebugSectionKind::DebugLoc); + + DebugLocSection.ListDebugULEB128DieRefPatch.forEach( + [](DebugULEB128DieRefPatch &Patch) { + Patch.RefDieIdxOrClonedOffset = + Patch.RefCU.getPointer()->getDieOutOffset( + Patch.RefDieIdxOrClonedOffset); + }); + + SectionDescriptor &DebugLocListsSection = + getSectionDescriptor(DebugSectionKind::DebugLocLists); + + DebugLocListsSection.ListDebugULEB128DieRefPatch.forEach( + [](DebugULEB128DieRefPatch &Patch) { + Patch.RefDieIdxOrClonedOffset = + Patch.RefCU.getPointer()->getDieOutOffset( + Patch.RefDieIdxOrClonedOffset); + }); +} + +std::optional> +CompileUnit::resolveDIEReference(const DWARFFormValue &RefValue) { + if (std::optional Ref = + *RefValue.getAsRelativeReference()) { + if (Ref->Unit != nullptr) { + // Referenced DIE is in current compile unit. + + if (std::optional RefDieIdx = + getDIEIndexForOffset(Ref->Unit->getOffset() + Ref->Offset)) + return std::make_pair(this, *RefDieIdx); + } else if (CompileUnit *RefCU = getUnitFromOffset(Ref->Offset)) { + // Referenced DIE is in other compile unit. + + // Check whether DIEs are loaded for that compile unit. + enum Stage ReferredCUStage = RefCU->getStage(); + if (ReferredCUStage < Stage::Loaded || ReferredCUStage > Stage::Cloned) + return std::make_pair(RefCU, 0); + + if (std::optional RefDieIdx = + RefCU->getDIEIndexForOffset(Ref->Offset)) + return std::make_pair(RefCU, *RefDieIdx); + } + } + + return std::nullopt; +} + +void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc, + int64_t PcOffset) { + std::lock_guard Guard(RangesMutex); + + Ranges.insert({FuncLowPc, FuncHighPc}, PcOffset); + if (LowPc) + LowPc = std::min(*LowPc, FuncLowPc + PcOffset); + else + LowPc = FuncLowPc + PcOffset; + this->HighPc = std::max(HighPc, FuncHighPc + PcOffset); +} + +void CompileUnit::addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset) { + std::lock_guard Guard(LabelsMutex); + Labels.insert({LabelLowPc, PcOffset}); +} + +Error CompileUnit::cloneAndEmitDebugLocations() { + if (getGlobalData().getOptions().UpdateIndexTablesOnly) + return Error::success(); + + if (getOrigUnit().getVersion() < 5) { + emitLocations(getSectionDescriptor(DebugSectionKind::DebugInfo), + getSectionDescriptor(DebugSectionKind::DebugLoc)); + return Error::success(); + } + + emitLocations(getSectionDescriptor(DebugSectionKind::DebugInfo), + getSectionDescriptor(DebugSectionKind::DebugLocLists)); + return Error::success(); +} + +void CompileUnit::emitLocations(SectionDescriptor &DebugInfoSection, + SectionDescriptor &OutLocationSection) { + if (!DebugInfoSection.ListDebugLocPatch.empty()) { + DWARFUnit &OrigUnit = getOrigUnit(); + + uint64_t OffsetAfterUnitLength = emitLocListHeader(OutLocationSection); + + DebugInfoSection.ListDebugLocPatch.forEach([&](DebugLocPatch &Patch) { + // Get location expressions vector corresponding to the current + // attribute from the source DWARF. + uint64_t InputDebugLocSectionOffset = DebugInfoSection.getIntVal( + Patch.PatchOffset, + DebugInfoSection.getFormParams().getDwarfOffsetByteSize()); + Expected OriginalLocations = + OrigUnit.findLoclistFromOffset(InputDebugLocSectionOffset); + + if (!OriginalLocations) { + warn(OriginalLocations.takeError()); + return; + } + + LinkedLocationExpressionsVector LinkedLocationExpressions; + for (DWARFLocationExpression &CurExpression : *OriginalLocations) { + LinkedLocationExpressionsWithOffsetPatches LinkedExpression; + + if (CurExpression.Range) { + // Relocate address range. + LinkedExpression.Expression.Range = { + CurExpression.Range->LowPC + Patch.AddrAdjustmentValue, + CurExpression.Range->HighPC + Patch.AddrAdjustmentValue}; + } + + DataExtractor Data(CurExpression.Expr, OrigUnit.isLittleEndian(), + OrigUnit.getAddressByteSize()); + + DWARFExpression InputExpression(Data, OrigUnit.getAddressByteSize(), + OrigUnit.getFormParams().Format); + cloneDieAttrExpression(InputExpression, + LinkedExpression.Expression.Expr, + OutLocationSection, Patch.AddrAdjustmentValue, + LinkedExpression.Patches); + + LinkedLocationExpressions.push_back({LinkedExpression}); + } + + // Emit locations list table fragment corresponding to the CurLocAttr. + DebugInfoSection.apply(Patch.PatchOffset, dwarf::DW_FORM_sec_offset, + OutLocationSection.OS.tell()); + emitLocListFragment(LinkedLocationExpressions, OutLocationSection); + }); + + if (OffsetAfterUnitLength > 0) { + assert(OffsetAfterUnitLength - + OutLocationSection.getFormParams().getDwarfOffsetByteSize() < + OffsetAfterUnitLength); + OutLocationSection.apply( + OffsetAfterUnitLength - + OutLocationSection.getFormParams().getDwarfOffsetByteSize(), + dwarf::DW_FORM_sec_offset, + OutLocationSection.OS.tell() - OffsetAfterUnitLength); + } + } +} + +/// Emit debug locations(.debug_loc, .debug_loclists) header. +uint64_t CompileUnit::emitLocListHeader(SectionDescriptor &OutLocationSection) { + if (getOrigUnit().getVersion() < 5) + return 0; + + // unit_length. + OutLocationSection.emitUnitLength(0xBADDEF); + uint64_t OffsetAfterUnitLength = OutLocationSection.OS.tell(); + + // Version. + OutLocationSection.emitIntVal(5, 2); + + // Address size. + OutLocationSection.emitIntVal(OutLocationSection.getFormParams().AddrSize, 1); + + // Seg_size + OutLocationSection.emitIntVal(0, 1); + + // Offset entry count + OutLocationSection.emitIntVal(0, 4); + + return OffsetAfterUnitLength; +} + +/// Emit debug locations(.debug_loc, .debug_loclists) fragment. +uint64_t CompileUnit::emitLocListFragment( + const LinkedLocationExpressionsVector &LinkedLocationExpression, + SectionDescriptor &OutLocationSection) { + uint64_t OffsetBeforeLocationExpression = 0; + + if (getOrigUnit().getVersion() < 5) { + uint64_t BaseAddress = 0; + if (std::optional LowPC = getLowPc()) + BaseAddress = *LowPC; + + for (const LinkedLocationExpressionsWithOffsetPatches &LocExpression : + LinkedLocationExpression) { + if (LocExpression.Expression.Range) { + OutLocationSection.emitIntVal( + LocExpression.Expression.Range->LowPC - BaseAddress, + OutLocationSection.getFormParams().AddrSize); + OutLocationSection.emitIntVal( + LocExpression.Expression.Range->HighPC - BaseAddress, + OutLocationSection.getFormParams().AddrSize); + } + + OutLocationSection.emitIntVal(LocExpression.Expression.Expr.size(), 2); + OffsetBeforeLocationExpression = OutLocationSection.OS.tell(); + for (uint64_t *OffsetPtr : LocExpression.Patches) + *OffsetPtr += OffsetBeforeLocationExpression; + + OutLocationSection.OS + << StringRef((const char *)LocExpression.Expression.Expr.data(), + LocExpression.Expression.Expr.size()); + } + + // Emit the terminator entry. + OutLocationSection.emitIntVal(0, + OutLocationSection.getFormParams().AddrSize); + OutLocationSection.emitIntVal(0, + OutLocationSection.getFormParams().AddrSize); + return OffsetBeforeLocationExpression; + } + + std::optional BaseAddress; + for (const LinkedLocationExpressionsWithOffsetPatches &LocExpression : + LinkedLocationExpression) { + if (LocExpression.Expression.Range) { + // Check whether base address is set. If it is not set yet + // then set current base address and emit base address selection entry. + if (!BaseAddress) { + BaseAddress = LocExpression.Expression.Range->LowPC; + + // Emit base address. + OutLocationSection.emitIntVal(dwarf::DW_LLE_base_address, 1); + OutLocationSection.emitIntVal(*BaseAddress, getFormParams().AddrSize); + } + + // Emit type of entry. + OutLocationSection.emitIntVal(dwarf::DW_LLE_offset_pair, 1); + + // Emit start offset relative to base address. + encodeULEB128(LocExpression.Expression.Range->LowPC - *BaseAddress, + OutLocationSection.OS); + + // Emit end offset relative to base address. + encodeULEB128(LocExpression.Expression.Range->HighPC - *BaseAddress, + OutLocationSection.OS); + } else + // Emit type of entry. + OutLocationSection.emitIntVal(dwarf::DW_LLE_default_location, 1); + + encodeULEB128(LocExpression.Expression.Expr.size(), OutLocationSection.OS); + OffsetBeforeLocationExpression = OutLocationSection.OS.tell(); + for (uint64_t *OffsetPtr : LocExpression.Patches) + *OffsetPtr += OffsetBeforeLocationExpression; + + OutLocationSection.OS << StringRef( + (const char *)LocExpression.Expression.Expr.data(), + LocExpression.Expression.Expr.size()); + } + + // Emit the terminator entry. + OutLocationSection.emitIntVal(dwarf::DW_LLE_end_of_list, 1); + return OffsetBeforeLocationExpression; +} + +Error CompileUnit::cloneAndEmitRanges() { + if (getGlobalData().getOptions().UpdateIndexTablesOnly) + return Error::success(); + + // Build set of linked address ranges for unit function ranges. + AddressRanges LinkedFunctionRanges; + for (const AddressRangeValuePair &Range : getFunctionRanges()) + LinkedFunctionRanges.insert( + {Range.Range.start() + Range.Value, Range.Range.end() + Range.Value}); + + emitAranges(getSectionDescriptor(DebugSectionKind::DebugInfo), + getSectionDescriptor(DebugSectionKind::DebugARanges), + LinkedFunctionRanges); + + if (getOrigUnit().getVersion() < 5) { + cloneAndEmitRangeList(getSectionDescriptor(DebugSectionKind::DebugInfo), + getSectionDescriptor(DebugSectionKind::DebugRange), + LinkedFunctionRanges); + return Error::success(); + } + + cloneAndEmitRangeList(getSectionDescriptor(DebugSectionKind::DebugInfo), + getSectionDescriptor(DebugSectionKind::DebugRngLists), + LinkedFunctionRanges); + return Error::success(); +} + +void CompileUnit::cloneAndEmitRangeList(SectionDescriptor &DebugInfoSection, + SectionDescriptor &OutRangeSection, + AddressRanges &LinkedFunctionRanges) { + if (!DebugInfoSection.ListDebugRangePatch.empty()) { + std::optional CachedRange; + uint64_t OffsetAfterUnitLength = emitRangeListHeader(OutRangeSection); + + DebugRangePatch *CompileUnitRangePtr = nullptr; + DebugInfoSection.ListDebugRangePatch.forEach([&](DebugRangePatch &Patch) { + if (Patch.IsCompileUnitRanges) { + CompileUnitRangePtr = &Patch; + } else { + // Get ranges from the source DWARF corresponding to the current + // attribute. + AddressRanges LinkedRanges; + uint64_t InputDebugRangesSectionOffset = DebugInfoSection.getIntVal( + Patch.PatchOffset, + DebugInfoSection.getFormParams().getDwarfOffsetByteSize()); + if (Expected InputRanges = + getOrigUnit().findRnglistFromOffset( + InputDebugRangesSectionOffset)) { + // Apply relocation adjustment. + for (const auto &Range : *InputRanges) { + if (!CachedRange || !CachedRange->Range.contains(Range.LowPC)) + CachedRange = + getFunctionRanges().getRangeThatContains(Range.LowPC); + + // All range entries should lie in the function range. + if (!CachedRange) { + warn("inconsistent range data."); + continue; + } + + // Store range for emiting. + LinkedRanges.insert({Range.LowPC + CachedRange->Value, + Range.HighPC + CachedRange->Value}); + } + } else { + llvm::consumeError(InputRanges.takeError()); + warn("invalid range list ignored."); + } + + // Emit linked ranges. + DebugInfoSection.apply(Patch.PatchOffset, dwarf::DW_FORM_sec_offset, + OutRangeSection.OS.tell()); + emitRangeListFragment(LinkedRanges, OutRangeSection); + } + }); + + if (CompileUnitRangePtr != nullptr) { + // Emit compile unit ranges last to be binary compatible with classic + // dsymutil. + DebugInfoSection.apply(CompileUnitRangePtr->PatchOffset, + dwarf::DW_FORM_sec_offset, + OutRangeSection.OS.tell()); + emitRangeListFragment(LinkedFunctionRanges, OutRangeSection); + } + + if (OffsetAfterUnitLength > 0) { + assert(OffsetAfterUnitLength - + OutRangeSection.getFormParams().getDwarfOffsetByteSize() < + OffsetAfterUnitLength); + OutRangeSection.apply( + OffsetAfterUnitLength - + OutRangeSection.getFormParams().getDwarfOffsetByteSize(), + dwarf::DW_FORM_sec_offset, + OutRangeSection.OS.tell() - OffsetAfterUnitLength); + } + } +} + +uint64_t CompileUnit::emitRangeListHeader(SectionDescriptor &OutRangeSection) { + if (OutRangeSection.getFormParams().Version < 5) + return 0; + + // unit_length. + OutRangeSection.emitUnitLength(0xBADDEF); + uint64_t OffsetAfterUnitLength = OutRangeSection.OS.tell(); + + // Version. + OutRangeSection.emitIntVal(5, 2); + + // Address size. + OutRangeSection.emitIntVal(OutRangeSection.getFormParams().AddrSize, 1); + + // Seg_size + OutRangeSection.emitIntVal(0, 1); + + // Offset entry count + OutRangeSection.emitIntVal(0, 4); + + return OffsetAfterUnitLength; +} + +void CompileUnit::emitRangeListFragment(const AddressRanges &LinkedRanges, + SectionDescriptor &OutRangeSection) { + if (OutRangeSection.getFormParams().Version < 5) { + // Emit ranges. + uint64_t BaseAddress = 0; + if (std::optional LowPC = getLowPc()) + BaseAddress = *LowPC; + + for (const AddressRange &Range : LinkedRanges) { + OutRangeSection.emitIntVal(Range.start() - BaseAddress, + OutRangeSection.getFormParams().AddrSize); + OutRangeSection.emitIntVal(Range.end() - BaseAddress, + OutRangeSection.getFormParams().AddrSize); + } + + // Add the terminator entry. + OutRangeSection.emitIntVal(0, OutRangeSection.getFormParams().AddrSize); + OutRangeSection.emitIntVal(0, OutRangeSection.getFormParams().AddrSize); + return; + } + + for (const AddressRange &Range : LinkedRanges) { + // Emit type of entry. + OutRangeSection.emitIntVal(dwarf::DW_RLE_start_length, 1); + + // Emit start address. + OutRangeSection.emitIntVal(Range.start(), + OutRangeSection.getFormParams().AddrSize); + + // Emit length of the range. + encodeULEB128(Range.end() - Range.start(), OutRangeSection.OS); + } + + // Emit the terminator entry. + OutRangeSection.emitIntVal(dwarf::DW_RLE_end_of_list, 1); +} + +void CompileUnit::emitAranges(SectionDescriptor &DebugInfoSection, + SectionDescriptor &OutArangesSection, + AddressRanges &LinkedFunctionRanges) { + if (LinkedFunctionRanges.empty()) + return; + + // Emit Header. + unsigned HeaderSize = + sizeof(int32_t) + // Size of contents (w/o this field + sizeof(int16_t) + // DWARF ARange version number + sizeof(int32_t) + // Offset of CU in the .debug_info section + sizeof(int8_t) + // Pointer Size (in bytes) + sizeof(int8_t); // Segment Size (in bytes) + + unsigned TupleSize = OutArangesSection.getFormParams().AddrSize * 2; + unsigned Padding = offsetToAlignment(HeaderSize, Align(TupleSize)); + + OutArangesSection.emitOffset(0xBADDEF); // Aranges length + uint64_t OffsetAfterArangesLengthField = OutArangesSection.OS.tell(); + + OutArangesSection.emitIntVal(dwarf::DW_ARANGES_VERSION, 2); // Version number + OutArangesSection.notePatch( + DebugOffsetPatch{OutArangesSection.OS.tell(), &DebugInfoSection}); + OutArangesSection.emitOffset(0xBADDEF); // Corresponding unit's offset + OutArangesSection.emitIntVal(OutArangesSection.getFormParams().AddrSize, + 1); // Address size + OutArangesSection.emitIntVal(0, 1); // Segment size + + for (size_t Idx = 0; Idx < Padding; Idx++) + OutArangesSection.emitIntVal(0, 1); // Padding + + // Emit linked ranges. + for (const AddressRange &Range : LinkedFunctionRanges) { + OutArangesSection.emitIntVal(Range.start(), + OutArangesSection.getFormParams().AddrSize); + OutArangesSection.emitIntVal(Range.end() - Range.start(), + OutArangesSection.getFormParams().AddrSize); + } + + // Emit terminator. + OutArangesSection.emitIntVal(0, OutArangesSection.getFormParams().AddrSize); + OutArangesSection.emitIntVal(0, OutArangesSection.getFormParams().AddrSize); + + uint64_t OffsetAfterArangesEnd = OutArangesSection.OS.tell(); + + // Update Aranges lentgh. + OutArangesSection.apply( + OffsetAfterArangesLengthField - + OutArangesSection.getFormParams().getDwarfOffsetByteSize(), + dwarf::DW_FORM_sec_offset, + OffsetAfterArangesEnd - OffsetAfterArangesLengthField); +} + +Error CompileUnit::cloneAndEmitDebugMacro() { + if (getOutUnitDIE() == nullptr) + return Error::success(); + + DWARFUnit &OrigUnit = getOrigUnit(); + DWARFDie OrigUnitDie = OrigUnit.getUnitDIE(); + + // Check for .debug_macro table. + if (std::optional MacroAttr = + dwarf::toSectionOffset(OrigUnitDie.find(dwarf::DW_AT_macros))) { + if (const DWARFDebugMacro *Table = + getContaingFile().Dwarf->getDebugMacro()) { + SectionDescriptor &OutSection = + getSectionDescriptor(DebugSectionKind::DebugMacro); + emitMacroTableImpl(Table, *MacroAttr, OutSection, true); + } + } + + // Check for .debug_macinfo table. + if (std::optional MacroAttr = + dwarf::toSectionOffset(OrigUnitDie.find(dwarf::DW_AT_macro_info))) { + if (const DWARFDebugMacro *Table = + getContaingFile().Dwarf->getDebugMacinfo()) { + SectionDescriptor &OutSection = + getSectionDescriptor(DebugSectionKind::DebugMacinfo); + emitMacroTableImpl(Table, *MacroAttr, OutSection, false); + } + } + + return Error::success(); +} + +void CompileUnit::emitMacroTableImpl(const DWARFDebugMacro *MacroTable, + uint64_t OffsetToMacroTable, + SectionDescriptor &Section, + bool hasDWARFv5Header) { + bool DefAttributeIsReported = false; + bool UndefAttributeIsReported = false; + bool ImportAttributeIsReported = false; + + for (const DWARFDebugMacro::MacroList &List : MacroTable->MacroLists) { + if (OffsetToMacroTable == List.Offset) { + // Write DWARFv5 header. + if (hasDWARFv5Header) { + // Write header version. + Section.emitIntVal(List.Header.Version, sizeof(List.Header.Version)); + + uint8_t Flags = List.Header.Flags; + + // Check for OPCODE_OPERANDS_TABLE. + if (Flags & + DWARFDebugMacro::HeaderFlagMask::MACRO_OPCODE_OPERANDS_TABLE) { + Flags &= + ~DWARFDebugMacro::HeaderFlagMask::MACRO_OPCODE_OPERANDS_TABLE; + warn("opcode_operands_table is not supported yet."); + } + + // Check for DEBUG_LINE_OFFSET. + std::optional StmtListOffset; + if (Flags & DWARFDebugMacro::HeaderFlagMask::MACRO_DEBUG_LINE_OFFSET) { + // Get offset to the line table from the cloned compile unit. + for (auto &V : getOutUnitDIE()->values()) { + if (V.getAttribute() == dwarf::DW_AT_stmt_list) { + StmtListOffset = V.getDIEInteger().getValue(); + break; + } + } + + if (!StmtListOffset) { + Flags &= ~DWARFDebugMacro::HeaderFlagMask::MACRO_DEBUG_LINE_OFFSET; + warn("couldn`t find line table for macro table."); + } + } + + // Write flags. + Section.emitIntVal(Flags, sizeof(Flags)); + + // Write offset to line table. + if (StmtListOffset) { + Section.notePatch(DebugOffsetPatch{ + Section.OS.tell(), + &getSectionDescriptor(DebugSectionKind::DebugLine)}); + // TODO: check that List.Header.getOffsetByteSize() and + // DebugOffsetPatch agree on size. + Section.emitIntVal(0xBADDEF, List.Header.getOffsetByteSize()); + } + } + + // Write macro entries. + for (const DWARFDebugMacro::Entry &MacroEntry : List.Macros) { + if (MacroEntry.Type == 0) { + encodeULEB128(MacroEntry.Type, Section.OS); + continue; + } + + uint8_t MacroType = MacroEntry.Type; + switch (MacroType) { + default: { + bool HasVendorSpecificExtension = + (!hasDWARFv5Header && + MacroType == dwarf::DW_MACINFO_vendor_ext) || + (hasDWARFv5Header && (MacroType >= dwarf::DW_MACRO_lo_user && + MacroType <= dwarf::DW_MACRO_hi_user)); + + if (HasVendorSpecificExtension) { + // Write macinfo type. + Section.emitIntVal(MacroType, 1); + + // Write vendor extension constant. + encodeULEB128(MacroEntry.ExtConstant, Section.OS); + + // Write vendor extension string. + Section.emitString(dwarf::DW_FORM_string, MacroEntry.ExtStr); + } else + warn("unknown macro type. skip."); + } break; + // debug_macro and debug_macinfo share some common encodings. + // DW_MACRO_define == DW_MACINFO_define + // DW_MACRO_undef == DW_MACINFO_undef + // DW_MACRO_start_file == DW_MACINFO_start_file + // DW_MACRO_end_file == DW_MACINFO_end_file + // For readibility/uniformity we are using DW_MACRO_*. + case dwarf::DW_MACRO_define: + case dwarf::DW_MACRO_undef: { + // Write macinfo type. + Section.emitIntVal(MacroType, 1); + + // Write source line. + encodeULEB128(MacroEntry.Line, Section.OS); + + // Write macro string. + Section.emitString(dwarf::DW_FORM_string, MacroEntry.MacroStr); + } break; + case dwarf::DW_MACRO_define_strp: + case dwarf::DW_MACRO_undef_strp: + case dwarf::DW_MACRO_define_strx: + case dwarf::DW_MACRO_undef_strx: { + // DW_MACRO_*_strx forms are not supported currently. + // Convert to *_strp. + switch (MacroType) { + case dwarf::DW_MACRO_define_strx: { + MacroType = dwarf::DW_MACRO_define_strp; + if (!DefAttributeIsReported) { + warn("DW_MACRO_define_strx unsupported yet. Convert to " + "DW_MACRO_define_strp."); + DefAttributeIsReported = true; + } + } break; + case dwarf::DW_MACRO_undef_strx: { + MacroType = dwarf::DW_MACRO_undef_strp; + if (!UndefAttributeIsReported) { + warn("DW_MACRO_undef_strx unsupported yet. Convert to " + "DW_MACRO_undef_strp."); + UndefAttributeIsReported = true; + } + } break; + default: + // Nothing to do. + break; + } + + // Write macinfo type. + Section.emitIntVal(MacroType, 1); + + // Write source line. + encodeULEB128(MacroEntry.Line, Section.OS); + + // Write macro string. + Section.emitString(dwarf::DW_FORM_strp, MacroEntry.MacroStr); + break; + } + case dwarf::DW_MACRO_start_file: { + // Write macinfo type. + Section.emitIntVal(MacroType, 1); + // Write source line. + encodeULEB128(MacroEntry.Line, Section.OS); + // Write source file id. + encodeULEB128(MacroEntry.File, Section.OS); + } break; + case dwarf::DW_MACRO_end_file: { + // Write macinfo type. + Section.emitIntVal(MacroType, 1); + } break; + case dwarf::DW_MACRO_import: + case dwarf::DW_MACRO_import_sup: { + if (!ImportAttributeIsReported) { + warn("DW_MACRO_import and DW_MACRO_import_sup are unsupported " + "yet. remove."); + ImportAttributeIsReported = true; + } + } break; + } + } + + return; + } + } +} + +void CompileUnit::cloneDieAttrExpression( + const DWARFExpression &InputExpression, + SmallVectorImpl &OutputExpression, SectionDescriptor &Section, + std::optional VarAddressAdjustment, + OffsetsPtrVector &PatchesOffsets) { + using Encoding = DWARFExpression::Operation::Encoding; + + DWARFUnit &OrigUnit = getOrigUnit(); + uint8_t OrigAddressByteSize = OrigUnit.getAddressByteSize(); + + uint64_t OpOffset = 0; + for (auto &Op : InputExpression) { + auto Desc = Op.getDescription(); + // DW_OP_const_type is variable-length and has 3 + // operands. Thus far we only support 2. + if ((Desc.Op.size() == 2 && Desc.Op[0] == Encoding::BaseTypeRef) || + (Desc.Op.size() == 2 && Desc.Op[1] == Encoding::BaseTypeRef && + Desc.Op[0] != Encoding::Size1)) + warn("unsupported DW_OP encoding."); + + if ((Desc.Op.size() == 1 && Desc.Op[0] == Encoding::BaseTypeRef) || + (Desc.Op.size() == 2 && Desc.Op[1] == Encoding::BaseTypeRef && + Desc.Op[0] == Encoding::Size1)) { + // This code assumes that the other non-typeref operand fits into 1 byte. + assert(OpOffset < Op.getEndOffset()); + uint32_t ULEBsize = Op.getEndOffset() - OpOffset - 1; + assert(ULEBsize <= 16); + + // Copy over the operation. + assert(!Op.getSubCode() && "SubOps not yet supported"); + OutputExpression.push_back(Op.getCode()); + uint64_t RefOffset; + if (Desc.Op.size() == 1) { + RefOffset = Op.getRawOperand(0); + } else { + OutputExpression.push_back(Op.getRawOperand(0)); + RefOffset = Op.getRawOperand(1); + } + uint8_t ULEB[16]; + uint32_t Offset = 0; + unsigned RealSize = 0; + // Look up the base type. For DW_OP_convert, the operand may be 0 to + // instead indicate the generic type. The same holds for + // DW_OP_reinterpret, which is currently not supported. + if (RefOffset > 0 || Op.getCode() != dwarf::DW_OP_convert) { + RefOffset += OrigUnit.getOffset(); + uint32_t RefDieIdx = 0; + if (std::optional Idx = + OrigUnit.getDIEIndexForOffset(RefOffset)) + RefDieIdx = *Idx; + + // Use fixed size for ULEB128 data, since we need to update that size + // later with the proper offsets. Use 5 for DWARF32, 9 for DWARF64. + ULEBsize = getFormParams().getDwarfOffsetByteSize() + 1; + + RealSize = encodeULEB128(0xBADDEF, ULEB, ULEBsize); + + Section.notePatchWithOffsetUpdate( + DebugULEB128DieRefPatch(OutputExpression.size(), this, this, + RefDieIdx), + PatchesOffsets); + } else + RealSize = encodeULEB128(Offset, ULEB, ULEBsize); + + if (RealSize > ULEBsize) { + // Emit the generic type as a fallback. + RealSize = encodeULEB128(0, ULEB, ULEBsize); + warn("base type ref doesn't fit."); + } + assert(RealSize == ULEBsize && "padding failed"); + ArrayRef ULEBbytes(ULEB, ULEBsize); + OutputExpression.append(ULEBbytes.begin(), ULEBbytes.end()); + } else if (!getGlobalData().getOptions().UpdateIndexTablesOnly && + Op.getCode() == dwarf::DW_OP_addrx) { + if (std::optional SA = + OrigUnit.getAddrOffsetSectionItem(Op.getRawOperand(0))) { + // DWARFLinker does not use addrx forms since it generates relocated + // addresses. Replace DW_OP_addrx with DW_OP_addr here. + // Argument of DW_OP_addrx should be relocated here as it is not + // processed by applyValidRelocs. + OutputExpression.push_back(dwarf::DW_OP_addr); + uint64_t LinkedAddress = + SA->Address + (VarAddressAdjustment ? *VarAddressAdjustment : 0); + if ((getEndianness() == support::endianness::little) != + sys::IsLittleEndianHost) + sys::swapByteOrder(LinkedAddress); + ArrayRef AddressBytes( + reinterpret_cast(&LinkedAddress), + OrigAddressByteSize); + OutputExpression.append(AddressBytes.begin(), AddressBytes.end()); + } else + warn("cann't read DW_OP_addrx operand."); + } else if (!getGlobalData().getOptions().UpdateIndexTablesOnly && + Op.getCode() == dwarf::DW_OP_constx) { + if (std::optional SA = + OrigUnit.getAddrOffsetSectionItem(Op.getRawOperand(0))) { + // DWARFLinker does not use constx forms since it generates relocated + // addresses. Replace DW_OP_constx with DW_OP_const[*]u here. + // Argument of DW_OP_constx should be relocated here as it is not + // processed by applyValidRelocs. + std::optional OutOperandKind; + switch (OrigAddressByteSize) { + case 2: + OutOperandKind = dwarf::DW_OP_const2u; + break; + case 4: + OutOperandKind = dwarf::DW_OP_const4u; + break; + case 8: + OutOperandKind = dwarf::DW_OP_const8u; + break; + default: + warn( + formatv(("unsupported address size: {0}."), OrigAddressByteSize)); + break; + } + + if (OutOperandKind) { + OutputExpression.push_back(*OutOperandKind); + uint64_t LinkedAddress = + SA->Address + (VarAddressAdjustment ? *VarAddressAdjustment : 0); + if ((getEndianness() == support::endianness::little) != + sys::IsLittleEndianHost) + sys::swapByteOrder(LinkedAddress); + ArrayRef AddressBytes( + reinterpret_cast(&LinkedAddress), + OrigAddressByteSize); + OutputExpression.append(AddressBytes.begin(), AddressBytes.end()); + } + } else + warn("cann't read DW_OP_constx operand."); + } else { + // Copy over everything else unmodified. + StringRef Bytes = + InputExpression.getData().slice(OpOffset, Op.getEndOffset()); + OutputExpression.append(Bytes.begin(), Bytes.end()); + } + OpOffset = Op.getEndOffset(); + } +} + +Error CompileUnit::cloneAndEmit(std::optional TargetTriple) { + BumpPtrAllocator Allocator; + + DWARFDie OrigUnitDIE = getOrigUnit().getUnitDIE(); + if (!OrigUnitDIE.isValid()) + return Error::success(); + + CanStripTemplateName = + llvm::is_contained(getGlobalData().getOptions().AccelTables, + DWARFLinker::AccelTableKind::Apple); + + // Clone input DIE entry recursively. + DIE *OutCUDie = cloneDIE(OrigUnitDIE.getDebugInfoEntry(), getHeaderSize(), + std::nullopt, std::nullopt, Allocator); + setOutUnitDIE(OutCUDie); + + if (getGlobalData().getOptions().NoOutput || (OutCUDie == nullptr)) + return Error::success(); + + assert(TargetTriple.has_value()); + if (Error Err = cloneAndEmitLineTable(*TargetTriple)) + return Err; + + if (Error Err = cloneAndEmitDebugMacro()) + return Err; + + if (Error Err = emitDebugInfo(*TargetTriple)) + return Err; + + // ASSUMPTION: .debug_info section should already be emitted at this point. + // cloneAndEmitRanges & cloneAndEmitDebugLocations use .debug_info section + // data. + + if (Error Err = cloneAndEmitRanges()) + return Err; + + if (Error Err = cloneAndEmitDebugLocations()) + return Err; + + return emitAbbreviations(); +} + +bool needToClone(CompileUnit::DIEInfo &Info) { + return Info.getKeep() || Info.getKeepChildren(); +} + +DIE *CompileUnit::cloneDIE(const DWARFDebugInfoEntry *InputDieEntry, + uint64_t OutOffset, + std::optional FuncAddressAdjustment, + std::optional VarAddressAdjustment, + BumpPtrAllocator &Allocator) { + uint32_t InputDieIdx = getDIEIndex(InputDieEntry); + CompileUnit::DIEInfo &Info = getDIEInfo(InputDieIdx); + + if (!needToClone(Info)) + return nullptr; + + bool HasLocationExpressionAddress = false; + if (InputDieEntry->getTag() == dwarf::DW_TAG_subprogram) { + // Get relocation adjustment value for the current function. + FuncAddressAdjustment = + getContaingFile().Addresses->getSubprogramRelocAdjustment( + getDIE(InputDieEntry)); + } else if (InputDieEntry->getTag() == dwarf::DW_TAG_variable) { + // Get relocation adjustment value for the current variable. + std::pair> LocExprAddrAndRelocAdjustment = + getContaingFile().Addresses->getVariableRelocAdjustment( + getDIE(InputDieEntry)); + + HasLocationExpressionAddress = LocExprAddrAndRelocAdjustment.first; + if (LocExprAddrAndRelocAdjustment.first && + LocExprAddrAndRelocAdjustment.second) + VarAddressAdjustment = *LocExprAddrAndRelocAdjustment.second; + } + + DIEGenerator DIEGenerator(Allocator, *this); + DIE *ClonedDIE = DIEGenerator.createDIE(InputDieEntry->getTag(), OutOffset); + rememberDieOutOffset(InputDieIdx, OutOffset); + + // Clone Attributes. + DIEAttributeCloner AttributesCloner( + ClonedDIE, *this, InputDieEntry, DIEGenerator, FuncAddressAdjustment, + VarAddressAdjustment, HasLocationExpressionAddress); + AttributesCloner.clone(); + + bool HasChildrenToClone = Info.getKeepChildren(); + OutOffset = AttributesCloner.finalizeAbbreviations(HasChildrenToClone); + + if (HasChildrenToClone) { + // Recursively clone children. + for (const DWARFDebugInfoEntry *CurChild = + getFirstChildEntry(InputDieEntry); + CurChild && CurChild->getAbbreviationDeclarationPtr(); + CurChild = getSiblingEntry(CurChild)) { + if (DIE *ClonedChild = + cloneDIE(CurChild, OutOffset, FuncAddressAdjustment, + VarAddressAdjustment, Allocator)) { + OutOffset = ClonedChild->getOffset() + ClonedChild->getSize(); + DIEGenerator.addChild(ClonedChild); + } + } + + // Account for the end of children marker. + OutOffset += sizeof(int8_t); + } + + // Update our size. + ClonedDIE->setSize(OutOffset - ClonedDIE->getOffset()); + return ClonedDIE; +} + +Error CompileUnit::cloneAndEmitLineTable(Triple &TargetTriple) { + const DWARFDebugLine::LineTable *InputLineTable = + getContaingFile().Dwarf->getLineTableForUnit(&getOrigUnit()); + if (InputLineTable == nullptr) { + warn("cann't load line table."); + return Error::success(); + } + + DWARFDebugLine::LineTable OutLineTable; + + // Set Line Table header. + OutLineTable.Prologue = InputLineTable->Prologue; + OutLineTable.Prologue.FormParams.AddrSize = getFormParams().AddrSize; + + // Set Line Table Rows. + if (getGlobalData().getOptions().UpdateIndexTablesOnly) { + OutLineTable.Rows = InputLineTable->Rows; + // If all the line table contains is a DW_LNE_end_sequence, clear the line + // table rows, it will be inserted again in the DWARFStreamer. + if (OutLineTable.Rows.size() == 1 && OutLineTable.Rows[0].EndSequence) + OutLineTable.Rows.clear(); + + OutLineTable.Sequences = InputLineTable->Sequences; + } else { + // This vector is the output line table. + std::vector NewRows; + NewRows.reserve(InputLineTable->Rows.size()); + + // Current sequence of rows being extracted, before being inserted + // in NewRows. + std::vector Seq; + + const auto &FunctionRanges = getFunctionRanges(); + std::optional CurrRange; + + // FIXME: This logic is meant to generate exactly the same output as + // Darwin's classic dsymutil. There is a nicer way to implement this + // by simply putting all the relocated line info in NewRows and simply + // sorting NewRows before passing it to emitLineTableForUnit. This + // should be correct as sequences for a function should stay + // together in the sorted output. There are a few corner cases that + // look suspicious though, and that required to implement the logic + // this way. Revisit that once initial validation is finished. + + // Iterate over the object file line info and extract the sequences + // that correspond to linked functions. + for (DWARFDebugLine::Row Row : InputLineTable->Rows) { + // Check whether we stepped out of the range. The range is + // half-open, but consider accept the end address of the range if + // it is marked as end_sequence in the input (because in that + // case, the relocation offset is accurate and that entry won't + // serve as the start of another function). + if (!CurrRange || !CurrRange->Range.contains(Row.Address.Address)) { + // We just stepped out of a known range. Insert a end_sequence + // corresponding to the end of the range. + uint64_t StopAddress = + CurrRange ? CurrRange->Range.end() + CurrRange->Value : -1ULL; + CurrRange = FunctionRanges.getRangeThatContains(Row.Address.Address); + if (StopAddress != -1ULL && !Seq.empty()) { + // Insert end sequence row with the computed end address, but + // the same line as the previous one. + auto NextLine = Seq.back(); + NextLine.Address.Address = StopAddress; + NextLine.EndSequence = 1; + NextLine.PrologueEnd = 0; + NextLine.BasicBlock = 0; + NextLine.EpilogueBegin = 0; + Seq.push_back(NextLine); + insertLineSequence(Seq, NewRows); + } + + if (!CurrRange) + continue; + } + + // Ignore empty sequences. + if (Row.EndSequence && Seq.empty()) + continue; + + // Relocate row address and add it to the current sequence. + Row.Address.Address += CurrRange->Value; + Seq.emplace_back(Row); + + if (Row.EndSequence) + insertLineSequence(Seq, NewRows); + } + + OutLineTable.Rows = std::move(NewRows); + } + + return emitDebugLine(TargetTriple, OutLineTable); +} + +void CompileUnit::insertLineSequence(std::vector &Seq, + std::vector &Rows) { + if (Seq.empty()) + return; + + if (!Rows.empty() && Rows.back().Address < Seq.front().Address) { + llvm::append_range(Rows, Seq); + Seq.clear(); + return; + } + + object::SectionedAddress Front = Seq.front().Address; + auto InsertPoint = partition_point( + Rows, [=](const DWARFDebugLine::Row &O) { return O.Address < Front; }); + + // FIXME: this only removes the unneeded end_sequence if the + // sequences have been inserted in order. Using a global sort like + // described in cloneAndEmitLineTable() and delaying the end_sequene + // elimination to DebugLineEmitter::emit() we can get rid of all of them. + if (InsertPoint != Rows.end() && InsertPoint->Address == Front && + InsertPoint->EndSequence) { + *InsertPoint = Seq.front(); + Rows.insert(InsertPoint + 1, Seq.begin() + 1, Seq.end()); + } else { + Rows.insert(InsertPoint, Seq.begin(), Seq.end()); + } + + Seq.clear(); +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void CompileUnit::DIEInfo::dump() { + llvm::errs() << "{\n"; + llvm::errs() << " Placement: "; + switch (getPlacement()) { + case NotSet: + llvm::errs() << "NotSet\n"; + break; + case TypeTable: + llvm::errs() << "TypeTable\n"; + break; + case PlainDwarf: + llvm::errs() << "PlainDwarf\n"; + break; + case Both: + llvm::errs() << "Both\n"; + break; + case Parent: + llvm::errs() << "Parent\n"; + break; + } + + llvm::errs() << " Keep: " << getKeep(); + llvm::errs() << " KeepChildren: " << getKeepChildren(); + llvm::errs() << " ReferrencedBy: " << getReferrencedBy(); + llvm::errs() << " IsInMouduleScope: " << getIsInMouduleScope(); + llvm::errs() << " IsInFunctionScope: " << getIsInFunctionScope(); + llvm::errs() << "}\n"; +} +#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerGlobalData.h b/llvm/lib/DWARFLinkerParallel/DWARFLinkerGlobalData.h new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerGlobalData.h @@ -0,0 +1,159 @@ +//===- DWARFLinkerGlobalData.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_LIB_DWARFLINKERPARALLEL_DWARFLINKERGLOBALDATA_H +#define LLVM_LIB_DWARFLINKERPARALLEL_DWARFLINKERGLOBALDATA_H + +#include "llvm/DWARFLinkerParallel/DWARFLinker.h" +#include "llvm/DWARFLinkerParallel/StringPool.h" +#include "llvm/Support/PerThreadBumpPtrAllocator.h" +#include + +namespace llvm { + +class DWARFDie; + +namespace dwarflinker_parallel { + +using TranslatorFuncTy = std::function; +using MessageHandlerTy = std::function; + +/// linking options +struct DWARFLinkerOptions { + /// DWARF version for the output. + uint16_t TargetDWARFVersion = 0; + + /// Generate processing log to the standard output. + bool Verbose = false; + + /// Print statistics. + bool Statistics = false; + + /// Verify the input DWARF. + bool VerifyInputDWARF = false; + + /// Do not emit output. + bool NoOutput = false; + + /// Do not unique types according to ODR + bool NoODR = false; + + /// Update index tables. + bool UpdateIndexTablesOnly = false; + + /// Whether we want a static variable to force us to keep its enclosing + /// function. + bool KeepFunctionForStatic = false; + + /// Allow to generate valid, but non deterministic output. + bool AllowNonDeterministicOutput = false; + + /// Number of threads. + unsigned Threads = 1; + + /// The accelerator table kinds + SmallVector AccelTables; + + /// Prepend path for the clang modules. + std::string PrependPath; + + /// input verification handler(it might be called asynchronously). + DWARFLinker::InputVerificationHandlerTy InputVerificationHandler = nullptr; + + /// A list of all .swiftinterface files referenced by the debug + /// info, mapping Module name to path on disk. The entries need to + /// be uniqued and sorted and there are only few entries expected + /// per compile unit, which is why this is a std::map. + /// this is dsymutil specific fag. + /// + /// (it might be called asynchronously). + DWARFLinker::SwiftInterfacesMapTy *ParseableSwiftInterfaces = nullptr; + + /// A list of remappings to apply to file paths. + /// + /// (it might be called asynchronously). + DWARFLinker::ObjectPrefixMapTy *ObjectPrefixMap = nullptr; +}; + +class DWARFLinkerImpl; + +/// This class keeps data and services common for the whole linking process. +class LinkingGlobalData { + friend DWARFLinkerImpl; + +public: + /// Returns global per-thread allocator. + parallel::PerThreadBumpPtrAllocator &getAllocator() { return Allocator; } + + /// Returns global string pool. + StringPool &getStringPool() { return Strings; } + + /// Set translation function. + void setTranslator(TranslatorFuncTy Translator) { + this->Translator = Translator; + } + + /// Translate specified string. + StringRef translateString(StringRef String) { + if (Translator) + return Translator(String); + + return String; + } + + /// Returns linking options. + const DWARFLinkerOptions &getOptions() const { return Options; } + + /// Set warning handler. + void setWarningHandler(MessageHandlerTy Handler) { WarningHandler = Handler; } + + /// Set error handler. + void setErrorHandler(MessageHandlerTy Handler) { ErrorHandler = Handler; } + + /// Report warning. + void warn(const Twine &Warning, StringRef Context, + const DWARFDie *DIE = nullptr) { + if (WarningHandler) + (WarningHandler)(Warning, Context, DIE); + } + + /// Report warning. + void warn(Error Warning, StringRef Context, const DWARFDie *DIE = nullptr) { + handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) { + warn(Info.message(), Context, DIE); + }); + } + + /// Report error. + void error(const Twine &Err, StringRef Context, + const DWARFDie *DIE = nullptr) { + if (ErrorHandler) + (ErrorHandler)(Err, Context, DIE); + } + + /// Report error. + void error(Error Err, StringRef Context, const DWARFDie *DIE = nullptr) { + handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) { + error(Info.message(), Context, DIE); + }); + } + +protected: + parallel::PerThreadBumpPtrAllocator Allocator; + StringPool Strings; + TranslatorFuncTy Translator; + DWARFLinkerOptions Options; + MessageHandlerTy WarningHandler; + MessageHandlerTy ErrorHandler; +}; + +} // end of namespace dwarflinker_parallel +} // end namespace llvm + +#endif // LLVM_LIB_DWARFLINKERPARALLEL_DWARFLINKERGLOBALDATA_H diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.h b/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.h --- a/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.h +++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.h @@ -11,29 +11,29 @@ #include "DWARFEmitterImpl.h" #include "DWARFLinkerCompileUnit.h" +#include "StringEntryToDwarfStringPoolEntryMap.h" #include "llvm/ADT/AddressRanges.h" #include "llvm/CodeGen/AccelTable.h" #include "llvm/DWARFLinkerParallel/DWARFLinker.h" #include "llvm/DWARFLinkerParallel/StringPool.h" -#include "llvm/DWARFLinkerParallel/StringTable.h" namespace llvm { namespace dwarflinker_parallel { -using Offset2UnitMapTy = DenseMap; - -struct RangeAttrPatch; -struct LocAttrPatch; - +/// This class links debug info. class DWARFLinkerImpl : public DWARFLinker { public: DWARFLinkerImpl(MessageHandlerTy ErrorHandler, MessageHandlerTy WarningHandler, TranslatorFuncTy StringsTranslator) - : UniqueUnitID(0), ErrorHandler(ErrorHandler), - WarningHandler(WarningHandler), - OutputStrings(Strings, StringsTranslator) {} + : UniqueUnitID(0), DebugStrStrings(GlobalData), + DebugLineStrStrings(GlobalData) { + GlobalData.setTranslator(StringsTranslator); + GlobalData.setErrorHandler(ErrorHandler); + GlobalData.setWarningHandler(WarningHandler); + } + /// Create debug info emitter. Error createEmitter(const Triple &TheTriple, OutputFileType FileType, raw_pwrite_stream &OutFile) override; @@ -47,13 +47,11 @@ /// \pre NoODR, Update options should be set before call to addObjectFile. void addObjectFile( DWARFFile &File, ObjFileLoaderTy Loader = nullptr, - CompileUnitHandlerTy OnCUDieLoaded = [](const DWARFUnit &) {}) override {} + + CompileUnitHandlerTy OnCUDieLoaded = [](const DWARFUnit &) {}) override; /// Link debug info for added files. - Error link() override { - reportWarning("LLVM parallel dwarflinker is not implemented yet.", ""); - return Error::success(); - } + Error link() override; /// \defgroup Methods setting various linking options: /// @@ -61,51 +59,58 @@ /// /// Allows to generate log of linking process to the standard output. - void setVerbosity(bool Verbose) override { Options.Verbose = Verbose; } + void setVerbosity(bool Verbose) override { + GlobalData.Options.Verbose = Verbose; + } /// Print statistics to standard output. void setStatistics(bool Statistics) override { - Options.Statistics = Statistics; + GlobalData.Options.Statistics = Statistics; } /// Verify the input DWARF. void setVerifyInputDWARF(bool Verify) override { - Options.VerifyInputDWARF = Verify; + GlobalData.Options.VerifyInputDWARF = Verify; } /// Do not unique types according to ODR. - void setNoODR(bool NoODR) override { Options.NoODR = NoODR; } + void setNoODR(bool) override { + // FIXME: set option when ODR mode will be supported. + // getOptions().NoODR = NoODR; + GlobalData.Options.NoODR = true; + } /// Update index tables only(do not modify rest of DWARF). void setUpdateIndexTablesOnly(bool UpdateIndexTablesOnly) override { - Options.UpdateIndexTablesOnly = UpdateIndexTablesOnly; + GlobalData.Options.UpdateIndexTablesOnly = UpdateIndexTablesOnly; } /// Allow generating valid, but non-deterministic output. void setAllowNonDeterministicOutput(bool AllowNonDeterministicOutput) override { - Options.AllowNonDeterministicOutput = AllowNonDeterministicOutput; + GlobalData.Options.AllowNonDeterministicOutput = + AllowNonDeterministicOutput; } /// Set to keep the enclosing function for a static variable. void setKeepFunctionForStatic(bool KeepFunctionForStatic) override { - Options.KeepFunctionForStatic = KeepFunctionForStatic; + GlobalData.Options.KeepFunctionForStatic = KeepFunctionForStatic; } /// Use specified number of threads for parallel files linking. void setNumThreads(unsigned NumThreads) override { - Options.Threads = NumThreads; + GlobalData.Options.Threads = NumThreads; } /// Add kind of accelerator tables to be generated. void addAccelTableKind(AccelTableKind Kind) override { - assert(!llvm::is_contained(Options.AccelTables, Kind)); - Options.AccelTables.emplace_back(Kind); + assert(!llvm::is_contained(GlobalData.getOptions().AccelTables, Kind)); + GlobalData.Options.AccelTables.emplace_back(Kind); } /// Set prepend path for clang modules. void setPrependPath(const std::string &Ppath) override { - Options.PrependPath = Ppath; + GlobalData.Options.PrependPath = Ppath; } /// Set estimated objects files amount, for preliminary data allocation. @@ -117,17 +122,17 @@ /// errors. void setInputVerificationHandler(InputVerificationHandlerTy Handler) override { - Options.InputVerificationHandler = Handler; + GlobalData.Options.InputVerificationHandler = Handler; } /// Set map for Swift interfaces. void setSwiftInterfacesMap(SwiftInterfacesMapTy *Map) override { - Options.ParseableSwiftInterfaces = Map; + GlobalData.Options.ParseableSwiftInterfaces = Map; } /// Set prefix map for objects. void setObjectPrefixMap(ObjectPrefixMapTy *Map) override { - Options.ObjectPrefixMap = Map; + GlobalData.Options.ObjectPrefixMap = Map; } /// Set target DWARF version. @@ -137,36 +142,35 @@ "unsupported DWARF version: %d", TargetDWARFVersion); - Options.TargetDWARFVersion = TargetDWARFVersion; + GlobalData.Options.TargetDWARFVersion = TargetDWARFVersion; return Error::success(); } /// @} protected: - /// Reports Warning. - void reportWarning(const Twine &Warning, const DWARFFile &File, - const DWARFDie *DIE = nullptr) const { - if (WarningHandler != nullptr) - WarningHandler(Warning, File.FileName, DIE); - } + /// Verify input DWARF file. + void verifyInput(const DWARFFile &File); - /// Reports Warning. - void reportWarning(const Twine &Warning, StringRef FileName, - const DWARFDie *DIE = nullptr) const { - if (WarningHandler != nullptr) - WarningHandler(Warning, FileName, DIE); - } + /// Validate specified options. + Error validateAndUpdateOptions(); - /// Reports Error. - void reportError(const Twine &Warning, StringRef FileName, - const DWARFDie *DIE = nullptr) const { - if (ErrorHandler != nullptr) - ErrorHandler(Warning, FileName, DIE); - } + /// Take already linked compile units and glue them into single file. + void glueCompileUnitsAndWriteToTheOutput(); + + /// Increment counter for specified \p File. + void addBinHolderNameRef(DWARFFile &File); - /// Returns next available unique Compile Unit ID. - unsigned getNextUniqueUnitID() { return UniqueUnitID.fetch_add(1); } + /// Decrement counter for specified \p File and unload file if counter + /// becomes zero. + void unloadObjectFile(DWARFFile &File); + + /// Hold the input and output of the debug info size in bytes. + struct DebugInfoSize { + uint64_t Input; + uint64_t Output; + }; + friend class DependencyTracker; /// Keeps track of data associated with one object during linking. /// i.e. source file descriptor, compilation units, output data /// for compilation units common tables. @@ -187,8 +191,11 @@ }; using ModuleUnitListTy = SmallVector; + /// Global linking data. + LinkingGlobalData &GlobalData; + /// Object file descriptor. - DWARFFile &File; + DWARFFile &InputDWARFFile; /// Set of Compilation Units(may be accessed asynchroniously for reading). UnitListTy CompileUnits; @@ -199,117 +206,182 @@ /// Size of Debug info before optimizing. uint64_t OriginalDebugInfoSize = 0; - /// Output sections, common for all compilation units. - OutTablesFileTy OutDebugInfoBytes; + /// Flag indicating that all inter-connected units are loaded + /// and the dwarf linking process for these units is started. + bool InterCUProcessingStarted = false; + + StringMap &ClangModules; + + std::optional TargetTriple; + + /// Flag indicating that new inter-connected compilation units were + /// discovered. It is used for restarting units processing + /// if new inter-connected units were found. + std::atomic HasNewInterconnectedCUs = {false}; - /// Endianness for the final file. - support::endianness Endianess = support::endianness::little; + /// Counter for compile units ID. + std::atomic &UniqueUnitID; + + LinkContext(LinkingGlobalData &GlobalData, DWARFFile &File, + StringMap &ClangModules, + std::atomic &UniqueUnitID, + std::optional TargetTriple) + : GlobalData(GlobalData), InputDWARFFile(File), + ClangModules(ClangModules), TargetTriple(TargetTriple), + UniqueUnitID(UniqueUnitID) { + setGlobalData(&GlobalData); - LinkContext(DWARFFile &File) : File(File) { if (File.Dwarf) { if (!File.Dwarf->compile_units().empty()) CompileUnits.reserve(File.Dwarf->getNumCompileUnits()); - - Endianess = File.Dwarf->isLittleEndian() ? support::endianness::little - : support::endianness::big; } } + /// Check whether specified \p CUDie is a Clang module reference. + /// if \p Quiet is false then display error messages. + /// \return first == true if CUDie is a Clang module reference. + /// second == true if module is already loaded. + std::pair isClangModuleRef(const DWARFDie &CUDie, + std::string &PCMFile, + unsigned Indent, bool Quiet); + + /// If this compile unit is really a skeleton CU that points to a + /// clang module, register it in ClangModules and return true. + /// + /// A skeleton CU is a CU without children, a DW_AT_gnu_dwo_name + /// pointing to the module, and a DW_AT_gnu_dwo_id with the module + /// hash. + bool registerModuleReference(const DWARFDie &CUDie, ObjFileLoaderTy Loader, + CompileUnitHandlerTy OnCUDieLoaded, + unsigned Indent = 0); + + /// Recursively add the debug info in this clang module .pcm + /// file (and all the modules imported by it in a bottom-up fashion) + /// to ModuleUnits. + Error loadClangModule(ObjFileLoaderTy Loader, const DWARFDie &CUDie, + const std::string &PCMFile, + CompileUnitHandlerTy OnCUDieLoaded, + unsigned Indent = 0); + /// Add Compile Unit corresponding to the module. void addModulesCompileUnit(RefModuleUnit &&Unit) { ModulesCompileUnits.emplace_back(std::move(Unit)); } - /// Return Endiannes of the source DWARF information. - support::endianness getEndianness() { return Endianess; } + /// Computes the total size of the debug info. + uint64_t getInputDebugInfoSize() const { + uint64_t Size = 0; - /// \returns pointer to compilation unit which corresponds \p Offset. - CompileUnit *getUnitForOffset(CompileUnit &CU, uint64_t Offset) const; - }; + if (InputDWARFFile.Dwarf == nullptr) + return Size; + + for (auto &Unit : InputDWARFFile.Dwarf->compile_units()) + Size += Unit->getLength(); + + return Size; + } - /// linking options - struct DWARFLinkerOptions { - /// DWARF version for the output. - uint16_t TargetDWARFVersion = 0; + /// Link compile units for this context. + Error link(); - /// Generate processing log to the standard output. - bool Verbose = false; + /// Link specified compile unit until specified stage. + void linkSingleCompileUnit( + CompileUnit &CU, + enum CompileUnit::Stage DoUntilStage = CompileUnit::Stage::Cleaned); - /// Print statistics. - bool Statistics = false; + /// Emit invariant sections. + Error emitInvariantSections(); - /// Verify the input DWARF. - bool VerifyInputDWARF = false; + /// Clone and emit .debug_frame. + Error cloneAndEmitDebugFrame(); - /// Do not unique types according to ODR - bool NoODR = false; + /// Emit FDE record. + void emitFDE(uint32_t CIEOffset, uint32_t AddrSize, uint64_t Address, + StringRef FDEBytes, SectionDescriptor &Section); - /// Update index tables. - bool UpdateIndexTablesOnly = false; + /// Clone and emit paper trails. + Error cloneAndEmitPaperTrails(); - /// Whether we want a static variable to force us to keep its enclosing - /// function. - bool KeepFunctionForStatic = false; + std::function getUnitForOffset = + [&](uint64_t Offset) -> CompileUnit * { + auto CU = llvm::upper_bound( + CompileUnits, Offset, + [](uint64_t LHS, const std::unique_ptr &RHS) { + return LHS < RHS->getOrigUnit().getNextUnitOffset(); + }); - /// Allow to generate valid, but non deterministic output. - bool AllowNonDeterministicOutput = false; + return CU != CompileUnits.end() ? CU->get() : nullptr; + }; + }; - /// Number of threads. - unsigned Threads = 1; + /// Enumerate all compile units and assign offsets to their sections and + /// strings. + void assignOffsets(); - /// The accelerator table kinds - SmallVector AccelTables; + /// Enumerate all compile units and assign offsets to their sections. + void assignOffsetsToSections(); - /// Prepend path for the clang modules. - std::string PrependPath; + /// Enumerate all compile units and assign offsets to their strings. + void assignOffsetsToStrings(); - /// input verification handler(it might be called asynchronously). - InputVerificationHandlerTy InputVerificationHandler = nullptr; + /// Enumerates specified string patches, assigns offset and index. + template + void assignOffsetsToStringsImpl( + ArrayList &Section, size_t &IndexAccumulator, + uint64_t &OffsetAccumulator, + StringEntryToDwarfStringPoolEntryMap &StringsForEmission); - /// A list of all .swiftinterface files referenced by the debug - /// info, mapping Module name to path on disk. The entries need to - /// be uniqued and sorted and there are only few entries expected - /// per compile unit, which is why this is a std::map. - /// this is dsymutil specific fag. - /// - /// (it might be called asynchronously). - SwiftInterfacesMapTy *ParseableSwiftInterfaces = nullptr; + /// Print statistic for processed Debug Info. + void printStatistic(); - /// A list of remappings to apply to file paths. - /// - /// (it might be called asynchronously). - ObjectPrefixMapTy *ObjectPrefixMap = nullptr; - } Options; + /// Enumerates sections for modules, invariant for object files, compile + /// units. + void forEachSectionsSet( + function_ref SectionsSetHandler); + + /// Enumerates all patches and update them with the correct values. + void patchOffsetsAndSizes(); + + /// Enumerate all compile units and put their data into the output stream. + void writeDWARFToTheOutput(); /// \defgroup Data members accessed asinchroniously. /// /// @{ /// Unique ID for compile unit. - std::atomic UniqueUnitID; - - /// Strings pool. Keeps all strings. - StringPool Strings; + std::atomic UniqueUnitID; - /// error handler(it might be called asynchronously). - MessageHandlerTy ErrorHandler = nullptr; + /// BinaryHolderRefCounter counts whether references to the binary + /// files exist. + DenseMap BinaryHolderRefCounter; + std::mutex BinaryHolderRefMutex; - /// warning handler(it might be called asynchronously). - MessageHandlerTy WarningHandler = nullptr; + /// Mapping the PCM filename to the DwoId. + StringMap ClangModules; + std::mutex ClangModulesMutex; /// @} /// \defgroup Data members accessed sequentially. /// /// @{ + /// DwarfStringPoolEntries for .debug_str section. + StringEntryToDwarfStringPoolEntryMap DebugStrStrings; - /// Set of strings which should be emitted. - StringTable OutputStrings; + /// DwarfStringPoolEntries for .debug_line_str section. + StringEntryToDwarfStringPoolEntryMap DebugLineStrStrings; /// Keeps all linking contexts. SmallVector> ObjectContexts; /// The emitter of final dwarf file. std::unique_ptr TheDwarfEmitter; + + /// Overall compile units number. + uint64_t OverallNumberOfCU = 0; + + /// Data global for the whole linking process. + LinkingGlobalData GlobalData; /// @} }; diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.cpp b/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.cpp --- a/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.cpp +++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.cpp @@ -7,33 +7,20 @@ //===----------------------------------------------------------------------===// #include "DWARFLinkerImpl.h" +#include "DIEGenerator.h" +#include "DependencyTracker.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/Parallel.h" +#include "llvm/Support/ThreadPool.h" namespace llvm { namespace dwarflinker_parallel { -/// Similar to DWARFUnitSection::getUnitForOffset(), but returning our -/// CompileUnit object instead. -CompileUnit * -DWARFLinkerImpl::LinkContext::getUnitForOffset(CompileUnit &CurrentCU, - uint64_t Offset) const { - if (CurrentCU.isClangModule()) - return &CurrentCU; - - auto CU = llvm::upper_bound( - CompileUnits, Offset, - [](uint64_t LHS, const std::unique_ptr &RHS) { - return LHS < RHS->getOrigUnit().getNextUnitOffset(); - }); - - return CU != CompileUnits.end() ? CU->get() : nullptr; -} - Error DWARFLinkerImpl::createEmitter(const Triple &TheTriple, OutputFileType FileType, raw_pwrite_stream &OutFile) { - TheDwarfEmitter = std::make_unique( - FileType, OutFile, OutputStrings.getTranslator(), WarningHandler); + TheDwarfEmitter = std::make_unique(FileType, OutFile); return TheDwarfEmitter->init(TheTriple, "__DWARF"); } @@ -42,5 +29,1012 @@ return TheDwarfEmitter.get(); } +void DWARFLinkerImpl::addObjectFile(DWARFFile &File, ObjFileLoaderTy Loader, + CompileUnitHandlerTy OnCUDieLoaded) { + ObjectContexts.emplace_back(std::make_unique( + GlobalData, File, ClangModules, UniqueUnitID, + (TheDwarfEmitter.get() == nullptr ? std::optional(std::nullopt) + : TheDwarfEmitter->getTargetTriple()))); + + // Count references to source files. + addBinHolderNameRef(ObjectContexts.back()->InputDWARFFile); + + if (ObjectContexts.back()->InputDWARFFile.Dwarf) { + for (const std::unique_ptr &CU : + ObjectContexts.back()->InputDWARFFile.Dwarf->compile_units()) { + DWARFDie CUDie = CU->getUnitDIE(); + OverallNumberOfCU++; + + if (!CUDie) + continue; + + OnCUDieLoaded(*CU); + + // Register mofule reference. + if (!GlobalData.getOptions().UpdateIndexTablesOnly) + ObjectContexts.back()->registerModuleReference(CUDie, Loader, + OnCUDieLoaded); + } + } +} + +Error DWARFLinkerImpl::link() { + // reset compile unit unique ID counter. + UniqueUnitID = 0; + + if (Error Err = validateAndUpdateOptions()) + return Err; + + if (GlobalData.getOptions().Verbose) { + for (std::unique_ptr &Context : ObjectContexts) { + outs() << "OBJECT: " << Context->InputDWARFFile.FileName << "\n"; + + if (Context->InputDWARFFile.Dwarf) { + for (const std::unique_ptr &OrigCU : + Context->InputDWARFFile.Dwarf->compile_units()) { + outs() << "Input compilation unit:"; + DIDumpOptions DumpOpts; + DumpOpts.ChildRecurseDepth = 0; + DumpOpts.Verbose = GlobalData.getOptions().Verbose; + OrigCU->getUnitDIE().dump(outs(), 0, DumpOpts); + } + } + } + } + + // Verify input DWARF if requested. + if (GlobalData.getOptions().VerifyInputDWARF) { + for (std::unique_ptr &Context : ObjectContexts) { + if (Context->InputDWARFFile.Dwarf) { + if (GlobalData.getOptions().VerifyInputDWARF) + verifyInput(Context->InputDWARFFile); + } + } + } + + // Set parallel options. + if (GlobalData.getOptions().Threads == 0) + parallel::strategy = optimal_concurrency(OverallNumberOfCU); + else + parallel::strategy = hardware_concurrency(GlobalData.getOptions().Threads); + + // Link object files. + if (GlobalData.getOptions().Threads == 1) { + for (std::unique_ptr &Context : ObjectContexts) { + // Link object file. + if (Error Err = Context->link()) + GlobalData.error(std::move(Err), Context->InputDWARFFile.FileName); + + unloadObjectFile(Context->InputDWARFFile); + } + } else { + ThreadPool Pool(parallel::strategy); + for (std::unique_ptr &Context : ObjectContexts) + Pool.async([&]() { + // Link object file. + if (Error Err = Context->link()) + GlobalData.error(std::move(Err), Context->InputDWARFFile.FileName); + + unloadObjectFile(Context->InputDWARFFile); + }); + + Pool.wait(); + } + + // At this stage each compile units are cloned to their own set of debug + // sections. Now, update patches, assign offsets and assemble final file + // glueing debug tables from each compile unit. + glueCompileUnitsAndWriteToTheOutput(); + + return Error::success(); +} + +void DWARFLinkerImpl::verifyInput(const DWARFFile &File) { + assert(File.Dwarf); + + raw_ostream &os = GlobalData.getOptions().Verbose ? errs() : nulls(); + DIDumpOptions DumpOpts; + if (!File.Dwarf->verify(os, DumpOpts.noImplicitRecursion())) { + if (GlobalData.getOptions().InputVerificationHandler) + GlobalData.getOptions().InputVerificationHandler(File); + } +} + +Error DWARFLinkerImpl::validateAndUpdateOptions() { + if (GlobalData.getOptions().TargetDWARFVersion == 0) + return createStringError(std::errc::invalid_argument, + "target DWARF version is not set"); + + GlobalData.Options.NoOutput = TheDwarfEmitter.get() == nullptr; + + if (GlobalData.getOptions().Verbose && GlobalData.getOptions().Threads != 1) { + GlobalData.Options.Threads = 1; + GlobalData.warn( + "set number of threads to 1 to make --verbose to work properly.", ""); + } + + return Error::success(); +} + +static bool isArchive(StringRef Name) { return Name.endswith(")"); } + +static StringRef getBinaryHolderName(StringRef Name) { + if (isArchive(Name)) + return Name.substr(0, Name.rfind('(')); + + return Name; +} + +void DWARFLinkerImpl::addBinHolderNameRef(DWARFFile &File) { + BinaryHolderRefCounter[getBinaryHolderName(File.FileName)]++; +} + +void DWARFLinkerImpl::unloadObjectFile(DWARFFile &File) { + std::unique_lock Guard(BinaryHolderRefMutex); + + assert(BinaryHolderRefCounter[getBinaryHolderName(File.FileName)] != 0); + + // we could not simply unload object file here. + // Object file might be a member of an archive. + // Unloading object file in such case would also + // unload containing archive. Thus we count references + // to the archive and unload it when there are no + // more unprocessed archive members. + BinaryHolderRefCounter[getBinaryHolderName(File.FileName)]--; + + if (BinaryHolderRefCounter[getBinaryHolderName(File.FileName)] == 0) + File.unload(); +} + +/// Resolve the relative path to a build artifact referenced by DWARF by +/// applying DW_AT_comp_dir. +static void resolveRelativeObjectPath(SmallVectorImpl &Buf, DWARFDie CU) { + sys::path::append(Buf, dwarf::toString(CU.find(dwarf::DW_AT_comp_dir), "")); +} + +static uint64_t getDwoId(const DWARFDie &CUDie) { + auto DwoId = dwarf::toUnsigned( + CUDie.find({dwarf::DW_AT_dwo_id, dwarf::DW_AT_GNU_dwo_id})); + if (DwoId) + return *DwoId; + return 0; +} + +static std::string +remapPath(StringRef Path, + const DWARFLinker::ObjectPrefixMapTy &ObjectPrefixMap) { + if (ObjectPrefixMap.empty()) + return Path.str(); + + SmallString<256> p = Path; + for (const auto &Entry : ObjectPrefixMap) + if (llvm::sys::path::replace_path_prefix(p, Entry.first, Entry.second)) + break; + return p.str().str(); +} + +static std::string getPCMFile(const DWARFDie &CUDie, + DWARFLinker::ObjectPrefixMapTy *ObjectPrefixMap) { + std::string PCMFile = dwarf::toString( + CUDie.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), ""); + + if (PCMFile.empty()) + return PCMFile; + + if (ObjectPrefixMap) + PCMFile = remapPath(PCMFile, *ObjectPrefixMap); + + return PCMFile; +} + +std::pair DWARFLinkerImpl::LinkContext::isClangModuleRef( + const DWARFDie &CUDie, std::string &PCMFile, unsigned Indent, bool Quiet) { + if (PCMFile.empty()) + return std::make_pair(false, false); + + // Clang module DWARF skeleton CUs abuse this for the path to the module. + uint64_t DwoId = getDwoId(CUDie); + + std::string Name = dwarf::toString(CUDie.find(dwarf::DW_AT_name), ""); + if (Name.empty()) { + if (!Quiet) + GlobalData.warn("anonymous module skeleton CU for " + PCMFile + ".", + InputDWARFFile.FileName); + return std::make_pair(true, true); + } + + if (!Quiet && GlobalData.getOptions().Verbose) { + outs().indent(Indent); + outs() << "Found clang module reference " << PCMFile; + } + + auto Cached = ClangModules.find(PCMFile); + if (Cached != ClangModules.end()) { + // FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is + // fixed in clang, only warn about DWO_id mismatches in verbose mode. + // ASTFileSignatures will change randomly when a module is rebuilt. + if (!Quiet && GlobalData.getOptions().Verbose && (Cached->second != DwoId)) + GlobalData.warn( + Twine("hash mismatch: this object file was built against a " + "different version of the module ") + + PCMFile + ".", + InputDWARFFile.FileName); + if (!Quiet && GlobalData.getOptions().Verbose) + outs() << " [cached].\n"; + return std::make_pair(true, true); + } + + return std::make_pair(true, false); +} + +/// If this compile unit is really a skeleton CU that points to a +/// clang module, register it in ClangModules and return true. +/// +/// A skeleton CU is a CU without children, a DW_AT_gnu_dwo_name +/// pointing to the module, and a DW_AT_gnu_dwo_id with the module +/// hash. +bool DWARFLinkerImpl::LinkContext::registerModuleReference( + const DWARFDie &CUDie, ObjFileLoaderTy Loader, + CompileUnitHandlerTy OnCUDieLoaded, unsigned Indent) { + std::string PCMFile = + getPCMFile(CUDie, GlobalData.getOptions().ObjectPrefixMap); + std::pair IsClangModuleRef = + isClangModuleRef(CUDie, PCMFile, Indent, false); + + if (!IsClangModuleRef.first) + return false; + + if (IsClangModuleRef.second) + return true; + + if (GlobalData.getOptions().Verbose) + outs() << " ...\n"; + + // Cyclic dependencies are disallowed by Clang, but we still + // shouldn't run into an infinite loop, so mark it as processed now. + ClangModules.insert({PCMFile, getDwoId(CUDie)}); + + if (Error E = + loadClangModule(Loader, CUDie, PCMFile, OnCUDieLoaded, Indent + 2)) { + consumeError(std::move(E)); + return false; + } + return true; +} + +Error DWARFLinkerImpl::LinkContext::loadClangModule( + ObjFileLoaderTy Loader, const DWARFDie &CUDie, const std::string &PCMFile, + CompileUnitHandlerTy OnCUDieLoaded, unsigned Indent) { + + uint64_t DwoId = getDwoId(CUDie); + std::string ModuleName = dwarf::toString(CUDie.find(dwarf::DW_AT_name), ""); + + /// Using a SmallString<0> because loadClangModule() is recursive. + SmallString<0> Path(GlobalData.getOptions().PrependPath); + if (sys::path::is_relative(PCMFile)) + resolveRelativeObjectPath(Path, CUDie); + sys::path::append(Path, PCMFile); + // Don't use the cached binary holder because we have no thread-safety + // guarantee and the lifetime is limited. + + if (Loader == nullptr) { + GlobalData.error("cann't load clang module: loader is not specified.", + InputDWARFFile.FileName); + return Error::success(); + } + + auto ErrOrObj = Loader(InputDWARFFile.FileName, Path); + if (!ErrOrObj) + return Error::success(); + + std::unique_ptr Unit; + for (const auto &CU : ErrOrObj->Dwarf->compile_units()) { + OnCUDieLoaded(*CU); + // Recursively get all modules imported by this one. + auto ChildCUDie = CU->getUnitDIE(); + if (!ChildCUDie) + continue; + if (!registerModuleReference(ChildCUDie, Loader, OnCUDieLoaded, Indent)) { + if (Unit) { + std::string Err = + (PCMFile + + ": Clang modules are expected to have exactly 1 compile unit.\n"); + GlobalData.error(Err, InputDWARFFile.FileName); + return make_error(Err, inconvertibleErrorCode()); + } + // FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is + // fixed in clang, only warn about DWO_id mismatches in verbose mode. + // ASTFileSignatures will change randomly when a module is rebuilt. + uint64_t PCMDwoId = getDwoId(ChildCUDie); + if (PCMDwoId != DwoId) { + if (GlobalData.getOptions().Verbose) + GlobalData.warn( + Twine("hash mismatch: this object file was built against a " + "different version of the module ") + + PCMFile + ".", + InputDWARFFile.FileName); + // Update the cache entry with the DwoId of the module loaded from disk. + ClangModules[PCMFile] = PCMDwoId; + } + + // Empty modules units should not be cloned. + if (!ChildCUDie.hasChildren()) + continue; + + // Add this module. + Unit = std::make_unique( + GlobalData, *CU, UniqueUnitID.fetch_add(1), ModuleName, *ErrOrObj, + getUnitForOffset); + } + } + + if (Unit) { + ModulesCompileUnits.emplace_back(RefModuleUnit{*ErrOrObj, std::move(Unit)}); + // Preload line table, as it can't be loaded asynchronously. + ModulesCompileUnits.back().Unit->loadLineTable(); + } + + return Error::success(); +} + +Error DWARFLinkerImpl::LinkContext::link() { + InterCUProcessingStarted = false; + if (InputDWARFFile.Warnings.empty()) { + if (!InputDWARFFile.Dwarf) + return Error::success(); + + // Preload macro tables, as they can't be loaded asynchronously. + InputDWARFFile.Dwarf->getDebugMacinfo(); + InputDWARFFile.Dwarf->getDebugMacro(); + + // Link modules compile units first. + parallelForEach(ModulesCompileUnits, [&](RefModuleUnit &RefModule) { + linkSingleCompileUnit(*RefModule.Unit); + }); + + // Check for live relocations. If there is no any live relocation then we + // can skip entire object file. + if (!GlobalData.getOptions().UpdateIndexTablesOnly && + !InputDWARFFile.Addresses->hasValidRelocs()) { + if (GlobalData.getOptions().Verbose) + outs() << "No valid relocations found. Skipping.\n"; + return Error::success(); + } + + OriginalDebugInfoSize = getInputDebugInfoSize(); + + // Create CompileUnit structures to keep information about source + // DWARFUnit`s, load line tables. + for (const auto &OrigCU : InputDWARFFile.Dwarf->compile_units()) { + // Load only unit DIE at this stage. + auto CUDie = OrigCU->getUnitDIE(); + std::string PCMFile = + getPCMFile(CUDie, GlobalData.getOptions().ObjectPrefixMap); + + // The !isClangModuleRef condition effectively skips over fully resolved + // skeleton units. + if (!CUDie || GlobalData.getOptions().UpdateIndexTablesOnly || + !isClangModuleRef(CUDie, PCMFile, 0, true).first) { + CompileUnits.emplace_back(std::make_unique( + GlobalData, *OrigCU, UniqueUnitID.fetch_add(1), "", InputDWARFFile, + getUnitForOffset)); + + // Preload line table, as it can't be loaded asynchronously. + CompileUnits.back()->loadLineTable(); + } + }; + + HasNewInterconnectedCUs = false; + + // Link self-sufficient compile units and discover inter-connected compile + // units. + parallelForEach(CompileUnits, [&](std::unique_ptr &CU) { + linkSingleCompileUnit(*CU); + }); + + // Link all inter-connected units. + if (HasNewInterconnectedCUs) { + InterCUProcessingStarted = true; + + do { + HasNewInterconnectedCUs = false; + + // Load inter-connected units. + parallelForEach(CompileUnits, [&](std::unique_ptr &CU) { + if (CU->isInterconnectedCU()) { + CU->maybeResetToLoadedStage(); + linkSingleCompileUnit(*CU, CompileUnit::Stage::Loaded); + } + }); + + // Do liveness analysis for inter-connected units. + parallelForEach(CompileUnits, [&](std::unique_ptr &CU) { + linkSingleCompileUnit(*CU, CompileUnit::Stage::LivenessAnalysisDone); + }); + } while (HasNewInterconnectedCUs); + + // Clone inter-connected units. + parallelForEach(CompileUnits, [&](std::unique_ptr &CU) { + linkSingleCompileUnit(*CU, CompileUnit::Stage::Cloned); + }); + + // Update patches for inter-connected units. + parallelForEach(CompileUnits, [&](std::unique_ptr &CU) { + linkSingleCompileUnit(*CU, CompileUnit::Stage::PatchesUpdated); + }); + + // Release data. + parallelForEach(CompileUnits, [&](std::unique_ptr &CU) { + linkSingleCompileUnit(*CU, CompileUnit::Stage::Cleaned); + }); + } + } + + if (!InputDWARFFile.Warnings.empty()) { + // Create compile unit with paper trail warnings. + + Error ResultErr = Error::success(); + // We use task group here as PerThreadBumpPtrAllocator should be called from + // the threads created by ThreadPoolExecutor. + parallel::TaskGroup TGroup; + TGroup.spawn([&]() { + if (Error Err = cloneAndEmitPaperTrails()) + ResultErr = std::move(Err); + }); + return ResultErr; + } else if (GlobalData.getOptions().UpdateIndexTablesOnly) { + // Emit Invariant sections. + + if (Error Err = emitInvariantSections()) + return Err; + } else if (!CompileUnits.empty()) { + // Emit .debug_frame section. + + Error ResultErr = Error::success(); + parallel::TaskGroup TGroup; + // We use task group here as PerThreadBumpPtrAllocator should be called from + // the threads created by ThreadPoolExecutor. + TGroup.spawn([&]() { + if (Error Err = cloneAndEmitDebugFrame()) + ResultErr = std::move(Err); + }); + return ResultErr; + } + + return Error::success(); +} + +void DWARFLinkerImpl::LinkContext::linkSingleCompileUnit( + CompileUnit &CU, enum CompileUnit::Stage DoUntilStage) { + while (CU.getStage() < DoUntilStage) { + if (InterCUProcessingStarted != CU.isInterconnectedCU()) + return; + + switch (CU.getStage()) { + case CompileUnit::Stage::CreatedNotLoaded: { + // Load input compilation unit DIEs. + // Analyze properties of DIEs. + if (!CU.loadInputDIEs()) { + // We do not need to do liveness analysis for invalud compilation unit. + CU.setStage(CompileUnit::Stage::LivenessAnalysisDone); + } else { + CU.analyzeDWARFStructure(); + + // The registerModuleReference() condition effectively skips + // over fully resolved skeleton units. This second pass of + // registerModuleReferences doesn't do any new work, but it + // will collect top-level errors, which are suppressed. Module + // warnings were already displayed in the first iteration. + if (registerModuleReference( + CU.getOrigUnit().getUnitDIE(), nullptr, + [](const DWARFUnit &) {}, 0)) + CU.setStage(CompileUnit::Stage::PatchesUpdated); + else + CU.setStage(CompileUnit::Stage::Loaded); + } + } break; + + case CompileUnit::Stage::Loaded: { + // Mark all the DIEs that need to be present in the generated output. + // If ODR requested, build type names. + if (!DependencyTracker(*this).resolveDependenciesAndMarkLiveness(CU)) { + assert(HasNewInterconnectedCUs); + return; + } + + CU.setStage(CompileUnit::Stage::LivenessAnalysisDone); + } break; + + case CompileUnit::Stage::LivenessAnalysisDone: + +#ifndef NDEBUG + DependencyTracker::verifyKeepChain(CU); +#endif + + // Clone input compile unit. + if (CU.isClangModule() || GlobalData.getOptions().UpdateIndexTablesOnly || + CU.getContaingFile().Addresses->hasValidRelocs()) { + if (Error Err = CU.cloneAndEmit(TargetTriple)) + CU.error(std::move(Err)); + } + + CU.setStage(CompileUnit::Stage::Cloned); + break; + + case CompileUnit::Stage::Cloned: + // Update DIEs referencies. + CU.updateDieRefPatchesWithClonedOffsets(); + CU.setStage(CompileUnit::Stage::PatchesUpdated); + break; + + case CompileUnit::Stage::PatchesUpdated: + // Cleanup resources. + CU.cleanupDataAfterClonning(); + CU.setStage(CompileUnit::Stage::Cleaned); + break; + + case CompileUnit::Stage::Cleaned: + assert(false); + break; + } + } +} + +Error DWARFLinkerImpl::LinkContext::emitInvariantSections() { + if (GlobalData.getOptions().NoOutput) + return Error::success(); + + getSectionDescriptor(DebugSectionKind::DebugLoc).OS + << InputDWARFFile.Dwarf->getDWARFObj().getLocSection().Data; + getSectionDescriptor(DebugSectionKind::DebugLocLists).OS + << InputDWARFFile.Dwarf->getDWARFObj().getLoclistsSection().Data; + getSectionDescriptor(DebugSectionKind::DebugRange).OS + << InputDWARFFile.Dwarf->getDWARFObj().getRangesSection().Data; + getSectionDescriptor(DebugSectionKind::DebugRngLists).OS + << InputDWARFFile.Dwarf->getDWARFObj().getRnglistsSection().Data; + getSectionDescriptor(DebugSectionKind::DebugFrame).OS + << InputDWARFFile.Dwarf->getDWARFObj().getFrameSection().Data; + getSectionDescriptor(DebugSectionKind::DebugARanges).OS + << InputDWARFFile.Dwarf->getDWARFObj().getArangesSection(); + getSectionDescriptor(DebugSectionKind::DebugAddr).OS + << InputDWARFFile.Dwarf->getDWARFObj().getAddrSection().Data; + + return Error::success(); +} + +Error DWARFLinkerImpl::LinkContext::cloneAndEmitDebugFrame() { + if (GlobalData.getOptions().NoOutput) + return Error::success(); + + if (InputDWARFFile.Dwarf.get() == nullptr) + return Error::success(); + + const DWARFObject &InputDWARFObj = InputDWARFFile.Dwarf->getDWARFObj(); + + StringRef OrigFrameData = InputDWARFObj.getFrameSection().Data; + if (OrigFrameData.empty()) + return Error::success(); + + RangesTy AllUnitsRanges; + for (std::unique_ptr &Unit : CompileUnits) { + for (auto CurRange : Unit->getFunctionRanges()) + AllUnitsRanges.insert(CurRange.Range, CurRange.Value); + } + + unsigned SrcAddrSize = InputDWARFObj.getAddressSize(); + + SectionDescriptor &OutSection = + getSectionDescriptor(DebugSectionKind::DebugFrame); + + DataExtractor Data(OrigFrameData, InputDWARFObj.isLittleEndian(), 0); + uint64_t InputOffset = 0; + + // Store the data of the CIEs defined in this object, keyed by their + // offsets. + DenseMap LocalCIES; + + /// The CIEs that have been emitted in the output section. The actual CIE + /// data serves a the key to this StringMap. + StringMap EmittedCIEs; + + while (Data.isValidOffset(InputOffset)) { + uint64_t EntryOffset = InputOffset; + uint32_t InitialLength = Data.getU32(&InputOffset); + if (InitialLength == 0xFFFFFFFF) + return createFileError(InputDWARFObj.getFileName(), + createStringError(std::errc::invalid_argument, + "Dwarf64 bits no supported")); + + uint32_t CIEId = Data.getU32(&InputOffset); + if (CIEId == 0xFFFFFFFF) { + // This is a CIE, store it. + StringRef CIEData = OrigFrameData.substr(EntryOffset, InitialLength + 4); + LocalCIES[EntryOffset] = CIEData; + // The -4 is to account for the CIEId we just read. + InputOffset += InitialLength - 4; + continue; + } + + uint64_t Loc = Data.getUnsigned(&InputOffset, SrcAddrSize); + + // Some compilers seem to emit frame info that doesn't start at + // the function entry point, thus we can't just lookup the address + // in the debug map. Use the AddressInfo's range map to see if the FDE + // describes something that we can relocate. + std::optional Range = + AllUnitsRanges.getRangeThatContains(Loc); + if (!Range) { + // The +4 is to account for the size of the InitialLength field itself. + InputOffset = EntryOffset + InitialLength + 4; + continue; + } + + // This is an FDE, and we have a mapping. + // Have we already emitted a corresponding CIE? + StringRef CIEData = LocalCIES[CIEId]; + if (CIEData.empty()) + return createFileError( + InputDWARFObj.getFileName(), + createStringError(std::errc::invalid_argument, + "Inconsistent debug_frame content. Dropping.")); + + uint64_t OffsetToCIERecord = OutSection.OS.tell(); + + // Look if we already emitted a CIE that corresponds to the + // referenced one (the CIE data is the key of that lookup). + auto IteratorInserted = + EmittedCIEs.insert(std::make_pair(CIEData, OffsetToCIERecord)); + OffsetToCIERecord = IteratorInserted.first->getValue(); + + // Emit CIE for this ID if it is not emitted yet. + if (IteratorInserted.second) + OutSection.OS << CIEData; + + // Remember offset to the FDE record, so that we might update + // field referencing CIE record(containing OffsetToCIERecord), + // when final offsets are known. OffsetToCIERecord(which is written later) + // is local to the current .debug_frame section, it should be updated + // with final offset of the .debug_frame section. + OutSection.notePatch( + DebugOffsetPatch{OutSection.OS.tell() + 4, &OutSection, true}); + + // Emit the FDE with updated address and CIE pointer. + // (4 + AddrSize) is the size of the CIEId + initial_location + // fields that will get reconstructed by emitFDE(). + unsigned FDERemainingBytes = InitialLength - (4 + SrcAddrSize); + emitFDE(OffsetToCIERecord, SrcAddrSize, Loc + Range->Value, + OrigFrameData.substr(InputOffset, FDERemainingBytes), OutSection); + InputOffset += FDERemainingBytes; + } + + return Error::success(); +} + +/// Emit a FDE into the debug_frame section. \p FDEBytes +/// contains the FDE data without the length, CIE offset and address +/// which will be replaced with the parameter values. +void DWARFLinkerImpl::LinkContext::emitFDE(uint32_t CIEOffset, + uint32_t AddrSize, uint64_t Address, + StringRef FDEBytes, + SectionDescriptor &Section) { + Section.emitIntVal(FDEBytes.size() + 4 + AddrSize, 4); + Section.emitIntVal(CIEOffset, 4); + Section.emitIntVal(Address, AddrSize); + Section.OS.write(FDEBytes.data(), FDEBytes.size()); +} + +Error DWARFLinkerImpl::LinkContext::cloneAndEmitPaperTrails() { + + CompileUnits.emplace_back( + std::make_unique(GlobalData, UniqueUnitID.fetch_add(1), "", + InputDWARFFile, getUnitForOffset)); + + CompileUnit &CU = *CompileUnits.back(); + + BumpPtrAllocator Allocator; + + DIEGenerator ParentGenerator(Allocator, CU); + + SectionDescriptor &DebugInfoSection = + CU.getSectionDescriptor(DebugSectionKind::DebugInfo); + OffsetsPtrVector PatchesOffsets; + + uint64_t CurrentOffset = CU.getHeaderSize(); + DIE *CUDie = + ParentGenerator.createDIE(dwarf::DW_TAG_compile_unit, CurrentOffset); + CU.setOutUnitDIE(CUDie); + + DebugInfoSection.notePatchWithOffsetUpdate( + DebugStrPatch{{CurrentOffset}, + GlobalData.getStringPool().insert("dsymutil").first}, + PatchesOffsets); + CurrentOffset += ParentGenerator + .addStringPlaceholderAttribute(dwarf::DW_AT_producer, + dwarf::DW_FORM_strp) + .second; + + CurrentOffset += + ParentGenerator + .addInplaceString(dwarf::DW_AT_name, InputDWARFFile.FileName) + .second; + + size_t SizeAbbrevNumber = ParentGenerator.finalizeAbbreviations(true); + CurrentOffset += SizeAbbrevNumber; + for (uint64_t *OffsetPtr : PatchesOffsets) + *OffsetPtr += SizeAbbrevNumber; + for (const auto &Warning : CU.getContaingFile().Warnings) { + PatchesOffsets.clear(); + DIEGenerator ChildGenerator(Allocator, CU); + + DIE *ChildDie = + ChildGenerator.createDIE(dwarf::DW_TAG_constant, CurrentOffset); + ParentGenerator.addChild(ChildDie); + + DebugInfoSection.notePatchWithOffsetUpdate( + DebugStrPatch{ + {CurrentOffset}, + GlobalData.getStringPool().insert("dsymutil_warning").first}, + PatchesOffsets); + CurrentOffset += ChildGenerator + .addStringPlaceholderAttribute(dwarf::DW_AT_name, + dwarf::DW_FORM_strp) + .second; + + CurrentOffset += + ChildGenerator + .addScalarAttribute(dwarf::DW_AT_artificial, dwarf::DW_FORM_flag, 1) + .second; + + DebugInfoSection.notePatchWithOffsetUpdate( + DebugStrPatch{{CurrentOffset}, + GlobalData.getStringPool().insert(Warning).first}, + PatchesOffsets); + CurrentOffset += ChildGenerator + .addStringPlaceholderAttribute( + dwarf::DW_AT_const_value, dwarf::DW_FORM_strp) + .second; + + SizeAbbrevNumber = ChildGenerator.finalizeAbbreviations(false); + + CurrentOffset += SizeAbbrevNumber; + for (uint64_t *OffsetPtr : PatchesOffsets) + *OffsetPtr += SizeAbbrevNumber; + + ChildDie->setSize(CurrentOffset - ChildDie->getOffset()); + } + + CurrentOffset += 1; // End of children + CUDie->setSize(CurrentOffset - CUDie->getOffset()); + + uint64_t UnitSize = 0; + UnitSize += CU.getHeaderSize(); + UnitSize += CUDie->getSize(); + CU.setUnitSize(UnitSize); + + if (GlobalData.getOptions().NoOutput) + return Error::success(); + + if (Error Err = CU.emitDebugInfo(*TargetTriple)) + return Err; + + return CU.emitAbbreviations(); +} + +void DWARFLinkerImpl::glueCompileUnitsAndWriteToTheOutput() { + if (GlobalData.getOptions().NoOutput) + return; + + // Go through all object files, all compile units and assign + // offsets to them. + assignOffsets(); + + // Patch size/offsets fields according to the assigned CU offsets. + patchOffsetsAndSizes(); + + // FIXME: Build accelerator tables. + + // Write debug tables from all object files/compile units into the + // resulting file. + writeDWARFToTheOutput(); + + if (GlobalData.getOptions().Statistics) + printStatistic(); +} + +void DWARFLinkerImpl::printStatistic() { + + // For each object file map how many bytes were emitted. + StringMap SizeByObject; + + for (const std::unique_ptr &Context : ObjectContexts) { + uint64_t AllDebugInfoSectionsSize = 0; + + for (std::unique_ptr &CU : Context->CompileUnits) + AllDebugInfoSectionsSize += + CU->getSectionDescriptor(DebugSectionKind::DebugInfo) + .getContents() + .size(); + + SizeByObject[Context->InputDWARFFile.FileName].Input = + Context->OriginalDebugInfoSize; + SizeByObject[Context->InputDWARFFile.FileName].Output = + AllDebugInfoSectionsSize; + } + + // Create a vector sorted in descending order by output size. + std::vector> Sorted; + for (auto &E : SizeByObject) + Sorted.emplace_back(E.first(), E.second); + llvm::sort(Sorted, [](auto &LHS, auto &RHS) { + return LHS.second.Output > RHS.second.Output; + }); + + auto ComputePercentange = [](int64_t Input, int64_t Output) -> float { + const float Difference = Output - Input; + const float Sum = Input + Output; + if (Sum == 0) + return 0; + return (Difference / (Sum / 2)); + }; + + int64_t InputTotal = 0; + int64_t OutputTotal = 0; + const char *FormatStr = "{0,-45} {1,10}b {2,10}b {3,8:P}\n"; + + // Print header. + outs() << ".debug_info section size (in bytes)\n"; + outs() << "----------------------------------------------------------------" + "---------------\n"; + outs() << "Filename Object " + " dSYM Change\n"; + outs() << "----------------------------------------------------------------" + "---------------\n"; + + // Print body. + for (auto &E : Sorted) { + InputTotal += E.second.Input; + OutputTotal += E.second.Output; + llvm::outs() << formatv( + FormatStr, sys::path::filename(E.first).take_back(45), E.second.Input, + E.second.Output, ComputePercentange(E.second.Input, E.second.Output)); + } + // Print total and footer. + outs() << "----------------------------------------------------------------" + "---------------\n"; + llvm::outs() << formatv(FormatStr, "Total", InputTotal, OutputTotal, + ComputePercentange(InputTotal, OutputTotal)); + outs() << "----------------------------------------------------------------" + "---------------\n\n"; +} + +void DWARFLinkerImpl::assignOffsets() { + parallel::TaskGroup TGroup; + TGroup.spawn([&]() { assignOffsetsToStrings(); }); + TGroup.spawn([&]() { assignOffsetsToSections(); }); +} + +void DWARFLinkerImpl::assignOffsetsToStrings() { + size_t CurDebugStrIndex = 1; // start from 1 to take into account zero entry. + uint64_t CurDebugStrOffset = + 1; // start from 1 to take into account zero entry. + size_t CurDebugLineStrIndex = 0; + uint64_t CurDebugLineStrOffset = 0; + + // To save space we do not create any separate string table. + // We use already allocated string patches and assign offsets + // to them in the natural order. + // ASSUMPTION: strings should be stored into .debug_str/.debug_line_str + // sections in the same order as they were assigned offsets. + + forEachSectionsSet([&](OutputSections &SectionsSet) { + SectionsSet.forEach([&](SectionDescriptor &OutSection) { + assignOffsetsToStringsImpl(OutSection.ListDebugStrPatch, CurDebugStrIndex, + CurDebugStrOffset, DebugStrStrings); + + assignOffsetsToStringsImpl(OutSection.ListDebugLineStrPatch, + CurDebugLineStrIndex, CurDebugLineStrOffset, + DebugLineStrStrings); + }); + }); +} + +template +void DWARFLinkerImpl::assignOffsetsToStringsImpl( + ArrayList &Patches, size_t &IndexAccumulator, + uint64_t &OffsetAccumulator, + StringEntryToDwarfStringPoolEntryMap &StringsForEmission) { + + // Enumerates all patches, adds string into the + // StringEntry->DwarfStringPoolEntry map, assign offset and index to the + // string if it is not indexed yet. + Patches.forEach([&](PatchTy &Patch) { + DwarfStringPoolEntryWithExtString *Entry = + StringsForEmission.add(Patch.String); + assert(Entry != nullptr); + + if (!Entry->isIndexed()) { + Entry->Offset = OffsetAccumulator; + OffsetAccumulator += Entry->String.size() + 1; + Entry->Index = IndexAccumulator++; + } + }); +} + +void DWARFLinkerImpl::assignOffsetsToSections() { + std::array SectionSizesAccumulator = {0}; + + forEachSectionsSet([&](OutputSections &UnitSections) { + UnitSections.assignSectionsOffsetAndAccumulateSize(SectionSizesAccumulator); + }); +} + +void DWARFLinkerImpl::forEachSectionsSet( + function_ref SectionsSetHandler) { + // Handle all modules first(before regular compilation units). + for (const std::unique_ptr &Context : ObjectContexts) + for (LinkContext::RefModuleUnit &ModuleUnit : Context->ModulesCompileUnits) + SectionsSetHandler(*ModuleUnit.Unit); + + for (const std::unique_ptr &Context : ObjectContexts) { + // Handle common sections. + SectionsSetHandler(*Context); + + // Handle compilation units. + for (std::unique_ptr &CU : Context->CompileUnits) + SectionsSetHandler(*CU); + } +} + +void DWARFLinkerImpl::patchOffsetsAndSizes() { + forEachSectionsSet([&](OutputSections &SectionsSet) { + SectionsSet.forEach([&](SectionDescriptor &OutSection) { + SectionsSet.applyPatches(OutSection, DebugStrStrings, + DebugLineStrStrings); + }); + }); +} + +void DWARFLinkerImpl::writeDWARFToTheOutput() { + bool HasAbbreviations = false; + uint64_t DebugStrNextOffset = 0; + uint64_t DebugLineStrNextOffset = 0; + + // Emit zero length string. Accelerator tables does not work correctly + // if the first string is not zero length string. + TheDwarfEmitter->emitZeroString(); + DebugStrNextOffset++; + + forEachSectionsSet([&](OutputSections &Sections) { + Sections.forEach([&](SectionDescriptor &OutSection) { + if (OutSection.getContents().empty()) + return; + + if (!HasAbbreviations && + OutSection.getKind() == DebugSectionKind::DebugAbbrev) { + HasAbbreviations = true; + } + + // Emit section content. + TheDwarfEmitter->emitSectionContents(OutSection.getContents(), + OutSection.getName()); + + // Pass through string patches and emit them in order. + if (!OutSection.ListDebugStrPatch.empty()) + TheDwarfEmitter->emitStrings(OutSection.ListDebugStrPatch, + DebugStrStrings, DebugStrNextOffset); + + if (!OutSection.ListDebugLineStrPatch.empty()) + TheDwarfEmitter->emitLineStrings(OutSection.ListDebugLineStrPatch, + DebugLineStrStrings, + DebugLineStrNextOffset); + }); + }); + + if (!HasAbbreviations) { + const SmallVector> Abbreviations; + TheDwarfEmitter->emitAbbrevs(Abbreviations, 3); + } +} + } // end of namespace dwarflinker_parallel } // namespace llvm diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.h b/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.h --- a/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.h +++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.h @@ -9,9 +9,11 @@ #ifndef LLVM_LIB_DWARFLINKERPARALLEL_DWARFLINKERUNIT_H #define LLVM_LIB_DWARFLINKERPARALLEL_DWARFLINKERUNIT_H +#include "DWARFLinkerGlobalData.h" #include "OutputSections.h" #include "llvm/ADT/SmallString.h" #include "llvm/CodeGen/DIE.h" +#include "llvm/DWARFLinkerParallel/DWARFLinker.h" #include "llvm/DWARFLinkerParallel/StringPool.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/Support/LEB128.h" @@ -19,45 +21,20 @@ namespace llvm { namespace dwarflinker_parallel { -using UnitMessageHandlerTy = function_ref; - -/// Each unit keeps output data as a file with debug tables -/// corresponding to the concrete unit. -using OutTablesFileTy = SmallString<0>; +class DwarfUnit; +using MacroOffset2UnitMapTy = DenseMap; /// Base class for all Dwarf units(Compile unit/Type table unit). class DwarfUnit : public OutputSections { public: virtual ~DwarfUnit() {} - DwarfUnit(unsigned ID, StringRef ClangModuleName, - UnitMessageHandlerTy WarningHandler) - : ID(ID), ClangModuleName(ClangModuleName), - WarningHandler(WarningHandler) { - FormParams.Version = 4; - FormParams.Format = dwarf::DWARF32; - FormParams.AddrSize = 4; + DwarfUnit(LinkingGlobalData &GlobalData, unsigned ID, + StringRef ClangModuleName) + : GlobalData(GlobalData), ID(ID), ClangModuleName(ClangModuleName), + OutUnitDIE(nullptr) { + setGlobalData(&GlobalData); } - /// Endiannes for the compile unit. - support::endianness getEndianness() const { return Endianess; } - - /// Return DWARF version. - uint16_t getVersion() const { return FormParams.Version; } - - /// Return size of header of debug_info table. - uint16_t getHeaderSize() const { return FormParams.Version >= 5 ? 12 : 11; } - - /// Return size of address. - uint8_t getAddressByteSize() const { return FormParams.AddrSize; } - - /// Return size of reference. - uint8_t getRefAddrByteSize() const { return FormParams.getRefAddrByteSize(); } - - /// Return format of the Dwarf(DWARF32 or DWARF64). - /// TODO: DWARF64 is not currently supported. - dwarf::DwarfFormat getDwarfFormat() const { return FormParams.Format; } - /// Unique id of the unit. unsigned getUniqueID() const { return ID; } @@ -76,41 +53,48 @@ /// Return the DW_AT_LLVM_sysroot of the compile unit or an empty StringRef. StringRef getSysRoot() { return SysRoot; } - /// Create a Die for this unit. - void setOutputDIE(DIE *UnitDie) { NewUnit = UnitDie; } - - /// Return Die for this compile unit. - DIE *getOutputUnitDIE() const { return NewUnit; } - /// Return true if this compile unit is from Clang module. bool isClangModule() const { return !ClangModuleName.empty(); } /// Return Clang module name; const std::string &getClangModuleName() const { return ClangModuleName; } - /// Returns generated file keeping debug tables for this compile unit. - OutTablesFileTy &getOutDwarfBits() { return OutDebugInfoBits; } + /// Return global data. + LinkingGlobalData &getGlobalData() { return GlobalData; } - /// Erases generated file keeping debug tables for this compile unit. - void eraseDwarfBits() { OutDebugInfoBits = OutTablesFileTy(); } + /// Returns true if unit is inter-connected(it references/referenced by other + /// unit). + bool isInterconnectedCU() const { return IsInterconnectedCU; } - MCSymbol *getLabelBegin() { return LabelBegin; } - void setLabelBegin(MCSymbol *S) { LabelBegin = S; } + /// Mark this unit as inter-connected(it references/referenced by other unit). + void setInterconnectedCU() { IsInterconnectedCU = true; } - /// Error reporting methods. - /// @{ + /// Adds \p Abbrev into unit`s abbreviation table. + void assignAbbrev(DIEAbbrev &Abbrev); - void reportWarning(const Twine &Warning, - const DWARFDie *Die = nullptr) const { - if (WarningHandler) - WarningHandler(Warning, getUnitName(), Die); - } - void reportWarning(Error Warning) const { - handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) { - if (WarningHandler) - WarningHandler(Info.message(), getUnitName(), nullptr); - }); + /// Returns abbreviations for this compile unit. + const std::vector> &getAbbreviations() const { + return Abbreviations; } + + /// Returns output unit DIE. + DIE *getOutUnitDIE() { return OutUnitDIE; } + + /// Set output unit DIE. + void setOutUnitDIE(DIE *UnitDie) { OutUnitDIE = UnitDie; } + + /// \defgroup Methods used to emit unit's debug info: + /// + /// @{ + /// Emit unit's abbreviations. + Error emitAbbreviations(); + + /// Emit .debug_info section for unit DIEs. + Error emitDebugInfo(Triple &TargetTriple); + + /// Emit .debug_line section. + Error emitDebugLine(Triple &TargetTriple, + const DWARFDebugLine::LineTable &OutLineTable); /// @} /// This structure keeps fields which would be used for creating accelerator @@ -142,15 +126,24 @@ const DIE *Die = nullptr; }; + /// \defgroup Methods used for reporting warnings and errors: + /// + /// @{ + void warn(const Twine &Warning) { GlobalData.warn(Warning, getUnitName()); } + + void error(const Twine &Err) { GlobalData.warn(Err, getUnitName()); } + /// @} + protected: - /// Unique ID for the unit. - unsigned ID = 0; + /// Emit single abbreviation entry. + void emitDwarfAbbrevEntry(const DIEAbbrev &Abbrev, + SectionDescriptor &AbbrevSection); - /// Properties of the unit. - dwarf::FormParams FormParams; + /// Linking global data. + LinkingGlobalData &GlobalData; - /// DIE for newly generated compile unit. - DIE *NewUnit = nullptr; + /// Unique ID for the unit. + unsigned ID = 0; /// The DW_AT_language of this unit. uint16_t Language = 0; @@ -166,18 +159,17 @@ uint64_t UnitSize = 0; - /// Elf file containg generated debug tables for this compile unit. - OutTablesFileTy OutDebugInfoBits; - - /// Endiannes for this compile unit. - support::endianness Endianess = support::endianness::little; - - MCSymbol *LabelBegin = nullptr; - /// true if current unit references_to/is_referenced by other unit. std::atomic IsInterconnectedCU = {false}; - UnitMessageHandlerTy WarningHandler; + /// FoldingSet that uniques the abbreviations. + FoldingSet AbbreviationsSet; + + /// Storage for the unique Abbreviations. + std::vector> Abbreviations; + + /// Output unit DIE. + DIE *OutUnitDIE = nullptr; }; } // end of namespace dwarflinker_parallel diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.cpp b/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.cpp @@ -0,0 +1,130 @@ +//===- DWARFLinkerUnit.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 "DWARFLinkerUnit.h" +#include "DWARFEmitterImpl.h" +#include "DebugLineSectionEmitter.h" + +namespace llvm { +namespace dwarflinker_parallel { + +void DwarfUnit::assignAbbrev(DIEAbbrev &Abbrev) { + // Check the set for priors. + FoldingSetNodeID ID; + Abbrev.Profile(ID); + void *InsertToken; + + DIEAbbrev *InSet = AbbreviationsSet.FindNodeOrInsertPos(ID, InsertToken); + // If it's newly added. + if (InSet) { + // Assign existing abbreviation number. + Abbrev.setNumber(InSet->getNumber()); + } else { + // Add to abbreviation list. + Abbreviations.push_back( + std::make_unique(Abbrev.getTag(), Abbrev.hasChildren())); + for (const auto &Attr : Abbrev.getData()) + Abbreviations.back()->AddAttribute(Attr); + AbbreviationsSet.InsertNode(Abbreviations.back().get(), InsertToken); + // Assign the unique abbreviation number. + Abbrev.setNumber(Abbreviations.size()); + Abbreviations.back()->setNumber(Abbreviations.size()); + } +} + +Error DwarfUnit::emitAbbreviations() { + const std::vector> &Abbrevs = getAbbreviations(); + if (Abbrevs.empty()) + return Error::success(); + + SectionDescriptor &AbbrevSection = + getSectionDescriptor(DebugSectionKind::DebugAbbrev); + + // For each abbreviation. + for (const auto &Abbrev : Abbrevs) + emitDwarfAbbrevEntry(*Abbrev, AbbrevSection); + + // Mark end of abbreviations. + encodeULEB128(0, AbbrevSection.OS); + + return Error::success(); +} + +void DwarfUnit::emitDwarfAbbrevEntry(const DIEAbbrev &Abbrev, + SectionDescriptor &AbbrevSection) { + // Emit the abbreviations code (base 1 index.) + encodeULEB128(Abbrev.getNumber(), AbbrevSection.OS); + + // Emit the abbreviations data. + // Emit its Dwarf tag type. + encodeULEB128(Abbrev.getTag(), AbbrevSection.OS); + + // Emit whether it has children DIEs. + encodeULEB128((unsigned)Abbrev.hasChildren(), AbbrevSection.OS); + + // For each attribute description. + const SmallVectorImpl &Data = Abbrev.getData(); + for (unsigned i = 0, N = Data.size(); i < N; ++i) { + const DIEAbbrevData &AttrData = Data[i]; + + // Emit attribute type. + encodeULEB128(AttrData.getAttribute(), AbbrevSection.OS); + + // Emit form type. + encodeULEB128(AttrData.getForm(), AbbrevSection.OS); + + // Emit value for DW_FORM_implicit_const. + if (AttrData.getForm() == dwarf::DW_FORM_implicit_const) + encodeSLEB128(AttrData.getValue(), AbbrevSection.OS); + } + + // Mark end of abbreviation. + encodeULEB128(0, AbbrevSection.OS); + encodeULEB128(0, AbbrevSection.OS); +} + +Error DwarfUnit::emitDebugInfo(Triple &TargetTriple) { + DIE *OutUnitDIE = getOutUnitDIE(); + if (OutUnitDIE == nullptr) + return Error::success(); + + // FIXME: Remove dependence on DwarfEmitterImpl/AsmPrinter and emit DIEs + // directly. + + SectionDescriptor &OutSection = + getSectionDescriptor(DebugSectionKind::DebugInfo); + DwarfEmitterImpl Emitter(DWARFLinker::OutputFileType::Object, OutSection.OS); + if (Error Err = Emitter.init(TargetTriple, "__DWARF")) + return Err; + + // Emit compile unit header. + Emitter.emitCompileUnitHeader(*this); + size_t OffsetToAbbreviationTableOffset = + (getFormParams().Version >= 5) ? 8 : 6; + OutSection.notePatch( + DebugOffsetPatch{OffsetToAbbreviationTableOffset, + &getSectionDescriptor(DebugSectionKind::DebugAbbrev)}); + + // Emit DIEs. + Emitter.emitDIE(*OutUnitDIE); + Emitter.finish(); + + // Set start offset ans size for .debug_info section. + OutSection.setSizesForSectionCreatedByAsmPrinter(); + return Error::success(); +} + +Error DwarfUnit::emitDebugLine(Triple &TargetTriple, + const DWARFDebugLine::LineTable &OutLineTable) { + DebugLineSectionEmitter DebugLineEmitter(TargetTriple, *this); + + return DebugLineEmitter.emit(OutLineTable); +} + +} // end of namespace dwarflinker_parallel +} // end of namespace llvm diff --git a/llvm/lib/DWARFLinkerParallel/DebugLineSectionEmitter.h b/llvm/lib/DWARFLinkerParallel/DebugLineSectionEmitter.h new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerParallel/DebugLineSectionEmitter.h @@ -0,0 +1,384 @@ +//===- DebugLineSectionEmitter.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_LIB_DWARFLINKERPARALLEL_DEBUGLINESECTIONEMITTER_H +#define LLVM_LIB_DWARFLINKERPARALLEL_DEBUGLINESECTIONEMITTER_H + +#include "DWARFEmitterImpl.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/DWARFLinkerParallel/AddressesMap.h" +#include "llvm/DWARFLinkerParallel/DWARFLinker.h" +#include "llvm/DebugInfo/DWARF/DWARFObject.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/MC/TargetRegistry.h" + +namespace llvm { +namespace dwarflinker_parallel { + +/// This class emits specified line table into the .debug_line section. +class DebugLineSectionEmitter { +public: + DebugLineSectionEmitter(const Triple &TheTriple, DwarfUnit &U) + : TheTriple(TheTriple), U(U) {} + + Error emit(const DWARFDebugLine::LineTable &LineTable) { + // FIXME: remove dependence on MCDwarfLineAddr::encode. + // As we reuse MCDwarfLineAddr::encode, we need to create/initialize + // some MC* classes. + if (Error Err = init(TheTriple)) + return Err; + + // Get descriptor for output .debug_line section. + SectionDescriptor &OutSection = + U.getSectionDescriptor(DebugSectionKind::DebugLine); + + // unit_length. + OutSection.emitUnitLength(0xBADDEF); + uint64_t OffsetAfterUnitLength = OutSection.OS.tell(); + + // Emit prologue. + emitLineTablePrologue(LineTable.Prologue, OutSection); + + // Emit rows. + emitLineTableRows(LineTable, OutSection); + uint64_t OffsetAfterEnd = OutSection.OS.tell(); + + // Update unit length field with actual length value. + assert(OffsetAfterUnitLength - + OutSection.getFormParams().getDwarfOffsetByteSize() < + OffsetAfterUnitLength); + OutSection.apply(OffsetAfterUnitLength - + OutSection.getFormParams().getDwarfOffsetByteSize(), + dwarf::DW_FORM_sec_offset, + OffsetAfterEnd - OffsetAfterUnitLength); + + return Error::success(); + } + +private: + Error init(Triple TheTriple) { + std::string ErrorStr; + std::string TripleName; + + // Get the target. + const Target *TheTarget = + TargetRegistry::lookupTarget(TripleName, TheTriple, ErrorStr); + if (!TheTarget) + return createStringError(std::errc::invalid_argument, ErrorStr.c_str()); + TripleName = TheTriple.getTriple(); + + // Create all the MC Objects. + MRI.reset(TheTarget->createMCRegInfo(TripleName)); + if (!MRI) + return createStringError(std::errc::invalid_argument, + "no register info for target %s", + TripleName.c_str()); + + MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags(); + MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); + if (!MAI) + return createStringError(std::errc::invalid_argument, + "no asm info for target %s", TripleName.c_str()); + + MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", "")); + if (!MSTI) + return createStringError(std::errc::invalid_argument, + "no subtarget info for target %s", + TripleName.c_str()); + + MC.reset(new MCContext(TheTriple, MAI.get(), MRI.get(), MSTI.get(), nullptr, + nullptr, true, "__DWARF")); + + return Error::success(); + } + + void emitLineTablePrologue(const DWARFDebugLine::Prologue &P, + SectionDescriptor &Section) { + // version (uhalf). + Section.emitIntVal(P.getVersion(), 2); + if (P.getVersion() == 5) { + // address_size (ubyte). + Section.emitIntVal(P.getAddressSize(), 1); + + // segment_selector_size (ubyte). + Section.emitIntVal(P.SegSelectorSize, 1); + } + + // header_length. + Section.emitOffset(0xBADDEF); + + uint64_t OffsetAfterPrologueLength = Section.OS.tell(); + emitLineTableProloguePayload(P, Section); + uint64_t OffsetAfterPrologueEnd = Section.OS.tell(); + + // Update prologue length field with actual length value. + Section.apply(OffsetAfterPrologueLength - + Section.getFormParams().getDwarfOffsetByteSize(), + dwarf::DW_FORM_sec_offset, + OffsetAfterPrologueEnd - OffsetAfterPrologueLength); + } + + void + emitLineTablePrologueV2IncludeAndFileTable(const DWARFDebugLine::Prologue &P, + SectionDescriptor &Section) { + // include_directories (sequence of path names). + for (const DWARFFormValue &Include : P.IncludeDirectories) { + std::optional IncludeStr = dwarf::toString(Include); + if (!IncludeStr) { + U.warn("cann't read string from line table."); + return; + } + + Section.emitString(Include.getForm(), *IncludeStr); + } + // The last entry is followed by a single null byte. + Section.emitIntVal(0, 1); + + // file_names (sequence of file entries). + for (const DWARFDebugLine::FileNameEntry &File : P.FileNames) { + std::optional FileNameStr = dwarf::toString(File.Name); + if (!FileNameStr) { + U.warn("cann't read string from line table."); + return; + } + + // A null-terminated string containing the full or relative path name of a + // source file. + Section.emitString(File.Name.getForm(), *FileNameStr); + // An unsigned LEB128 number representing the directory index of a + // directory in the include_directories section. + encodeULEB128(File.DirIdx, Section.OS); + // An unsigned LEB128 number representing the (implementation-defined) + // time of last modification for the file, or 0 if not available. + encodeULEB128(File.ModTime, Section.OS); + // An unsigned LEB128 number representing the length in bytes of the file, + // or 0 if not available. + encodeULEB128(File.Length, Section.OS); + } + // The last entry is followed by a single null byte. + Section.emitIntVal(0, 1); + } + + void + emitLineTablePrologueV5IncludeAndFileTable(const DWARFDebugLine::Prologue &P, + SectionDescriptor &Section) { + if (P.IncludeDirectories.empty()) { + // directory_entry_format_count(ubyte). + Section.emitIntVal(0, 1); + } else { + // directory_entry_format_count(ubyte). + Section.emitIntVal(1, 1); + + // directory_entry_format (sequence of ULEB128 pairs). + encodeULEB128(dwarf::DW_LNCT_path, Section.OS); + encodeULEB128(P.IncludeDirectories[0].getForm(), Section.OS); + } + + // directories_count (ULEB128). + encodeULEB128(P.IncludeDirectories.size(), Section.OS); + // directories (sequence of directory names). + for (auto Include : P.IncludeDirectories) { + std::optional IncludeStr = dwarf::toString(Include); + if (!IncludeStr) { + U.warn("cann't read string from line table."); + return; + } + + Section.emitString(Include.getForm(), *IncludeStr); + } + + if (P.FileNames.empty()) { + // file_name_entry_format_count (ubyte). + Section.emitIntVal(0, 1); + } else { + // file_name_entry_format_count (ubyte). + Section.emitIntVal(2, 1); + + // file_name_entry_format (sequence of ULEB128 pairs). + encodeULEB128(dwarf::DW_LNCT_path, Section.OS); + encodeULEB128(P.FileNames[0].Name.getForm(), Section.OS); + + encodeULEB128(dwarf::DW_LNCT_directory_index, Section.OS); + encodeULEB128(dwarf::DW_FORM_data1, Section.OS); + } + + // file_names_count (ULEB128). + encodeULEB128(P.FileNames.size(), Section.OS); + + // file_names (sequence of file name entries). + for (auto File : P.FileNames) { + std::optional FileNameStr = dwarf::toString(File.Name); + if (!FileNameStr) { + U.warn("cann't read string from line table."); + return; + } + + // A null-terminated string containing the full or relative path name of a + // source file. + Section.emitString(File.Name.getForm(), *FileNameStr); + Section.emitIntVal(File.DirIdx, 1); + } + } + + void emitLineTableProloguePayload(const DWARFDebugLine::Prologue &P, + SectionDescriptor &Section) { + // minimum_instruction_length (ubyte). + Section.emitIntVal(P.MinInstLength, 1); + if (P.FormParams.Version >= 4) { + // maximum_operations_per_instruction (ubyte). + Section.emitIntVal(P.MaxOpsPerInst, 1); + } + // default_is_stmt (ubyte). + Section.emitIntVal(P.DefaultIsStmt, 1); + // line_base (sbyte). + Section.emitIntVal(P.LineBase, 1); + // line_range (ubyte). + Section.emitIntVal(P.LineRange, 1); + // opcode_base (ubyte). + Section.emitIntVal(P.OpcodeBase, 1); + + // standard_opcode_lengths (array of ubyte). + for (auto Length : P.StandardOpcodeLengths) + Section.emitIntVal(Length, 1); + + if (P.FormParams.Version < 5) + emitLineTablePrologueV2IncludeAndFileTable(P, Section); + else + emitLineTablePrologueV5IncludeAndFileTable(P, Section); + } + + void emitLineTableRows(const DWARFDebugLine::LineTable &LineTable, + SectionDescriptor &Section) { + + MCDwarfLineTableParams Params; + Params.DWARF2LineOpcodeBase = LineTable.Prologue.OpcodeBase; + Params.DWARF2LineBase = LineTable.Prologue.LineBase; + Params.DWARF2LineRange = LineTable.Prologue.LineRange; + + SmallString<128> EncodingBuffer; + + if (LineTable.Rows.empty()) { + // We only have the dummy entry, dsymutil emits an entry with a 0 + // address in that case. + MCDwarfLineAddr::encode(*MC, Params, std::numeric_limits::max(), + 0, EncodingBuffer); + Section.OS.write(EncodingBuffer.c_str(), EncodingBuffer.size()); + return; + } + + // Line table state machine fields + unsigned FileNum = 1; + unsigned LastLine = 1; + unsigned Column = 0; + unsigned IsStatement = 1; + unsigned Isa = 0; + uint64_t Address = -1ULL; + + unsigned RowsSinceLastSequence = 0; + + for (const DWARFDebugLine::Row &Row : LineTable.Rows) { + int64_t AddressDelta; + if (Address == -1ULL) { + Section.emitIntVal(dwarf::DW_LNS_extended_op, 1); + encodeULEB128(Section.getFormParams().AddrSize + 1, Section.OS); + Section.emitIntVal(dwarf::DW_LNE_set_address, 1); + Section.emitIntVal(Row.Address.Address, + Section.getFormParams().AddrSize); + AddressDelta = 0; + } else { + AddressDelta = + (Row.Address.Address - Address) / LineTable.Prologue.MinInstLength; + } + + // FIXME: code copied and transformed from + // MCDwarf.cpp::EmitDwarfLineTable. We should find a way to share this + // code, but the current compatibility requirement with classic dsymutil + // makes it hard. Revisit that once this requirement is dropped. + + if (FileNum != Row.File) { + FileNum = Row.File; + Section.emitIntVal(dwarf::DW_LNS_set_file, 1); + encodeULEB128(FileNum, Section.OS); + } + if (Column != Row.Column) { + Column = Row.Column; + Section.emitIntVal(dwarf::DW_LNS_set_column, 1); + encodeULEB128(Column, Section.OS); + } + + // FIXME: We should handle the discriminator here, but dsymutil doesn't + // consider it, thus ignore it for now. + + if (Isa != Row.Isa) { + Isa = Row.Isa; + Section.emitIntVal(dwarf::DW_LNS_set_isa, 1); + encodeULEB128(Isa, Section.OS); + } + if (IsStatement != Row.IsStmt) { + IsStatement = Row.IsStmt; + Section.emitIntVal(dwarf::DW_LNS_negate_stmt, 1); + } + if (Row.BasicBlock) + Section.emitIntVal(dwarf::DW_LNS_set_basic_block, 1); + + if (Row.PrologueEnd) + Section.emitIntVal(dwarf::DW_LNS_set_prologue_end, 1); + + if (Row.EpilogueBegin) + Section.emitIntVal(dwarf::DW_LNS_set_epilogue_begin, 1); + + int64_t LineDelta = int64_t(Row.Line) - LastLine; + if (!Row.EndSequence) { + MCDwarfLineAddr::encode(*MC, Params, LineDelta, AddressDelta, + EncodingBuffer); + Section.OS.write(EncodingBuffer.c_str(), EncodingBuffer.size()); + EncodingBuffer.resize(0); + Address = Row.Address.Address; + LastLine = Row.Line; + RowsSinceLastSequence++; + } else { + if (LineDelta) { + Section.emitIntVal(dwarf::DW_LNS_advance_line, 1); + encodeSLEB128(LineDelta, Section.OS); + } + if (AddressDelta) { + Section.emitIntVal(dwarf::DW_LNS_advance_pc, 1); + encodeULEB128(AddressDelta, Section.OS); + } + MCDwarfLineAddr::encode(*MC, Params, + std::numeric_limits::max(), 0, + EncodingBuffer); + Section.OS.write(EncodingBuffer.c_str(), EncodingBuffer.size()); + EncodingBuffer.resize(0); + Address = -1ULL; + LastLine = FileNum = IsStatement = 1; + RowsSinceLastSequence = Column = Isa = 0; + } + } + + if (RowsSinceLastSequence) { + MCDwarfLineAddr::encode(*MC, Params, std::numeric_limits::max(), + 0, EncodingBuffer); + Section.OS.write(EncodingBuffer.c_str(), EncodingBuffer.size()); + EncodingBuffer.resize(0); + } + } + + Triple TheTriple; + DwarfUnit &U; + + std::unique_ptr MRI; + std::unique_ptr MAI; + std::unique_ptr MC; + std::unique_ptr MSTI; +}; + +} // end of namespace dwarflinker_parallel +} // end namespace llvm + +#endif // LLVM_LIB_DWARFLINKERPARALLEL_DEBUGLINESECTIONEMITTER_H diff --git a/llvm/lib/DWARFLinkerParallel/DependencyTracker.h b/llvm/lib/DWARFLinkerParallel/DependencyTracker.h new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerParallel/DependencyTracker.h @@ -0,0 +1,102 @@ +//===- "DependencyTracker.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_LIB_DWARFLINKERPARALLEL_DEPENDENCYTRACKER_H +#define LLVM_LIB_DWARFLINKERPARALLEL_DEPENDENCYTRACKER_H + +#include "DWARFLinkerCompileUnit.h" +#include "DWARFLinkerImpl.h" +#include "llvm/ADT/SmallVector.h" + +namespace llvm { +class DWARFDebugInfoEntry; +class DWARFDie; + +namespace dwarflinker_parallel { + +/// This class discovers DIEs dependencies and marks "live" DIEs. +class DependencyTracker { +public: + DependencyTracker(DWARFLinkerImpl::LinkContext &Context) : Context(Context) {} + + /// Recursively walk the \p DIE tree and look for DIEs to keep. Store that + /// information in \p CU's DIEInfo. + /// + /// This function is the entry point of the DIE selection algorithm. It is + /// expected to walk the DIE tree and(through the mediation of + /// Context.File.Addresses) ask for relocation adjustment value on each + /// DIE that might be a 'root DIE'. + /// + /// Returns true if all dependencies are correctly discovered. Inter-CU + /// dependencies cannot be discovered if referenced CU is not analyzed yet. + /// If that is the case this method returns false. + bool resolveDependenciesAndMarkLiveness(CompileUnit &CU); + + /// Recursively walk the \p DIE tree and check "keepness" information. + /// It is an error if parent node does not have "keep" flag, while + /// child have one. This function dump error at stderr in that case. +#ifndef NDEBUG + static void verifyKeepChain(CompileUnit &CU); +#endif + +protected: + struct RootEntryTy { + RootEntryTy(CompileUnit &CU, const DWARFDebugInfoEntry *RootEntry) + : CU(CU), RootEntry(RootEntry) {} + + // Compile unit keeping root entry. + CompileUnit &CU; + + // Root entry. + const DWARFDebugInfoEntry *RootEntry; + }; + + using RootEntriesListTy = SmallVector; + + /// This function navigates DIEs tree starting from specified \p Entry. + /// It puts 'root DIE' into the worklist. + void collectRootsToKeep(CompileUnit &CU, const DWARFDebugInfoEntry *Entry); + + /// Returns true if specified variable references live code section. + bool isLiveVariableEntry(CompileUnit &CU, const DWARFDebugInfoEntry *Entry); + + /// Returns true if specified subprogram references live code section. + bool isLiveSubprogramEntry(CompileUnit &CU, const DWARFDebugInfoEntry *Entry); + + /// Examine worklist and mark all 'root DIE's as kept. + bool markLiveRootsAsKept(); + + /// Mark whole DIE tree as kept recursively. + bool markDIEEntryAsKeptRec(const RootEntryTy &RootItem, CompileUnit &CU, + const DWARFDebugInfoEntry *Entry); + + /// Check referenced DIEs and add them into the worklist if neccessary. + bool maybeAddReferencedRoots(const RootEntryTy &RootItem, CompileUnit &CU, + const DWARFDebugInfoEntry *Entry); + + /// Add 'root DIE' into the worklist. + void addItemToWorklist(CompileUnit &CU, const DWARFDebugInfoEntry *Entry); + + /// Set kind of placement(whether it goes into type table, plain dwarf or + /// both) for the specified die \p DieIdx. + void setDIEPlacementAndTypename(CompileUnit::DIEInfo &Info); + + /// Flag indicating whether liveness information should be examined. + bool TrackLiveness = false; + + /// List of CU, Entry pairs which are 'root DIE's. + RootEntriesListTy RootEntriesWorkList; + + /// Link context for the analyzed CU. + DWARFLinkerImpl::LinkContext &Context; +}; + +} // end namespace dwarflinker_parallel +} // end namespace llvm + +#endif // LLVM_LIB_DWARFLINKERPARALLEL_DEPENDENCYTRACKER_H diff --git a/llvm/lib/DWARFLinkerParallel/DependencyTracker.cpp b/llvm/lib/DWARFLinkerParallel/DependencyTracker.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerParallel/DependencyTracker.cpp @@ -0,0 +1,426 @@ +//=== DependencyTracker.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 "DependencyTracker.h" +#include "llvm/Support/FormatVariadic.h" + +namespace llvm { +namespace dwarflinker_parallel { + +#ifndef NDEBUG +/// A broken link in the keep chain. By recording both the parent and the child +/// we can show only broken links for DIEs with multiple children. +struct BrokenLink { + BrokenLink(DWARFDie Parent, DWARFDie Child) : Parent(Parent), Child(Child) {} + DWARFDie Parent; + DWARFDie Child; +}; + +/// Verify the keep chain by looking for DIEs that are kept but who's parent +/// isn't. +void DependencyTracker::verifyKeepChain(CompileUnit &CU) { + SmallVector Worklist; + Worklist.push_back(CU.getOrigUnit().getUnitDIE()); + + // List of broken links. + SmallVector BrokenLinks; + + while (!Worklist.empty()) { + const DWARFDie Current = Worklist.back(); + Worklist.pop_back(); + + if (!Current.isValid()) + continue; + + const bool CurrentDieIsKept = CU.getDIEInfo(Current).getKeep() || + CU.getDIEInfo(Current).getKeepChildren(); + + for (DWARFDie Child : reverse(Current.children())) { + Worklist.push_back(Child); + + const bool ChildDieIsKept = CU.getDIEInfo(Child).getKeep() || + CU.getDIEInfo(Child).getKeepChildren(); + if (!CurrentDieIsKept && ChildDieIsKept) + BrokenLinks.emplace_back(Current, Child); + } + } + + if (!BrokenLinks.empty()) { + for (BrokenLink Link : BrokenLinks) { + WithColor::error() << formatv( + "Found invalid link in keep chain between {0:x} and {1:x}\n", + Link.Parent.getOffset(), Link.Child.getOffset()); + + errs() << "Parent:"; + Link.Parent.dump(errs(), 0, {}); + CU.getDIEInfo(Link.Parent).dump(); + + errs() << "Child:"; + Link.Child.dump(errs(), 2, {}); + CU.getDIEInfo(Link.Child).dump(); + } + report_fatal_error("invalid keep chain"); + } +} +#endif + +bool DependencyTracker::resolveDependenciesAndMarkLiveness(CompileUnit &CU) { + // We do not track liveness inside Clang modules. We also do not track + // liveness if UpdateIndexTablesOnly is requested. + TrackLiveness = !(CU.isClangModule() || + CU.getGlobalData().getOptions().UpdateIndexTablesOnly); + RootEntriesWorkList.clear(); + + // Search for live root DIEs. + collectRootsToKeep(CU, CU.getDebugInfoEntry(0)); + + // Mark live DIEs as kept. + return markLiveRootsAsKept(); +} + +void DependencyTracker::collectRootsToKeep(CompileUnit &CU, + const DWARFDebugInfoEntry *Entry) { + if (!TrackLiveness) { + addItemToWorklist(CU, Entry); + return; + } + + switch (Entry->getTag()) { + case dwarf::DW_TAG_subprogram: + case dwarf::DW_TAG_label: + if (isLiveSubprogramEntry(CU, Entry)) { + addItemToWorklist(CU, Entry); + break; + } + [[fallthrough]]; + case dwarf::DW_TAG_compile_unit: + case dwarf::DW_TAG_namespace: + case dwarf::DW_TAG_module: + case dwarf::DW_TAG_lexical_block: { + for (const DWARFDebugInfoEntry *CurChild = CU.getFirstChildEntry(Entry); + CurChild && CurChild->getAbbreviationDeclarationPtr(); + CurChild = CU.getSiblingEntry(CurChild)) + collectRootsToKeep(CU, CurChild); + } break; + case dwarf::DW_TAG_constant: + case dwarf::DW_TAG_variable: { + if (isLiveVariableEntry(CU, Entry)) + addItemToWorklist(CU, Entry); + } break; + case dwarf::DW_TAG_base_type: { + addItemToWorklist(CU, Entry); + } break; + case dwarf::DW_TAG_imported_module: + case dwarf::DW_TAG_imported_declaration: + case dwarf::DW_TAG_imported_unit: { + addItemToWorklist(CU, Entry); + } break; + default: + // Nothing to do. + break; + } +} + +bool DependencyTracker::markLiveRootsAsKept() { + bool Res = true; + + while (!RootEntriesWorkList.empty()) { + RootEntryTy CurrentItem = RootEntriesWorkList.pop_back_val(); + + if (!markDIEEntryAsKeptRec(CurrentItem, CurrentItem.CU, + CurrentItem.RootEntry)) + Res = false; + } + + return Res; +} + +bool DependencyTracker::markDIEEntryAsKeptRec( + const RootEntryTy &RootItem, CompileUnit &CU, + const DWARFDebugInfoEntry *Entry) { + if (Entry->getAbbreviationDeclarationPtr() == nullptr) + return true; + + CompileUnit::DIEInfo &Info = CU.getDIEInfo(Entry); + + if (Info.getKeep()) + return true; + + // Mark parents as 'KeepChildren'. + std::optional ParentIdx = Entry->getParentIdx(); + while (ParentIdx) { + const DWARFDebugInfoEntry *ParentEntry = CU.getDebugInfoEntry(*ParentIdx); + CompileUnit::DIEInfo &ParentInfo = CU.getDIEInfo(*ParentIdx); + if (ParentInfo.getKeepChildren()) + break; + ParentInfo.setKeepChildren(); + ParentIdx = ParentEntry->getParentIdx(); + } + + // Mark current DIE as kept. + Info.setKeep(); + setDIEPlacementAndTypename(Info); + + // Set liveness information. + switch (Entry->getTag()) { + case dwarf::DW_TAG_constant: + case dwarf::DW_TAG_variable: { + isLiveVariableEntry(CU, Entry); + } break; + case dwarf::DW_TAG_subprogram: + case dwarf::DW_TAG_label: { + isLiveSubprogramEntry(CU, Entry); + } break; + default: + // Nothing to do. + break; + } + + // Analyse referenced DIEs. + bool Res = true; + if (!maybeAddReferencedRoots(RootItem, CU, Entry)) + Res = false; + + // Navigate children. + for (const DWARFDebugInfoEntry *CurChild = CU.getFirstChildEntry(Entry); + CurChild && CurChild->getAbbreviationDeclarationPtr(); + CurChild = CU.getSiblingEntry(CurChild)) { + if (!markDIEEntryAsKeptRec(RootItem, CU, CurChild)) + Res = false; + } + + return Res; +} + +bool DependencyTracker::maybeAddReferencedRoots( + const RootEntryTy &RootItem, CompileUnit &CU, + const DWARFDebugInfoEntry *Entry) { + const auto *Abbrev = Entry->getAbbreviationDeclarationPtr(); + if (Abbrev == nullptr) + return true; + + DWARFUnit &Unit = CU.getOrigUnit(); + DWARFDataExtractor Data = Unit.getDebugInfoExtractor(); + uint64_t Offset = Entry->getOffset() + getULEB128Size(Abbrev->getCode()); + + // For each DIE attribute... + for (const auto &AttrSpec : Abbrev->attributes()) { + DWARFFormValue Val(AttrSpec.Form); + if (!Val.isFormClass(DWARFFormValue::FC_Reference) || + AttrSpec.Attr == dwarf::DW_AT_sibling) { + DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, + Unit.getFormParams()); + continue; + } + Val.extractValue(Data, &Offset, Unit.getFormParams(), &Unit); + + // Resolve reference. + std::optional> RefDie = + CU.resolveDIEReference(Val); + if (!RefDie) { + CU.warn("cann't find referenced DIE", Entry); + continue; + } + + if (CU.getUniqueID() == RefDie->first->getUniqueID()) { + // Check if referenced DIE entry is already kept. + if (RefDie->first->getDIEInfo(RefDie->second).getKeep()) + continue; + + // If referenced DIE is inside current compilation unit. + const DWARFDebugInfoEntry *RefEntry = + RefDie->first->getDebugInfoEntry(RefDie->second); + + if (RootItem.RootEntry->getTag() == dwarf::DW_TAG_compile_unit) + addItemToWorklist(*RefDie->first, RefEntry); + else { + uint64_t RootStartOffset = RootItem.RootEntry->getOffset(); + uint64_t RootEndOffset; + if (std::optional SiblingIdx = + RootItem.RootEntry->getSiblingIdx()) { + RootEndOffset = + RootItem.CU.getDebugInfoEntry(*SiblingIdx)->getOffset(); + } else { + RootEndOffset = RootItem.CU.getOrigUnit().getNextUnitOffset(); + } + + // Do not put item in work list if it is an ancestor of RootItem. + // (since we will visit and mark it as kept during normal traversing of + // RootItem children) + if (RootStartOffset > RefEntry->getOffset() || + RefEntry->getOffset() >= RootEndOffset) + addItemToWorklist(*RefDie->first, RefEntry); + } + } else if (Context.InterCUProcessingStarted && RefDie->second != 0) { + // If referenced DIE is in other compilation unit and + // it is safe to navigate other units DIEs. + addItemToWorklist(*RefDie->first, + RefDie->first->getDebugInfoEntry(RefDie->second)); + } else { + // Delay resolving reference. + RefDie->first->setInterconnectedCU(); + CU.setInterconnectedCU(); + Context.HasNewInterconnectedCUs = true; + return false; + } + } + + return true; +} + +// Returns true if the specified DIE type allows removing children. +static bool childrenCanBeRemoved(uint32_t Tag) { + switch (Tag) { + default: + return true; + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_common_block: + case dwarf::DW_TAG_lexical_block: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_subprogram: + case dwarf::DW_TAG_subroutine_type: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_array_type: + return false; + } + llvm_unreachable("Invalid Tag"); +} + +void DependencyTracker::addItemToWorklist(CompileUnit &CU, + const DWARFDebugInfoEntry *Entry) { + if (Entry->getAbbreviationDeclarationPtr() == nullptr) + return; + + const DWARFDebugInfoEntry *EntryToAdd = Entry; + + // If parent does not allow children removing then use that parent as a root + // DIE. + std::optional ParentIdx = Entry->getParentIdx(); + while (ParentIdx) { + const DWARFDebugInfoEntry *ParentEntry = CU.getDebugInfoEntry(*ParentIdx); + if (childrenCanBeRemoved(ParentEntry->getTag())) + break; + EntryToAdd = ParentEntry; + ParentIdx = ParentEntry->getParentIdx(); + } + + // Check if the DIE entry is already kept. + if (CU.getDIEInfo(EntryToAdd).getKeep()) + return; + + RootEntriesWorkList.emplace_back(CU, EntryToAdd); +} + +bool DependencyTracker::isLiveVariableEntry(CompileUnit &CU, + const DWARFDebugInfoEntry *Entry) { + DWARFDie DIE = CU.getDIE(Entry); + CompileUnit::DIEInfo &Info = CU.getDIEInfo(DIE); + + if (TrackLiveness) { + const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); + + // Global variables with constant value can always be kept. + if (!Info.getIsInFunctionScope() && + Abbrev->findAttributeIndex(dwarf::DW_AT_const_value)) + return true; + + // See if there is a relocation to a valid debug map entry inside this + // variable's location. The order is important here. We want to always check + // if the variable has a location expression address. + // However, we don't want a static variable in a function to force us to + // keep the enclosing function, unless requested explicitly. + std::pair> LocExprAddrAndRelocAdjustment = + CU.getContaingFile().Addresses->getVariableRelocAdjustment(DIE); + + if (!LocExprAddrAndRelocAdjustment.second) + return false; + + if ((Info.getIsInFunctionScope()) && + !LLVM_UNLIKELY(CU.getGlobalData().getOptions().KeepFunctionForStatic)) + return false; + } + + if (CU.getGlobalData().getOptions().Verbose) { + outs() << "Keeping variable DIE:"; + DIDumpOptions DumpOpts; + DumpOpts.ChildRecurseDepth = 0; + DumpOpts.Verbose = CU.getGlobalData().getOptions().Verbose; + DIE.dump(outs(), 8 /* Indent */, DumpOpts); + } + + return true; +} + +bool DependencyTracker::isLiveSubprogramEntry( + CompileUnit &CU, const DWARFDebugInfoEntry *Entry) { + DWARFDie DIE = CU.getDIE(Entry); + + std::optional LowPc; + std::optional HighPc; + std::optional RelocAdjustment; + + if (TrackLiveness) { + LowPc = dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc)); + if (!LowPc) + return false; + + if (DIE.getTag() == dwarf::DW_TAG_subprogram) { + // Validate subprogram address range. + + HighPc = DIE.getHighPC(*LowPc); + if (!HighPc) { + CU.warn("function without high_pc. Range will be discarded.", &DIE); + return false; + } + + if (*LowPc > *HighPc) { + CU.warn("low_pc greater than high_pc. Range will be discarded.", &DIE); + return false; + } + } else if (DIE.getTag() == dwarf::DW_TAG_variable) { + // FIXME: dsymutil-classic compat. dsymutil-classic doesn't consider + // labels that don't fall into the CU's aranges. This is wrong IMO. Debug + // info generation bugs aside, this is really wrong in the case of labels, + // where a label marking the end of a function will have a PC == CU's + // high_pc. + if (dwarf::toAddress( + CU.getOrigUnit().getUnitDIE().find(dwarf::DW_AT_high_pc)) + .value_or(UINT64_MAX) <= LowPc) + return false; + + if (!CU.hasLabelAt(*LowPc)) + CU.addLabelLowPc(*LowPc, *RelocAdjustment); + } + + RelocAdjustment = + CU.getContaingFile().Addresses->getSubprogramRelocAdjustment(DIE); + if (!RelocAdjustment) + return false; + } + + if (CU.getGlobalData().getOptions().Verbose) { + outs() << "Keeping subprogram DIE:"; + DIDumpOptions DumpOpts; + DumpOpts.ChildRecurseDepth = 0; + DumpOpts.Verbose = CU.getGlobalData().getOptions().Verbose; + DIE.dump(outs(), 8 /* Indent */, DumpOpts); + } + + if (!TrackLiveness || DIE.getTag() == dwarf::DW_TAG_label) + return true; + + CU.addFunctionRange(*LowPc, *HighPc, *RelocAdjustment); + return true; +} + +void DependencyTracker::setDIEPlacementAndTypename(CompileUnit::DIEInfo &Info) { + Info.setPlacement(CompileUnit::PlainDwarf); +} + +} // end of namespace dwarflinker_parallel +} // namespace llvm diff --git a/llvm/lib/DWARFLinkerParallel/OutputSections.h b/llvm/lib/DWARFLinkerParallel/OutputSections.h --- a/llvm/lib/DWARFLinkerParallel/OutputSections.h +++ b/llvm/lib/DWARFLinkerParallel/OutputSections.h @@ -9,56 +9,374 @@ #ifndef LLVM_LIB_DWARFLINKERPARALLEL_OUTPUTSECTIONS_H #define LLVM_LIB_DWARFLINKERPARALLEL_OUTPUTSECTIONS_H +#include "ArrayList.h" +#include "StringEntryToDwarfStringPoolEntryMap.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/CodeGen/DwarfStringPoolEntry.h" +#include "llvm/DWARFLinkerParallel/StringPool.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFObject.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/MemoryBufferRef.h" +#include "llvm/Support/raw_ostream.h" #include #include namespace llvm { namespace dwarflinker_parallel { -/// This class keeps offsets to the debug sections. Any object which is -/// supposed to be emitted into the debug section should use this class to -/// track debug sections offsets. -class OutputSections { -public: - /// List of tracked debug sections. - enum class DebugSectionKind : uint8_t { - DebugInfo = 0, - DebugLine, - DebugFrame, - DebugRange, - DebugRngLists, - DebugLoc, - DebugLocLists, - DebugARanges, - DebugAbbrev, - DebugMacinfo, - DebugMacro, - }; - constexpr static size_t SectionKindsNum = 11; - - /// Recognise the section name and match it with the DebugSectionKind. - static std::optional parseDebugSectionName(StringRef Name); +/// List of tracked debug tables. +enum class DebugSectionKind : uint8_t { + DebugInfo = 0, + DebugLine, + DebugFrame, + DebugRange, + DebugRngLists, + DebugLoc, + DebugLocLists, + DebugARanges, + DebugAbbrev, + DebugMacinfo, + DebugMacro, + DebugAddr, + NumberOfEnumEntries // must be last +}; +constexpr static size_t SectionKindsNum = + static_cast(DebugSectionKind::NumberOfEnumEntries); + +/// Recognise the table name and match it with the DebugSectionKind. +std::optional parseDebugTableName(StringRef Name); + +/// Return the name of the section. +const StringLiteral &getSectionName(DebugSectionKind SectionKind); + +/// There are fields(sizes, offsets) which should be updated after +/// sections are generated. To remember offsets and related data +/// the descendants of SectionPatch structure should be used. + +struct SectionPatch { + uint64_t PatchOffset = 0; +}; + +/// This structure is used to update strings offsets into .debug_str. +struct DebugStrPatch : SectionPatch { + StringEntry *String = nullptr; +}; + +/// This structure is used to update strings offsets into .debug_line_str. +struct DebugLineStrPatch : SectionPatch { + StringEntry *String = nullptr; +}; + +/// This structure is used to update range list offset into +/// .debug_ranges/.debug_rnglists. +struct DebugRangePatch : SectionPatch { + /// Indicates patch which points to immediate compile unit's attribute. + bool IsCompileUnitRanges = false; +}; + +/// This structure is used to update location list offset into +/// .debug_loc/.debug_loclists. +struct DebugLocPatch : SectionPatch { + int64_t AddrAdjustmentValue = 0; +}; + +/// This structure is used to update offset with start of another section. +struct SectionDescriptor; +struct DebugOffsetPatch : SectionPatch { + DebugOffsetPatch(uint64_t PatchOffset, SectionDescriptor *SectionPtr, + bool AddLocalValue = false) + : SectionPatch({PatchOffset}), SectionPtr(SectionPtr, AddLocalValue) {} + + PointerIntPair SectionPtr; +}; + +/// This structure is used to update reference to the DIE. +struct DebugDieRefPatch : SectionPatch { + DebugDieRefPatch(uint64_t PatchOffset, CompileUnit *SrcCU, CompileUnit *RefCU, + uint32_t RefIdx); + + PointerIntPair RefCU; + uint64_t RefDieIdxOrClonedOffset; +}; + +/// This structure is used to update reference to the DIE of ULEB128 form. +struct DebugULEB128DieRefPatch : SectionPatch { + DebugULEB128DieRefPatch(uint64_t PatchOffset, CompileUnit *SrcCU, + CompileUnit *RefCU, uint32_t RefIdx); + + PointerIntPair RefCU; + uint64_t RefDieIdxOrClonedOffset; +}; + +/// Type for section data. +using OutSectionDataTy = SmallString<0>; + +/// Type for list of pointers to patches offsets. +using OffsetsPtrVector = SmallVector; + +class OutputSections; + +/// This structure is used to keep data of the concrete section. +/// Like data bits, list of patches, format. +struct SectionDescriptor { + friend OutputSections; + + SectionDescriptor() : OS(Contents) {} + + /// Erase whole section contents(data bits, list of patches, format). + void erase(); /// When objects(f.e. compile units) are glued into the single file, /// the debug sections corresponding to the concrete object are assigned - /// with offsets inside the whole file. This method returns offset - /// to the \p SectionKind debug section, corresponding to this object. - uint64_t getStartOffset(DebugSectionKind SectionKind) const { - return Offsets[static_cast< + /// with offsets inside the whole file. This field keeps offset + /// to the debug section, corresponding to this object. + uint64_t StartOffset; + + /// Stream which stores data to the Contents. + raw_svector_ostream OS; + + /// Section patches. +#define ADD_PATCHES_LIST(T) \ + T ¬ePatch(const T &Patch) { return List##T.noteItem(Patch); } \ + ArrayList List##T; + + ADD_PATCHES_LIST(DebugStrPatch) + ADD_PATCHES_LIST(DebugLineStrPatch) + ADD_PATCHES_LIST(DebugRangePatch) + ADD_PATCHES_LIST(DebugLocPatch) + ADD_PATCHES_LIST(DebugDieRefPatch) + ADD_PATCHES_LIST(DebugULEB128DieRefPatch) + ADD_PATCHES_LIST(DebugOffsetPatch) + + /// Offsets to some fields are not known at the moment of noting patch. + /// In that case we remember pointers to patch offset to update them later. + template + void notePatchWithOffsetUpdate(const T &Patch, + OffsetsPtrVector &PatchesOffsetsList) { + PatchesOffsetsList.emplace_back(¬ePatch(Patch).PatchOffset); + } + + /// Some sections are emitted using AsmPrinter. In that case "Contents" + /// member of SectionDescriptor contains elf file. This method searches + /// for section data inside elf file and remember offset to it. + void setSizesForSectionCreatedByAsmPrinter(); + + /// Returns section content. + StringRef getContents() { + if (SectionOffsetInsideAsmPrinterOutputStart == 0) + return StringRef(Contents.data(), Contents.size()); + + return Contents.slice(SectionOffsetInsideAsmPrinterOutputStart, + SectionOffsetInsideAsmPrinterOutputEnd); + } + + /// Emit unit length into the current section contents. + void emitUnitLength(uint64_t Length) { + maybeEmitDwarf64Mark(); + emitIntVal(Length, getFormParams().getDwarfOffsetByteSize()); + } + + /// Emit DWARF64 mark into the current section contents. + void maybeEmitDwarf64Mark() { + if (getFormParams().Format != dwarf::DWARF64) + return; + emitIntVal(dwarf::DW_LENGTH_DWARF64, 4); + } + + /// Emit specified offset value into the current section contents. + void emitOffset(uint64_t Val) { + emitIntVal(Val, getFormParams().getDwarfOffsetByteSize()); + } + + /// Emit specified integer value into the current section contents. + void emitIntVal(uint64_t Val, unsigned Size); + + /// Emit specified string value into the current section contents. + void emitString(dwarf::Form StringForm, const char *StringVal); + + /// Emit specified inplace string value into the current section contents. + void emitInplaceString(StringRef String) { + OS << String; + emitIntVal(0, 1); + } + + /// Emit string placeholder into the current section contents. + void emitStringPlaceholder() { + // emit bad offset which should be updated later. + emitOffset(0xBADDEF); + } + + /// Write specified \p Value of \p AttrForm to the \p PatchOffset. + void apply(uint64_t PatchOffset, dwarf::Form AttrForm, uint64_t Val); + + /// Returns section kind. + DebugSectionKind getKind() { return SectionKind; } + + /// Returns section name. + const StringLiteral &getName() const { return getSectionName(SectionKind); } + + /// Returns endianess used by section. + support::endianness getEndianess() const { return Endianess; }; + + /// Returns FormParams used by section. + dwarf::FormParams getFormParams() const { return Format; } + + /// Returns integer value of \p Size located by specified \p PatchOffset. + uint64_t getIntVal(uint64_t PatchOffset, unsigned Size); + + void setGlobalData(LinkingGlobalData *GlobalData) { + this->GlobalData = GlobalData; + ListDebugStrPatch.setAllocator(&GlobalData->getAllocator()); + ListDebugLineStrPatch.setAllocator(&GlobalData->getAllocator()); + ListDebugRangePatch.setAllocator(&GlobalData->getAllocator()); + ListDebugLocPatch.setAllocator(&GlobalData->getAllocator()); + ListDebugDieRefPatch.setAllocator(&GlobalData->getAllocator()); + ListDebugULEB128DieRefPatch.setAllocator(&GlobalData->getAllocator()); + ListDebugOffsetPatch.setAllocator(&GlobalData->getAllocator()); + } + +protected: + /// Writes integer value \p Val of \p Size by specified \p PatchOffset. + void applyIntVal(uint64_t PatchOffset, uint64_t Val, unsigned Size); + + /// Writes integer value \p Val of ULEB128 format by specified \p PatchOffset. + void applyULEB128(uint64_t PatchOffset, uint64_t Val); + + /// Writes integer value \p Val of SLEB128 format by specified \p PatchOffset. + void applySLEB128(uint64_t PatchOffset, uint64_t Val); + + /// Sets output format. + void setOutputFormat(dwarf::FormParams Format, + support::endianness Endianess) { + this->Format = Format; + this->Endianess = Endianess; + } + + LinkingGlobalData *GlobalData = nullptr; + + /// The section kind. + DebugSectionKind SectionKind; + + /// Section data bits. + OutSectionDataTy Contents; + + /// Some sections are generated using AsmPrinter. The real section data + /// located inside elf file in that case. Following fields points to the + /// real section content inside elf file. + size_t SectionOffsetInsideAsmPrinterOutputStart = 0; + size_t SectionOffsetInsideAsmPrinterOutputEnd = 0; + + /// Output format. + dwarf::FormParams Format = {4, 4, dwarf::DWARF32}; + support::endianness Endianess = support::endianness::little; +}; + +/// This class keeps contents and offsets to the debug sections. Any objects +/// which is supposed to be emitted into the debug sections should use this +/// class to track debug sections offsets and keep sections data. +class OutputSections { +public: + OutputSections() { + for (size_t Idx = 0; Idx < SectionKindsNum; ++Idx) + SectionDescriptors[Idx].SectionKind = static_cast(Idx); + } + + /// Sets output format for all keeping sections. + void setOutputFormat(DWARFUnit &OriginalUnit) { + setOutputFormat(OriginalUnit.getFormParams(), + OriginalUnit.isLittleEndian() ? support::endianness::little + : support::endianness::big); + } + + /// Sets output format for all keeping sections. + void setOutputFormat(dwarf::FormParams Format, + support::endianness Endianess) { + this->Format = Format; + this->Endianess = Endianess; + + for (size_t Idx = 0; Idx < SectionKindsNum; ++Idx) { + SectionDescriptors[Idx].Format = Format; + SectionDescriptors[Idx].Endianess = Endianess; + } + } + + void setGlobalData(LinkingGlobalData *GlobalData) { + this->GlobalData = GlobalData; + for (size_t Idx = 0; Idx < SectionKindsNum; ++Idx) + SectionDescriptors[Idx].setGlobalData(GlobalData); + } + + /// Returns descriptor for the specified section of \p SectionKind. + const SectionDescriptor & + getSectionDescriptor(DebugSectionKind SectionKind) const { + return SectionDescriptors[static_cast< + typename std::underlying_type::type>(SectionKind)]; + } + + /// Returns descriptor for the specified section of \p SectionKind. + SectionDescriptor &getSectionDescriptor(DebugSectionKind SectionKind) { + return SectionDescriptors[static_cast< typename std::underlying_type::type>(SectionKind)]; } - /// Set offset to the start of specified \p SectionKind debug section, - /// corresponding to this object. - void setStartOffset(DebugSectionKind SectionKind, uint64_t Offset) { - Offsets[static_cast::type>( - SectionKind)] = Offset; + /// Erases data of all sections. + void eraseSections() { + for (SectionDescriptor &CurSection : SectionDescriptors) + CurSection.erase(); } + /// Enumerate all sections and call \p Handler for each. + void forEach(function_ref Handler) { + for (size_t Idx = 0; Idx < SectionKindsNum; ++Idx) + Handler(SectionDescriptors[Idx]); + } + + /// Enumerate all sections, for each section set current offset + /// (kept by \p SectionSizesAccumulator), update current offset with section + /// length. + void assignSectionsOffsetAndAccumulateSize( + std::array &SectionSizesAccumulator) { + for (size_t Idx = 0; Idx < SectionKindsNum; ++Idx) { + SectionDescriptor &Section = SectionDescriptors[Idx]; + Section.StartOffset = SectionSizesAccumulator[Idx]; + SectionSizesAccumulator[Idx] += Section.getContents().size(); + } + } + + /// Enumerate all sections, for each section apply all section patches. + void applyPatches(SectionDescriptor &Section, + StringEntryToDwarfStringPoolEntryMap &DebugStrStrings, + StringEntryToDwarfStringPoolEntryMap &DebugLineStrStrings); + + /// Endiannes for the sections. + support::endianness getEndianness() const { return Endianess; } + + /// Return DWARF version. + uint16_t getVersion() const { return Format.Version; } + + /// Return size of header of debug_info table. + uint16_t getHeaderSize() const { return Format.Version >= 5 ? 12 : 11; } + + /// Return size of address. + const dwarf::FormParams &getFormParams() const { return Format; } + protected: - /// Offsets to the debug sections composing this object. - std::array Offsets = {0}; + LinkingGlobalData *GlobalData = nullptr; + + /// Format for sections. + dwarf::FormParams Format = {4, 4, dwarf::DWARF32}; + + /// Endiannes for sections. + support::endianness Endianess = support::endianness::little; + + /// All keeping sections. + std::array SectionDescriptors; }; } // end of namespace dwarflinker_parallel diff --git a/llvm/lib/DWARFLinkerParallel/OutputSections.cpp b/llvm/lib/DWARFLinkerParallel/OutputSections.cpp --- a/llvm/lib/DWARFLinkerParallel/OutputSections.cpp +++ b/llvm/lib/DWARFLinkerParallel/OutputSections.cpp @@ -7,30 +7,380 @@ //===----------------------------------------------------------------------===// #include "OutputSections.h" +#include "DWARFLinkerCompileUnit.h" #include "llvm/ADT/StringSwitch.h" namespace llvm { namespace dwarflinker_parallel { -std::optional -OutputSections::parseDebugSectionName(llvm::StringRef SecName) { - return llvm::StringSwitch>( - SecName) - .Case("debug_info", DebugSectionKind::DebugInfo) - .Case("debug_line", DebugSectionKind::DebugLine) - .Case("debug_frame", DebugSectionKind::DebugFrame) - .Case("debug_ranges", DebugSectionKind::DebugRange) - .Case("debug_rnglists", DebugSectionKind::DebugRngLists) - .Case("debug_loc", DebugSectionKind::DebugLoc) - .Case("debug_loclists", DebugSectionKind::DebugLocLists) - .Case("debug_aranges", DebugSectionKind::DebugARanges) - .Case("debug_abbrev", DebugSectionKind::DebugAbbrev) - .Case("debug_macinfo", DebugSectionKind::DebugMacinfo) - .Case("debug_macro", DebugSectionKind::DebugMacro) +static constexpr StringLiteral SectionNames[SectionKindsNum] = { + "debug_info", "debug_line", "debug_frame", "debug_ranges", + "debug_rnglists", "debug_loc", "debug_loclists", "debug_aranges", + "debug_abbrev", "debug_macinfo", "debug_macro", "debug_addr", +}; + +const StringLiteral &getSectionName(DebugSectionKind SectionKind) { + return SectionNames[static_cast(SectionKind)]; +} + +std::optional parseDebugTableName(llvm::StringRef SecName) { + return llvm::StringSwitch>( + SecName.substr(SecName.find_first_not_of("._"))) + .Case(getSectionName(DebugSectionKind::DebugInfo), + DebugSectionKind::DebugInfo) + .Case(getSectionName(DebugSectionKind::DebugLine), + DebugSectionKind::DebugLine) + .Case(getSectionName(DebugSectionKind::DebugFrame), + DebugSectionKind::DebugFrame) + .Case(getSectionName(DebugSectionKind::DebugRange), + DebugSectionKind::DebugRange) + .Case(getSectionName(DebugSectionKind::DebugRngLists), + DebugSectionKind::DebugRngLists) + .Case(getSectionName(DebugSectionKind::DebugLoc), + DebugSectionKind::DebugLoc) + .Case(getSectionName(DebugSectionKind::DebugLocLists), + DebugSectionKind::DebugLocLists) + .Case(getSectionName(DebugSectionKind::DebugARanges), + DebugSectionKind::DebugARanges) + .Case(getSectionName(DebugSectionKind::DebugAbbrev), + DebugSectionKind::DebugAbbrev) + .Case(getSectionName(DebugSectionKind::DebugMacinfo), + DebugSectionKind::DebugMacinfo) + .Case(getSectionName(DebugSectionKind::DebugMacro), + DebugSectionKind::DebugMacro) + .Case(getSectionName(DebugSectionKind::DebugAddr), + DebugSectionKind::DebugAddr) .Default(std::nullopt); return std::nullopt; } +DebugDieRefPatch::DebugDieRefPatch(uint64_t PatchOffset, CompileUnit *SrcCU, + CompileUnit *RefCU, uint32_t RefIdx) + : SectionPatch({PatchOffset}), + RefCU(RefCU, (SrcCU != nullptr) && + (SrcCU->getUniqueID() == RefCU->getUniqueID())), + RefDieIdxOrClonedOffset(RefIdx) {} + +DebugULEB128DieRefPatch::DebugULEB128DieRefPatch(uint64_t PatchOffset, + CompileUnit *SrcCU, + CompileUnit *RefCU, + uint32_t RefIdx) + : SectionPatch({PatchOffset}), + RefCU(RefCU, SrcCU->getUniqueID() == RefCU->getUniqueID()), + RefDieIdxOrClonedOffset(RefIdx) {} + +void SectionDescriptor::erase() { + StartOffset = 0; + Contents = OutSectionDataTy(); + ListDebugStrPatch.erase(); + ListDebugLineStrPatch.erase(); + ListDebugRangePatch.erase(); + ListDebugLocPatch.erase(); + ListDebugDieRefPatch.erase(); + ListDebugULEB128DieRefPatch.erase(); + ListDebugOffsetPatch.erase(); +} + +void SectionDescriptor::setSizesForSectionCreatedByAsmPrinter() { + if (Contents.empty()) + return; + + MemoryBufferRef Mem(Contents, "obj"); + Expected> Obj = + object::ObjectFile::createObjectFile(Mem); + if (!Obj) { + consumeError(Obj.takeError()); + Contents.clear(); + return; + } + + for (const object::SectionRef &Sect : (*Obj).get()->sections()) { + Expected SectNameOrErr = Sect.getName(); + if (!SectNameOrErr) { + consumeError(SectNameOrErr.takeError()); + continue; + } + + if (std::optional SectKind = + parseDebugTableName(*SectNameOrErr)) { + if (*SectKind == SectionKind) { + Expected Data = Sect.getContents(); + if (!Data) { + consumeError(SectNameOrErr.takeError()); + Contents.clear(); + return; + } + + SectionOffsetInsideAsmPrinterOutputStart = + Data->data() - Contents.data(); + SectionOffsetInsideAsmPrinterOutputEnd = + SectionOffsetInsideAsmPrinterOutputStart + Data->size(); + } + } + } + + return; +} + +void SectionDescriptor::emitIntVal(uint64_t Val, unsigned Size) { + switch (Size) { + case 1: { + uint8_t ShortVal = static_cast(Val); + OS.write(reinterpret_cast(&ShortVal), Size); + } break; + case 2: { + uint16_t ShortVal = static_cast(Val); + ShortVal = support::endian::byte_swap(ShortVal, getEndianess()); + OS.write(reinterpret_cast(&ShortVal), Size); + } break; + case 4: { + uint32_t ShortVal = static_cast(Val); + ShortVal = support::endian::byte_swap(ShortVal, getEndianess()); + OS.write(reinterpret_cast(&ShortVal), Size); + } break; + case 8: { + uint64_t ShortVal = static_cast(Val); + ShortVal = support::endian::byte_swap(ShortVal, getEndianess()); + OS.write(reinterpret_cast(&ShortVal), Size); + } break; + default: + llvm_unreachable("Unsupported integer type size"); + } +} + +void SectionDescriptor::emitString(dwarf::Form StringForm, + const char *StringVal) { + assert(StringVal != nullptr); + + switch (StringForm) { + case dwarf::DW_FORM_string: { + emitInplaceString(GlobalData->translateString(StringVal)); + } break; + case dwarf::DW_FORM_strp: { + notePatch(DebugStrPatch{ + {OS.tell()}, GlobalData->getStringPool().insert(StringVal).first}); + emitStringPlaceholder(); + } break; + case dwarf::DW_FORM_line_strp: { + notePatch(DebugLineStrPatch{ + {OS.tell()}, GlobalData->getStringPool().insert(StringVal).first}); + emitStringPlaceholder(); + } break; + default: + llvm_unreachable("Unsupported string form"); + break; + }; +} + +void SectionDescriptor::apply(uint64_t PatchOffset, dwarf::Form AttrForm, + uint64_t Val) { + switch (AttrForm) { + case dwarf::DW_FORM_strp: + case dwarf::DW_FORM_line_strp: { + applyIntVal(PatchOffset, Val, Format.getDwarfOffsetByteSize()); + } break; + + case dwarf::DW_FORM_ref_addr: { + applyIntVal(PatchOffset, Val, Format.getRefAddrByteSize()); + } break; + case dwarf::DW_FORM_ref1: { + applyIntVal(PatchOffset, Val, 1); + } break; + case dwarf::DW_FORM_ref2: { + applyIntVal(PatchOffset, Val, 2); + } break; + case dwarf::DW_FORM_ref4: { + applyIntVal(PatchOffset, Val, 4); + } break; + case dwarf::DW_FORM_ref8: { + applyIntVal(PatchOffset, Val, 8); + } break; + + case dwarf::DW_FORM_data1: { + applyIntVal(PatchOffset, Val, 1); + } break; + case dwarf::DW_FORM_data2: { + applyIntVal(PatchOffset, Val, 2); + } break; + case dwarf::DW_FORM_data4: { + applyIntVal(PatchOffset, Val, 4); + } break; + case dwarf::DW_FORM_data8: { + applyIntVal(PatchOffset, Val, 8); + } break; + case dwarf::DW_FORM_udata: { + applyULEB128(PatchOffset, Val); + } break; + case dwarf::DW_FORM_sdata: { + applySLEB128(PatchOffset, Val); + } break; + case dwarf::DW_FORM_sec_offset: { + applyIntVal(PatchOffset, Val, Format.getDwarfOffsetByteSize()); + } break; + case dwarf::DW_FORM_flag: { + applyIntVal(PatchOffset, Val, 1); + } break; + + default: + llvm_unreachable("Unsupported attribute form"); + break; + } +} + +uint64_t SectionDescriptor::getIntVal(uint64_t PatchOffset, unsigned Size) { + assert(PatchOffset < getContents().size()); + switch (Size) { + case 1: { + return support::endian::read( + const_cast(getContents().data() + PatchOffset), Endianess); + } + case 2: { + return support::endian::read( + const_cast(getContents().data() + PatchOffset), Endianess); + } + case 4: { + return support::endian::read( + const_cast(getContents().data() + PatchOffset), Endianess); + } + case 8: { + return support::endian::read( + const_cast(getContents().data() + PatchOffset), Endianess); + } + } + llvm_unreachable("Unsupported integer type size"); + return 0; +} + +void SectionDescriptor::applyIntVal(uint64_t PatchOffset, uint64_t Val, + unsigned Size) { + assert(PatchOffset < getContents().size()); + + switch (Size) { + case 1: { + support::endian::write( + const_cast(getContents().data() + PatchOffset), + static_cast(Val), Endianess); + } break; + case 2: { + support::endian::write( + const_cast(getContents().data() + PatchOffset), + static_cast(Val), Endianess); + } break; + case 4: { + support::endian::write( + const_cast(getContents().data() + PatchOffset), + static_cast(Val), Endianess); + } break; + case 8: { + support::endian::write( + const_cast(getContents().data() + PatchOffset), + static_cast(Val), Endianess); + } break; + default: + llvm_unreachable("Unsupported integer type size"); + } +} + +void SectionDescriptor::applyULEB128(uint64_t PatchOffset, uint64_t Val) { + assert(PatchOffset < getContents().size()); + + uint8_t ULEB[16]; + uint8_t DestSize = Format.getDwarfOffsetByteSize() + 1; + uint8_t RealSize = encodeULEB128(Val, ULEB, DestSize); + + memcpy(const_cast(getContents().data() + PatchOffset), ULEB, + RealSize); +} + +/// Writes integer value \p Val of SLEB128 format by specified \p PatchOffset. +void SectionDescriptor::applySLEB128(uint64_t PatchOffset, uint64_t Val) { + assert(PatchOffset < getContents().size()); + + uint8_t SLEB[16]; + uint8_t DestSize = Format.getDwarfOffsetByteSize() + 1; + uint8_t RealSize = encodeSLEB128(Val, SLEB, DestSize); + + memcpy(const_cast(getContents().data() + PatchOffset), SLEB, + RealSize); +} + +void OutputSections::applyPatches( + SectionDescriptor &Section, + StringEntryToDwarfStringPoolEntryMap &DebugStrStrings, + StringEntryToDwarfStringPoolEntryMap &DebugLineStrStrings) { + + Section.ListDebugStrPatch.forEach([&](DebugStrPatch &Patch) { + DwarfStringPoolEntryWithExtString *Entry = + DebugStrStrings.getExistingEntry(Patch.String); + assert(Entry != nullptr); + + Section.apply(Patch.PatchOffset, dwarf::DW_FORM_strp, Entry->Offset); + }); + + Section.ListDebugLineStrPatch.forEach([&](DebugLineStrPatch &Patch) { + DwarfStringPoolEntryWithExtString *Entry = + DebugLineStrStrings.getExistingEntry(Patch.String); + assert(Entry != nullptr); + + Section.apply(Patch.PatchOffset, dwarf::DW_FORM_line_strp, Entry->Offset); + }); + + SectionDescriptor *RangeSection = nullptr; + if (Format.Version >= 5) + RangeSection = &getSectionDescriptor(DebugSectionKind::DebugRngLists); + else + RangeSection = &getSectionDescriptor(DebugSectionKind::DebugRange); + Section.ListDebugRangePatch.forEach([&](DebugRangePatch &Patch) { + uint64_t FinalValue = + Section.getIntVal(Patch.PatchOffset, Format.getDwarfOffsetByteSize()); + FinalValue += RangeSection->StartOffset; + + Section.apply(Patch.PatchOffset, dwarf::DW_FORM_sec_offset, FinalValue); + }); + + SectionDescriptor *LocationSection = nullptr; + if (Format.Version >= 5) + LocationSection = &getSectionDescriptor(DebugSectionKind::DebugLocLists); + else + LocationSection = &getSectionDescriptor(DebugSectionKind::DebugLoc); + Section.ListDebugLocPatch.forEach([&](DebugLocPatch &Patch) { + uint64_t FinalValue = + Section.getIntVal(Patch.PatchOffset, Format.getDwarfOffsetByteSize()); + FinalValue += LocationSection->StartOffset; + + Section.apply(Patch.PatchOffset, dwarf::DW_FORM_sec_offset, FinalValue); + }); + + Section.ListDebugDieRefPatch.forEach([&](DebugDieRefPatch &Patch) { + uint64_t FinalOffset = Patch.RefDieIdxOrClonedOffset; + dwarf::Form FinalForm = dwarf::DW_FORM_ref4; + + if (!Patch.RefCU.getInt()) { + FinalForm = dwarf::DW_FORM_ref_addr; + FinalOffset += Patch.RefCU.getPointer() + ->getSectionDescriptor(DebugSectionKind::DebugInfo) + .StartOffset; + } + Section.apply(Patch.PatchOffset, FinalForm, FinalOffset); + }); + + Section.ListDebugULEB128DieRefPatch.forEach( + [&](DebugULEB128DieRefPatch &Patch) { + assert(Patch.RefCU.getInt()); + Section.apply(Patch.PatchOffset, dwarf::DW_FORM_udata, + Patch.RefDieIdxOrClonedOffset); + }); + + Section.ListDebugOffsetPatch.forEach([&](DebugOffsetPatch &Patch) { + uint64_t FinalValue = Patch.SectionPtr.getPointer()->StartOffset; + if (Patch.SectionPtr.getInt()) + FinalValue += + Section.getIntVal(Patch.PatchOffset, Format.getDwarfOffsetByteSize()); + + Section.apply(Patch.PatchOffset, dwarf::DW_FORM_sec_offset, FinalValue); + }); +} + } // end of namespace dwarflinker_parallel } // end of namespace llvm diff --git a/llvm/lib/DWARFLinkerParallel/StringEntryToDwarfStringPoolEntryMap.h b/llvm/lib/DWARFLinkerParallel/StringEntryToDwarfStringPoolEntryMap.h new file mode 100644 --- /dev/null +++ b/llvm/lib/DWARFLinkerParallel/StringEntryToDwarfStringPoolEntryMap.h @@ -0,0 +1,72 @@ +//===- StringEntryToDwarfStringPoolEntryMap.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_LIB_DWARFLINKERPARALLEL_STRINGENTRYTODWARFSTRINGPOOLENTRYMAP_H +#define LLVM_LIB_DWARFLINKERPARALLEL_STRINGENTRYTODWARFSTRINGPOOLENTRYMAP_H + +#include "DWARFLinkerGlobalData.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/DWARFLinkerParallel/StringPool.h" + +namespace llvm { +namespace dwarflinker_parallel { + +/// This class creates a DwarfStringPoolEntry for the corresponding StringEntry. +class StringEntryToDwarfStringPoolEntryMap { +public: + StringEntryToDwarfStringPoolEntryMap(LinkingGlobalData &GlobalData) + : GlobalData(GlobalData) {} + ~StringEntryToDwarfStringPoolEntryMap() {} + + /// Create DwarfStringPoolEntry for specified StringEntry if necessary. + /// Initialize DwarfStringPoolEntry with initial values. + DwarfStringPoolEntryWithExtString *add(StringEntry *String) { + DwarfStringPoolEntriesTy::iterator it = DwarfStringPoolEntries.find(String); + + if (it == DwarfStringPoolEntries.end()) { + DwarfStringPoolEntryWithExtString *DataPtr = + GlobalData.getAllocator() + .Allocate(); + DataPtr->String = GlobalData.translateString(String->getKey()); + DataPtr->Index = DwarfStringPoolEntry::NotIndexed; + DataPtr->Offset = 0; + DataPtr->Symbol = nullptr; + it = DwarfStringPoolEntries.insert(std::make_pair(String, DataPtr)).first; + } + + assert(it->second != nullptr); + return it->second; + } + + /// Returns already existed DwarfStringPoolEntry for the specified + /// StringEntry. + DwarfStringPoolEntryWithExtString * + getExistingEntry(StringEntry *String) const { + DwarfStringPoolEntriesTy::const_iterator it = + DwarfStringPoolEntries.find(String); + + assert(it != DwarfStringPoolEntries.end()); + assert(it->second != nullptr); + return it->second; + } + + /// Erase contents of StringsForEmission. + void clear() { DwarfStringPoolEntries.clear(); } + +protected: + using DwarfStringPoolEntriesTy = + DenseMap; + DwarfStringPoolEntriesTy DwarfStringPoolEntries; + + LinkingGlobalData &GlobalData; +}; + +} // end of namespace dwarflinker_parallel +} // end namespace llvm + +#endif // LLVM_LIB_DWARFLINKERPARALLEL_STRINGENTRYTODWARFSTRINGPOOLENTRYMAP_H diff --git a/llvm/test/tools/dsymutil/ARM/dwarf5-dwarf4-combination-macho.test b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/dwarf5-dwarf4-combination-macho.test copy from llvm/test/tools/dsymutil/ARM/dwarf5-dwarf4-combination-macho.test copy to llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/dwarf5-dwarf4-combination-macho.test --- a/llvm/test/tools/dsymutil/ARM/dwarf5-dwarf4-combination-macho.test +++ b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/dwarf5-dwarf4-combination-macho.test @@ -31,7 +31,9 @@ RUN: rm -rf %t.dir && mkdir -p %t.dir -RUN: dsymutil -y %p/dummy-debug-map-amr64.map -oso-prepend-path=%p/../Inputs/DWARF5-DWARF4-combination -o %t.dir/dwarf5-dwarf4-combination-macho.dSYM +RUN: dsymutil --linker llvm -y %p/../dummy-debug-map-amr64.map \ +RUN: -oso-prepend-path=%p/../../Inputs/DWARF5-DWARF4-combination \ +RUN: -o %t.dir/dwarf5-dwarf4-combination-macho.dSYM RUN: llvm-dwarfdump %t.dir/dwarf5-dwarf4-combination-macho.dSYM -a --verbose | FileCheck %s @@ -49,7 +51,7 @@ CHECK-NEXT: [0x[[#%.16x,LOCLIST_PAIR_START:]], 0x[[#%.16x,LOCLIST_PAIR_END:]]): [[LOCLIST_EXPR:.*]] CHECK-NEXT: [0x[[#%.16x,LOCLIST_PAIR_START2:]], 0x[[#%.16x,LOCLIST_PAIR_END2:]]): [[LOCLIST_EXPR2:.*]]) -CHECK: 0x00000068: Compile Unit: length = 0x00000072, format = DWARF32, version = 0x0004, abbr_offset = 0x0000, addr_size = 0x08 +CHECK: 0x00000068: Compile Unit: length = 0x00000072, format = DWARF32, version = 0x0004, abbr_offset = 0x0056, addr_size = 0x08 CHECK: DW_AT_low_pc [DW_FORM_addr] (0x[[#%.16x,RANGE_LOWPC:]]) CHECK-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000000 CHECK-NEXT: [0x[[#%.16x,RANGE_START:]], 0x[[#%.16x,RANGE_END:]])) @@ -150,18 +152,3 @@ CHECK-NEXT: 0x00000000: range list header: length = 0x00000013, format = DWARF32, version = 0x0005, addr_size = 0x08, seg_size = 0x00, offset_entry_count = 0x00000000 CHECK-NEXT: ranges: CHECK-NEXT: [[RANGELIST_OFFSET]]: [DW_RLE_start_length]: {{.*}}[0x[[RANGELIST_OFFSET_START]], 0x[[RANGELIST_OFFSET_END]]) - -CHECK: .debug_names contents: -CHECK-NEXT: Name Index @ 0x0 { -CHECK-NEXT: Header { -CHECK-NEXT: Length: 0xBC -CHECK-NEXT: Format: DWARF32 -CHECK-NEXT: Version: 5 -CHECK-NEXT: CU count: 2 -CHECK-NEXT: Local TU count: 0 -CHECK-NEXT: Foreign TU count: 0 -CHECK-NEXT: Bucket count: 5 -CHECK-NEXT: Name count: 5 -CHECK-NEXT: Abbreviations table size: 0x11 -CHECK-NEXT: Augmentation: 'LLVM0700' -CHECK-NEXT: } diff --git a/llvm/test/tools/dsymutil/ARM/dwarf5-macho.test b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/dwarf5-macho.test copy from llvm/test/tools/dsymutil/ARM/dwarf5-macho.test copy to llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/dwarf5-macho.test --- a/llvm/test/tools/dsymutil/ARM/dwarf5-macho.test +++ b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/dwarf5-macho.test @@ -18,7 +18,8 @@ RUN: rm -rf %t.dir && mkdir -p %t.dir -RUN: dsymutil -y %p/dummy-debug-map-amr64.map -oso-prepend-path=%p/../Inputs/DWARF5 -o %t.dir/dwarf5-macho.dSYM +RUN: dsymutil --linker llvm -y %p/../dummy-debug-map-amr64.map \ +RUN: -oso-prepend-path=%p/../../Inputs/DWARF5 -o %t.dir/dwarf5-macho.dSYM RUN: llvm-dwarfdump %t.dir/dwarf5-macho.dSYM -a --verbose | FileCheck %s CHECK:.debug_abbrev contents: @@ -88,18 +89,3 @@ CHECK-NEXT: 0x00000000: range list header: length = 0x00000013, format = DWARF32, version = 0x0005, addr_size = 0x08, seg_size = 0x00, offset_entry_count = 0x00000000 CHECK-NEXT: ranges: CHECK-NEXT: [[RANGELIST_OFFSET]]: [DW_RLE_start_length]: {{.*}}[0x[[RANGELIST_OFFSET_START]], 0x[[RANGELIST_OFFSET_END]]) - -CHECK: .debug_names contents: -CHECK-NEX:T Name Index @ 0x0 { -CHECK-NEX:T Header { -CHECK-NEX:T Length: 0x7C -CHECK-NEX:T Format: DWARF32 -CHECK-NEX:T Version: 5 -CHECK-NEX:T CU count: 1 -CHECK-NEX:T Local TU count: 0 -CHECK-NEX:T Foreign TU count: 0 -CHECK-NEX:T Bucket count: 3 -CHECK-NEX:T Name count: 3 -CHECK-NEX:T Abbreviations table size: 0xD -CHECK-NEX:T Augmentation: 'LLVM0700' -CHECK-NEX:T } diff --git a/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/fat-dylib-update.test b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/fat-dylib-update.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/fat-dylib-update.test @@ -0,0 +1,110 @@ +# REQUIRES: object-emission,system-darwin +# RUN: dsymutil --linker llvm -oso-prepend-path %p/../.. %p/../../Inputs/fat-test.arm.dylib -o %t.dSYM +# RUN: llvm-dwarfdump -a -v %t.dSYM/Contents/Resources/DWARF/fat-test.arm.dylib | FileCheck %s +# RUN: dsymutil --linker llvm -u %t.dSYM +# RUN: llvm-dwarfdump -a -v %t.dSYM/Contents/Resources/DWARF/fat-test.arm.dylib | FileCheck %s +# RUN: dsymutil --linker llvm -u %t.dSYM -o %t1.dSYM +# RUN: llvm-dwarfdump -a -v %t1.dSYM/Contents/Resources/DWARF/fat-test.arm.dylib | FileCheck %s + +CHECK: /Contents/Resources/DWARF/fat-test.arm.dylib(armv7): file format Mach-O arm + +CHECK: .debug_info contents: +CHECK: Compile Unit: length = 0x00000034, format = DWARF32, version = 0x0002, abbr_offset = 0x0000, addr_size = 0x04 (next unit at 0x00000038) +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( .debug_str[0x00000001] = "clang version 3.8.0 (trunk 243776)") +CHECK: DW_AT_language [DW_FORM_data2] (DW_LANG_C99) +CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000024] = "fat-test.c") +CHECK: DW_AT_stmt_list [DW_FORM_data4] (0x00000000) +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( .debug_str[0x0000002f] = "/Inputs") +CHECK: DW_TAG_variable [2] +CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000037] = "armv7_var") +CHECK: DW_AT_type [DW_FORM_ref4] (cu + 0x0030 => {0x00000030} +CHECK: DW_AT_external [DW_FORM_flag] (0x01) +CHECK: DW_AT_decl_file [DW_FORM_data1] ("/Inputs/fat-test.c") +CHECK: DW_AT_decl_line [DW_FORM_data1] (23) +CHECK: DW_AT_location [DW_FORM_block1] (DW_OP_addr 0x1000) +CHECK: DW_TAG_base_type [3] +CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000041] = "int") +CHECK: DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed) +CHECK: DW_AT_byte_size [DW_FORM_data1] (0x04) +CHECK: NULL + + +CHECK: .debug_line contents: +CHECK: Line table prologue: +CHECK: total_length: 0x0000002a +CHECK: version: 2 +CHECK: prologue_length: 0x00000021 +CHECK: min_inst_length: 1 +CHECK: default_is_stmt: 1 +CHECK: line_base: -5 +CHECK: line_range: 14 +CHECK: opcode_base: 13 + +CHECK: /Contents/Resources/DWARF/fat-test.arm.dylib(armv7s): file format Mach-O arm + +CHECK: .debug_info contents: +CHECK: Compile Unit: length = 0x00000034, format = DWARF32, version = 0x0002, abbr_offset = 0x0000, addr_size = 0x04 (next unit at 0x00000038) +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( .debug_str[0x00000001] = "clang version 3.8.0 (trunk 243776)") +CHECK: DW_AT_language [DW_FORM_data2] (DW_LANG_C99) +CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000024] = "fat-test.c") +CHECK: DW_AT_stmt_list [DW_FORM_data4] (0x00000000) +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( .debug_str[0x0000002f] = "/Inputs") +CHECK: DW_TAG_variable [2] +CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000037] = "armv7s_var") +CHECK: DW_AT_type [DW_FORM_ref4] (cu + 0x0030 => {0x00000030} +CHECK: DW_AT_external [DW_FORM_flag] (0x01) +CHECK: DW_AT_decl_file [DW_FORM_data1] ("/Inputs/fat-test.c") +CHECK: DW_AT_decl_line [DW_FORM_data1] (21) +CHECK: DW_AT_location [DW_FORM_block1] (DW_OP_addr 0x1000) +CHECK: DW_TAG_base_type [3] +CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000042] = "int") +CHECK: DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed) +CHECK: DW_AT_byte_size [DW_FORM_data1] (0x04) +CHECK: NULL + +CHECK: .debug_line contents: +CHECK: Line table prologue: +CHECK: total_length: 0x0000002a +CHECK: version: 2 +CHECK: prologue_length: 0x00000021 +CHECK: min_inst_length: 1 +CHECK: default_is_stmt: 1 +CHECK: line_base: -5 +CHECK: line_range: 14 +CHECK: opcode_base: 13 + +CHECK: /Contents/Resources/DWARF/fat-test.arm.dylib(arm64): file format Mach-O arm64 + +CHECK: .debug_info contents: +CHECK: Compile Unit: length = 0x00000038, format = DWARF32, version = 0x0002, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x0000003c) +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( .debug_str[0x00000001] = "clang version 3.8.0 (trunk 243776)") +CHECK: DW_AT_language [DW_FORM_data2] (DW_LANG_C99) +CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000024] = "fat-test.c") +CHECK: DW_AT_stmt_list [DW_FORM_data4] (0x00000000) +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( .debug_str[0x0000002f] = "/Inputs") +CHECK: DW_TAG_variable [2] +CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000037] = "arm64_var") +CHECK: DW_AT_type [DW_FORM_ref4] (cu + 0x0034 => {0x00000034} +CHECK: DW_AT_external [DW_FORM_flag] (0x01) +CHECK: DW_AT_decl_file [DW_FORM_data1] ("/Inputs/fat-test.c") +CHECK: DW_AT_decl_line [DW_FORM_data1] (25) +CHECK: DW_AT_location [DW_FORM_block1] (DW_OP_addr 0x4000) +CHECK: DW_TAG_base_type [3] +CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000041] = "int") +CHECK: DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed) +CHECK: DW_AT_byte_size [DW_FORM_data1] (0x04) +CHECK: NULL + +CHECK: .debug_line contents: +CHECK: Line table prologue: +CHECK: total_length: 0x0000002a +CHECK: version: 2 +CHECK: prologue_length: 0x00000021 +CHECK: min_inst_length: 1 +CHECK: default_is_stmt: 1 +CHECK: line_base: -5 +CHECK: line_range: 14 +CHECK: opcode_base: 13 diff --git a/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/obfuscated.test b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/obfuscated.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/obfuscated.test @@ -0,0 +1,119 @@ +REQUIRES: system-darwin + +RUN: dsymutil --linker llvm --symbol-map %p/../../Inputs/obfuscated.map %p/../../Inputs/obfuscated.arm64 -f -o - \ +RUN: | llvm-dwarfdump -v - \ +RUN: | FileCheck %s + +RUN: dsymutil --linker llvm --symbol-map %p/../../Inputs/obfuscated.map %p/../../Inputs/obfuscated.arm64 -f -o - \ +RUN: | llvm-dwarfdump -v - \ +RUN: | FileCheck --check-prefix=NOHIDDEN %s + +RUN: dsymutil --linker llvm --symbol-map %p/../../Inputs/obfuscated.2.map %p/../../Inputs/obfuscated.2.arm64 -f -o - \ +RUN: | llvm-dwarfdump -v - \ +RUN: | FileCheck --check-prefix=NOHIDDEN %s + +// Run with plist and make sure dsymutil finds it. +RUN: mkdir -p %t.dSYM/Contents/Resources/DWARF/ +RUN: mkdir -p %t.mapdir +RUN: cp %p/../../Inputs/obfuscated.arm64 %t.dSYM/Contents/Resources/DWARF/ +RUN: cp %p/../../Inputs/E828A486-8433-3A5E-B6DB-A6294D28133D.plist %t.dSYM/Contents/Resources/ +RUN: cp %p/../../Inputs/obfuscated.map %t.mapdir/506AA50A-6B26-3B37-86D2-DC6EBD57B720.bcsymbolmap +RUN: dsymutil --linker llvm --symbol-map %t.mapdir %t.dSYM 2>&1 | FileCheck --check-prefix=OBFUSCATING %s + +// Run without plist and make sure dsymutil doesn't crash. +RUN: rm %t.dSYM/Contents/Resources/E828A486-8433-3A5E-B6DB-A6294D28133D.plist +RUN: dsymutil --linker llvm --symbol-map %t.mapdir %t.dSYM 2>&1 | FileCheck --check-prefix=NOTOBFUSCATING %s + +OBFUSCATING-NOT: not unobfuscating + +NOTOBFUSCATING: not unobfuscating + +NOHIDDEN-NOT: __hidden# + +CHECK: .debug_info contents: + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)") +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "main.c") +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src") +CHECK: DW_TAG_subprogram [2] +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "main") + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)") +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "one.c") +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src") +CHECK: DW_TAG_subprogram [2] +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "one") + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)") +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "two.c") +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src") +CHECK: DW_TAG_subprogram [2] +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "two") + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)") +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "three.c") +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src") +CHECK: DW_TAG_subprogram [2] +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "three") + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)") +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "four.c") +CHECK: DW_AT_stmt_list [DW_FORM_data4] (0x0000011e) +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src") +CHECK: DW_TAG_subprogram [2] +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "four") + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)") +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "five.c") +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src") +CHECK: DW_TAG_subprogram [2] +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "five") + +CHECK: DW_TAG_compile_unit [1] * +CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)") +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "six.c") +CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src") +CHECK: DW_TAG_subprogram [2] +CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "six") + +CHECK: .debug_line contents: +CHECK: file_names[ 1]: +CHECK: name: "main.c" +CHECK: dir_index: 0 +CHECK: mod_time: 0x00000000 +CHECK: file_names[ 1]: +CHECK: name: "one.c" +CHECK: dir_index: 0 +CHECK: mod_time: 0x00000000 +CHECK: length: 0x00000000 +CHECK: file_names[ 1]: +CHECK: name: "two.c" +CHECK: dir_index: 0 +CHECK: mod_time: 0x00000000 +CHECK: length: 0x00000000 +CHECK: file_names[ 1]: +CHECK: name: "three.c" +CHECK: dir_index: 0 +CHECK: mod_time: 0x00000000 +CHECK: length: 0x00000000 +CHECK: file_names[ 1]: +CHECK: name: "four.c" +CHECK: dir_index: 0 +CHECK: mod_time: 0x00000000 +CHECK: length: 0x00000000 +CHECK: file_names[ 1]: +CHECK: name: "five.c" +CHECK: dir_index: 0 +CHECK: mod_time: 0x00000000 +CHECK: length: 0x00000000 +CHECK: file_names[ 1]: +CHECK: name: "six.c" +CHECK: dir_index: 0 +CHECK: mod_time: 0x00000000 +CHECK: length: 0x00000000 diff --git a/llvm/test/tools/dsymutil/ARM/call-pc-reloc.test b/llvm/test/tools/dsymutil/ARM/call-pc-reloc.test --- a/llvm/test/tools/dsymutil/ARM/call-pc-reloc.test +++ b/llvm/test/tools/dsymutil/ARM/call-pc-reloc.test @@ -17,4 +17,7 @@ RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/call_pc/main.arm64 -o %t.dSYM RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_pc +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/call_pc/main.arm64 -o %t.dSYM +RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_pc + CHECK: DW_AT_call_pc (0x0000000100007f94) diff --git a/llvm/test/tools/dsymutil/ARM/dwarf5-addrx-0x0-last.test b/llvm/test/tools/dsymutil/ARM/dwarf5-addrx-0x0-last.test --- a/llvm/test/tools/dsymutil/ARM/dwarf5-addrx-0x0-last.test +++ b/llvm/test/tools/dsymutil/ARM/dwarf5-addrx-0x0-last.test @@ -28,6 +28,14 @@ RUN: llvm-dwarfdump --verbose -debug-info %t.dSYM | FileCheck %s --check-prefix DEBUGINFO RUN: llvm-dwarfdump --verbose -debug-line %t.dSYM | FileCheck %s --check-prefix DEBUGLINE +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs \ +RUN: %p/../Inputs/private/tmp/dwarf5/dwarf5-addrx-0x0-last.out \ +RUN: -o %t.dSYM 2>&1 | FileCheck %s --allow-empty +RUN: llvm-dwarfdump --verify %t.dSYM 2>&1 | FileCheck %s +RUN: llvm-dwarfdump --verbose -debug-info %t.dSYM | FileCheck %s --check-prefix DEBUGINFO +RUN: llvm-dwarfdump --verbose -debug-line %t.dSYM | FileCheck %s --check-prefix DEBUGLINE + + CHECK-NOT: error: DEBUGINFO: DW_TAG_subprogram diff --git a/llvm/test/tools/dsymutil/ARM/dwarf5-dwarf4-combination-macho.test b/llvm/test/tools/dsymutil/ARM/dwarf5-dwarf4-combination-macho.test --- a/llvm/test/tools/dsymutil/ARM/dwarf5-dwarf4-combination-macho.test +++ b/llvm/test/tools/dsymutil/ARM/dwarf5-dwarf4-combination-macho.test @@ -45,7 +45,7 @@ CHECK: 0x00000033: DW_TAG_subprogram [2] * (0x0000000c) CHECK-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x[[#%.16x,LOCLIST_LOWPC:]]) CHECK: 0x00000050: DW_TAG_formal_parameter [3] (0x00000033) -CHECK-NEXT: DW_AT_location [DW_FORM_sec_offset] (0x[[LOCLIST_OFFSET:[0-9a-f]+]]: +CHECK-NEXT: DW_AT_location [DW_FORM_sec_offset] (0x[[LOCLIST_OFFSET:[0-9a-f]+]]: CHECK-NEXT: [0x[[#%.16x,LOCLIST_PAIR_START:]], 0x[[#%.16x,LOCLIST_PAIR_END:]]): [[LOCLIST_EXPR:.*]] CHECK-NEXT: [0x[[#%.16x,LOCLIST_PAIR_START2:]], 0x[[#%.16x,LOCLIST_PAIR_END2:]]): [[LOCLIST_EXPR2:.*]]) @@ -56,7 +56,7 @@ CHECK:0x0000009a: DW_TAG_subprogram [2] * (0x00000073) CHECK-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x[[#%.16x,LOC_LOWPC:]]) CHECK:0x000000b7: DW_TAG_formal_parameter [3] (0x0000009a) -CHECK-NEXT: DW_AT_location [DW_FORM_sec_offset] (0x[[LOC_OFFSET:[0-9a-f]+]]: +CHECK-NEXT: DW_AT_location [DW_FORM_sec_offset] (0x[[LOC_OFFSET:[0-9a-f]+]]: CHECK-NEXT: [0x[[#%.16x,LOC_PAIR_START:]], 0x[[#%.16x,LOC_PAIR_END:]]): [[LOC_EXPR:.*]] CHECK-NEXT: [0x[[#%.16x,LOC_PAIR_START2:]], 0x[[#%.16x,LOC_PAIR_END2:]]): [[LOC_EXPR2:.*]]) @@ -67,7 +67,7 @@ CHECK: .debug_loclists contents: CHECK-NEXT: 0x00000000: locations list header: length = 0x0000001f, format = DWARF32, version = 0x0005, addr_size = 0x08, seg_size = 0x00, offset_entry_count = 0x00000000 -CHECK-NEXT: 0x[[LOCLIST_OFFSET]]: +CHECK-NEXT: 0x[[LOCLIST_OFFSET]]: CHECK-NEXT: DW_LLE_base_address (0x[[#LOCLIST_LOWPC]]) CHECK-NEXT: DW_LLE_offset_pair (0x[[#sub(LOCLIST_PAIR_START,LOCLIST_LOWPC)]], 0x[[#sub(LOCLIST_PAIR_END,LOCLIST_LOWPC)]]) CHECK-NEXT: => [0x[[#LOCLIST_PAIR_START]], 0x[[#LOCLIST_PAIR_END]]): [[LOCLIST_EXPR]] diff --git a/llvm/test/tools/dsymutil/ARM/dwarf5-macho.test b/llvm/test/tools/dsymutil/ARM/dwarf5-macho.test --- a/llvm/test/tools/dsymutil/ARM/dwarf5-macho.test +++ b/llvm/test/tools/dsymutil/ARM/dwarf5-macho.test @@ -25,13 +25,13 @@ CHECK-NEXT: Abbrev table for offset: 0x00000000 CHECK: .debug_info contents: -CHECK-NEXT: 0x00000000: Compile Unit: length = 0x00000064, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08 +CHECK-NEXT: 0x00000000: Compile Unit: length = 0x00000064, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08 CHECK: DW_AT_ranges [DW_FORM_sec_offset] (0x[[RANGELIST_OFFSET:[0-9a-f]+]] CHECK-NEXT: [0x[[RANGELIST_OFFSET_START:[0-9a-f]+]], 0x[[RANGELIST_OFFSET_END:[0-9a-f]+]])) CHECK: 0x00000033: DW_TAG_subprogram [2] * (0x0000000c) CHECK-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x[[#%.16x,LOCLIST_LOWPC:]]) CHECK: 0x00000050: DW_TAG_formal_parameter [3] (0x00000033) -CHECK-NEXT: DW_AT_location [DW_FORM_sec_offset] (0x[[LOC_OFFSET:[0-9a-f]+]]: +CHECK-NEXT: DW_AT_location [DW_FORM_sec_offset] (0x[[LOC_OFFSET:[0-9a-f]+]]: CHECK-NEXT: [0x[[#%.16x,LOCLIST_PAIR_START:]], 0x[[#%.16x,LOCLIST_PAIR_END:]]): [[LOCLIST_EXPR:.*]] CHECK-NEXT: [0x[[#%.16x,LOCLIST_PAIR_START2:]], 0x[[#%.16x,LOCLIST_PAIR_END2:]]): [[LOCLIST_EXPR2:.*]]) diff --git a/llvm/test/tools/dsymutil/ARM/empty-map.test b/llvm/test/tools/dsymutil/ARM/empty-map.test --- a/llvm/test/tools/dsymutil/ARM/empty-map.test +++ b/llvm/test/tools/dsymutil/ARM/empty-map.test @@ -1,5 +1,7 @@ # RUN: dsymutil -f -oso-prepend-path=%p/../Inputs -y %s -o - 2>&1 | FileCheck %s +# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs -y %s -o - 2>&1 | FileCheck %s + --- triple: 'thumbv7-apple-darwin' ... diff --git a/llvm/test/tools/dsymutil/ARM/extern-alias.test b/llvm/test/tools/dsymutil/ARM/extern-alias.test --- a/llvm/test/tools/dsymutil/ARM/extern-alias.test +++ b/llvm/test/tools/dsymutil/ARM/extern-alias.test @@ -37,6 +37,10 @@ RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/private_extern/private_extern.out -o %t.dSYM --verbose | FileCheck %s RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/extern/extern.out -o %t.dSYM --verbose | FileCheck %s + +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/private_extern/private_extern.out -o %t.dSYM --verbose | FileCheck %s +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/extern/extern.out -o %t.dSYM --verbose | FileCheck %s + CHECK-NOT: could not find object file symbol for symbol _baz CHECK: { sym: _baz, objAddr: 0x0, binAddr: 0x100007F58, size: 0x0 } CHECK: { sym: _foo, objAddr: 0x0, binAddr: 0x100007F58, size: 0x20 } diff --git a/llvm/test/tools/dsymutil/ARM/fat-arch-name.test b/llvm/test/tools/dsymutil/ARM/fat-arch-name.test --- a/llvm/test/tools/dsymutil/ARM/fat-arch-name.test +++ b/llvm/test/tools/dsymutil/ARM/fat-arch-name.test @@ -1,5 +1,7 @@ # RUN: dsymutil -no-output %p/../Inputs/fat-test.arm.dylib -o /dev/null -verbose 2>&1 | FileCheck %s +# RUN: dsymutil --linker llvm -no-output %p/../Inputs/fat-test.arm.dylib -o /dev/null -verbose 2>&1 | FileCheck %s + # We detect thumb triples from the binaries, because those are the only ones # that are guaranteed to be able to generate a Target instance (for example # we would detect armv7m-apple-darwin as non-thumb triple, but you can't diff --git a/llvm/test/tools/dsymutil/ARM/fat-arch-not-found.test b/llvm/test/tools/dsymutil/ARM/fat-arch-not-found.test --- a/llvm/test/tools/dsymutil/ARM/fat-arch-not-found.test +++ b/llvm/test/tools/dsymutil/ARM/fat-arch-not-found.test @@ -1,4 +1,6 @@ # RUN: dsymutil -f -oso-prepend-path=%p/../Inputs -y %s -o - 2>&1 | FileCheck %s +# +# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs -y %s -o - 2>&1 | FileCheck %s --- triple: 'armv7-apple-darwin' diff --git a/llvm/test/tools/dsymutil/ARM/inlined-low_pc.c b/llvm/test/tools/dsymutil/ARM/inlined-low_pc.c --- a/llvm/test/tools/dsymutil/ARM/inlined-low_pc.c +++ b/llvm/test/tools/dsymutil/ARM/inlined-low_pc.c @@ -5,6 +5,10 @@ // RUN: dsymutil -f -y %p/dummy-debug-map-amr64.map -oso-prepend-path %p/../Inputs/inlined-low_pc -o - | llvm-dwarfdump - | FileCheck %s +// RUN: dsymutil --linker llvm -f -y %p/dummy-debug-map-amr64.map \ +// RUN: -oso-prepend-path %p/../Inputs/inlined-low_pc -o - | \ +// RUN: llvm-dwarfdump - | FileCheck %s + // CHECK: DW_TAG_subprogram // CHECK: DW_AT_low_pc{{.*}}0x0000000000010000 // CHECK: DW_AT_name{{.*}}"bar" diff --git a/llvm/test/tools/dsymutil/ARM/preload.test b/llvm/test/tools/dsymutil/ARM/preload.test --- a/llvm/test/tools/dsymutil/ARM/preload.test +++ b/llvm/test/tools/dsymutil/ARM/preload.test @@ -7,4 +7,9 @@ RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/preload/foo -o %t.dSYM RUN: llvm-nm %p/../Inputs/private/tmp/preload/foo | FileCheck %s RUN: llvm-nm %t.dSYM/Contents/Resources/DWARF/foo | FileCheck %s + +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/preload/foo -o %t.dSYM +RUN: llvm-nm %p/../Inputs/private/tmp/preload/foo | FileCheck %s +RUN: llvm-nm %t.dSYM/Contents/Resources/DWARF/foo | FileCheck %s + CHECK: start diff --git a/llvm/test/tools/dsymutil/ARM/scattered.c b/llvm/test/tools/dsymutil/ARM/scattered.c --- a/llvm/test/tools/dsymutil/ARM/scattered.c +++ b/llvm/test/tools/dsymutil/ARM/scattered.c @@ -1,4 +1,10 @@ -RUN: dsymutil -y %p/dummy-debug-map.map -oso-prepend-path %p/../Inputs/scattered-reloc/ -f -o - | llvm-dwarfdump -debug-info - | FileCheck %s +RUN: dsymutil -y %p/dummy-debug-map.map -oso-prepend-path \ +RUN: %p/../Inputs/scattered-reloc/ -f -o - | \ +RUN: llvm-dwarfdump -debug-info - | FileCheck %s + +RUN: dsymutil --linker llvm -y %p/dummy-debug-map.map \ +RUN: -oso-prepend-path %p/../Inputs/scattered-reloc/ -f -o - \ +RUN: | llvm-dwarfdump -debug-info - | FileCheck %s // See Inputs/scattered-reloc/scattered.s to see how this test // actually works. @@ -8,4 +14,4 @@ CHECK-NOT: DW_TAG CHECK: DW_AT_name{{.*}} "bar" CHECK - NOT : DW_TAG - CHECK : DW_AT_location{{.*}}(DW_OP_addr 0x10010) +CHECK : DW_AT_location{{.*}}(DW_OP_addr 0x10010) diff --git a/llvm/test/tools/dsymutil/ARM/thumb.c b/llvm/test/tools/dsymutil/ARM/thumb.c --- a/llvm/test/tools/dsymutil/ARM/thumb.c +++ b/llvm/test/tools/dsymutil/ARM/thumb.c @@ -1,6 +1,11 @@ // RUN: dsymutil -f -oso-prepend-path=%p/.. %p/../Inputs/thumb.armv7m -o - | llvm-dwarfdump - | FileCheck %s // RUN: dsymutil -arch armv7m -f -oso-prepend-path=%p/.. %p/../Inputs/thumb.armv7m -o - | llvm-dwarfdump - | FileCheck %s +// RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/.. \ +// RUN: %p/../Inputs/thumb.armv7m -o - | llvm-dwarfdump - | FileCheck %s +// RUN: dsymutil --linker llvm -arch armv7m -f -oso-prepend-path=%p/.. \ +// RUN: %p/../Inputs/thumb.armv7m -o - | llvm-dwarfdump - | FileCheck %s + /* Compile with: clang -c thumb.c -arch armv7m -g clang thumb.o -o thumb.armv7m -arch armv7m -nostdlib -static -Wl,-e,_start diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-bundle.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-bundle.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-bundle.test @@ -0,0 +1,41 @@ +RUN: rm -rf %t +RUN: mkdir -p %t/dsymdest +RUN: cat %p/../../Inputs/basic.macho.x86_64 > %t/basic.macho.x86_64 + +RUN: dsymutil --linker llvm -accelerator=Pub -oso-prepend-path=%p/../.. %t/basic.macho.x86_64 + +Check that the object file in the bundle exists and is sane: +RUN: llvm-dwarfdump -a %t/basic.macho.x86_64.dSYM/Contents/Resources/DWARF/basic.macho.x86_64 | FileCheck %S/basic-linking-x86.test + +Check that we don't create an empty Remarks directory if there are no remarks. +RUN: not ls %t/basic.macho.x86_64.dSYM/Contents/Resources/Remarks + +Check that llvm-dwarfdump -a recognizes the bundle as a dSYM: +RUN: llvm-dwarfdump -a %t/basic.macho.x86_64.dSYM | FileCheck %S/basic-linking-x86.test + +RUN: FileCheck %s --input-file %t/basic.macho.x86_64.dSYM/Contents/Info.plist + +RUN: dsymutil --linker llvm -oso-prepend-path=%p/../.. %t/basic.macho.x86_64 -o %t/dsymdest/basic.macho.x86_64.dSYM +RUN: llvm-dwarfdump -a %t/dsymdest/basic.macho.x86_64.dSYM/Contents/Resources/DWARF/basic.macho.x86_64 | FileCheck %S/basic-linking-x86.test +RUN: FileCheck %s --input-file %t/dsymdest/basic.macho.x86_64.dSYM/Contents/Info.plist + +CHECK: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: CFBundleDevelopmentRegion +CHECK-NEXT: English +CHECK-NEXT: CFBundleIdentifier +CHECK-NEXT: com.apple.xcode.dsym.basic.macho.x86_64 +CHECK-NEXT: CFBundleInfoDictionaryVersion +CHECK-NEXT: 6.0 +CHECK-NEXT: CFBundlePackageType +CHECK-NEXT: dSYM +CHECK-NEXT: CFBundleSignature +CHECK-NEXT: ???? +CHECK-NEXT: CFBundleShortVersionString +CHECK-NEXT: 1.0 +CHECK-NEXT: CFBundleVersion +CHECK-NEXT: 1 +CHECK-NEXT: +CHECK-NEXT: diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-x86.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-x86.test --- a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-x86.test +++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-x86.test @@ -1,4 +1,196 @@ RUN: cat %p/../../Inputs/basic.macho.x86_64 > %t1 -RUN: dsymutil --linker llvm --no-output -accelerator=Pub -f -oso-prepend-path=%p/../.. %t1 2>&1 | FileCheck %s --allow-empty +RUN: dsymutil --linker llvm -accelerator=Pub -f -oso-prepend-path=%p/../.. %t1 +RUN: llvm-dwarfdump --verify %t1.dwarf | FileCheck %s --check-prefix=VERIFY +RUN: llvm-dwarfdump -a %t1.dwarf | FileCheck %s -#CHECK: LLVM parallel dwarflinker is not implemented yet. +RUN: dsymutil --linker llvm -accelerator=Pub -f -o %t2 -oso-prepend-path=%p/../.. %p/../../Inputs/basic.macho.x86_64 +RUN: llvm-dwarfdump --verify %t2 | FileCheck %s --check-prefix=VERIFY +RUN: llvm-dwarfdump -a %t2 | FileCheck %s + +RUN: dsymutil -accelerator=Pub -f -o - -oso-prepend-path=%p/../.. %p/../../Inputs/basic.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,BASIC +RUN: dsymutil -accelerator=Pub -f -o - -oso-prepend-path=%p/../.. %p/../../Inputs/basic-archive.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,ARCHIVE +RUN: dsymutil -accelerator=Pub -dump-debug-map -oso-prepend-path=%p/../.. %p/../../Inputs/basic.macho.x86_64 | dsymutil -accelerator=Pub -f -y -o - - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,BASIC +RUN: dsymutil -accelerator=Pub -dump-debug-map -oso-prepend-path=%p/../.. %p/../../Inputs/basic-archive.macho.x86_64 | dsymutil -accelerator=Pub -f -o - -y - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,ARCHIVE + +VERIFY: No errors. + +CHECK: file format Mach-O 64-bit x86-64 + +CHECK: debug_info contents + +CHECK: Compile Unit: + +CHECK: DW_TAG_compile_unit +CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)") +CHECK: DW_AT_language (DW_LANG_C99) +CHECK: DW_AT_name ("basic1.c") +CHECK: DW_AT_stmt_list (0x00000000) +CHECK: DW_AT_comp_dir ("/Inputs") +CHECK: DW_AT_low_pc (0x0000000100000ea0) +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("main") +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") +CHECK: DW_AT_decl_line (23) +CHECK: DW_AT_prototyped (0x01) +CHECK: DW_AT_type (0x00000063 +CHECK: DW_AT_external (0x01) +CHECK: DW_AT_accessibility (DW_ACCESS_public) +CHECK: DW_AT_low_pc (0x0000000100000ea0) +CHECK: DW_AT_high_pc (0x0000000100000ec4) +CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) +CHECK: DW_TAG_formal_parameter +CHECK: DW_AT_name ("argc") +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") +CHECK: DW_AT_decl_line (23) +CHECK: DW_AT_type (0x00000063 +CHECK: DW_AT_location (DW_OP_fbreg -8) +CHECK: DW_TAG_formal_parameter +CHECK: DW_AT_name ("argv") +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") +CHECK: DW_AT_decl_line (23) +CHECK: DW_AT_type (0x0000006a +CHECK: DW_AT_location (DW_OP_fbreg -16) +CHECK: NULL +CHECK: DW_TAG_base_type +CHECK: DW_AT_name ("int") +CHECK: DW_AT_encoding (DW_ATE_signed) +CHECK: DW_AT_byte_size (0x04) +CHECK: DW_TAG_pointer_type +CHECK: DW_AT_type (0x0000006f +CHECK: DW_TAG_pointer_type +CHECK: DW_AT_type (0x00000074 +CHECK: DW_TAG_const_type +CHECK: DW_AT_type (0x00000079 +CHECK: DW_TAG_base_type +CHECK: DW_AT_name ("char") +CHECK: DW_AT_encoding (DW_ATE_signed_char) +CHECK: DW_AT_byte_size (0x01) +CHECK: NULL + +CHECK: Compile Unit: + +CHECK: DW_TAG_compile_unit +CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)") +CHECK: DW_AT_name ("basic2.c") +CHECK: DW_AT_stmt_list (0x0000003f) +CHECK: DW_AT_comp_dir ("/Inputs") +CHECK: DW_AT_low_pc (0x0000000100000ed0) +CHECK: DW_TAG_base_type +CHECK: DW_AT_name ("int") +CHECK: DW_TAG_variable +CHECK: DW_AT_name ("private_int") +CHECK: DW_AT_type (0x000000a7 +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c") +BASIC: DW_AT_location (DW_OP_addr 0x100001008) +ARCHIVE: DW_AT_location (DW_OP_addr 0x100001004) +CHECK: DW_TAG_variable +CHECK: DW_AT_name ("baz") +CHECK: DW_AT_type (0x000000a7 +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c") +CHECK: DW_AT_location (DW_OP_addr 0x100001000) +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("foo") +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c") +CHECK: DW_AT_type (0x000000a7 +CHECK: DW_AT_low_pc (0x0000000100000ed0) +CHECK: DW_AT_high_pc (0x0000000100000f19) +CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) +CHECK: DW_TAG_formal_parameter +CHECK: DW_AT_name ("arg") +CHECK: DW_AT_type (0x000000a7 +CHECK: DW_AT_location (DW_OP_fbreg -4) +CHECK: NULL +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("inc") +CHECK: DW_AT_type (0x000000a7 +CHECK: DW_AT_low_pc (0x0000000100000f20) +CHECK: DW_AT_high_pc (0x0000000100000f37) +CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) +CHECK: NULL + +CHECK: Compile Unit: + +CHECK: DW_TAG_compile_unit +CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)") +CHECK: DW_AT_name ("basic3.c") +CHECK: DW_AT_stmt_list (0x00000093) +CHECK: DW_AT_comp_dir ("/Inputs") +CHECK: DW_AT_low_pc (0x0000000100000f40) +CHECK: DW_TAG_variable +CHECK: DW_AT_name ("val") +CHECK: DW_AT_type (0x00000162 +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic3.c") +BASIC: DW_AT_location (DW_OP_addr 0x100001004) +ARCHIVE: DW_AT_location (DW_OP_addr 0x100001008) +CHECK: DW_TAG_volatile_type +CHECK: DW_AT_type (0x00000167 +CHECK: DW_TAG_base_type +CHECK: DW_AT_name ("int") +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("bar") +CHECK: DW_AT_type (0x00000167 +CHECK: DW_AT_low_pc (0x0000000100000f40) +CHECK: DW_AT_high_pc (0x0000000100000f84) +CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) +CHECK: DW_TAG_formal_parameter +CHECK: DW_AT_name ("arg") +CHECK: DW_AT_type (0x00000167 +CHECK: DW_AT_location (DW_OP_fbreg -8) +CHECK: NULL +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("inc") +CHECK: DW_AT_type (0x00000167 +CHECK: DW_AT_low_pc (0x0000000100000f90) +CHECK: DW_AT_high_pc (0x0000000100000fa9) +CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) + +CHECK: NULL + +CHECK-NOT: .debug_loc contents + +CHECK:.debug_aranges contents: +CHECK-NEXT:Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000000, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT:[0x0000000100000ea0, 0x0000000100000ec4) +CHECK-NEXT:Address Range Header: length = 0x0000003c, format = DWARF32, version = 0x0002, cu_offset = 0x00000081, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT:[0x0000000100000ed0, 0x0000000100000f19) +CHECK-NEXT:[0x0000000100000f20, 0x0000000100000f37) +CHECK-NEXT:Address Range Header: length = 0x0000003c, format = DWARF32, version = 0x0002, cu_offset = 0x00000126, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT:[0x0000000100000f40, 0x0000000100000f84) +CHECK-NEXT:[0x0000000100000f90, 0x0000000100000fa9) + +CHECK: .debug_line contents: +CHECK: file_names[ 1]: +CHECK-NEXT: name: "basic1.c" +CHECK-NEXT: dir_index: 0 +CHECK: Address Line Column File ISA Discriminator Flags +CHECK-NEXT: ------------------ ------ ------ ------ --- ------------- ------------- +CHECK-NEXT: 0x0000000100000ea0 23 0 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000eb6 24 0 1 0 0 is_stmt prologue_end +CHECK-NEXT: 0x0000000100000ec4 24 0 1 0 0 is_stmt end_sequence + +CHECK: file_names[ 1]: +CHECK-NEXT: name: "basic2.c" +CHECK-NEXT: dir_index: 0 +CHECK: Address Line Column File ISA Discriminator Flags +CHECK-NEXT: ------------------ ------ ------ ------ --- ------------- ------------- +CHECK-NEXT: 0x0000000100000ed0 19 0 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000ee2 20 0 1 0 0 is_stmt prologue_end +CHECK-NEXT: 0x0000000100000f19 20 0 1 0 0 is_stmt end_sequence +CHECK-NEXT: 0x0000000100000f20 14 0 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000f24 15 0 1 0 0 is_stmt prologue_end +CHECK-NEXT: 0x0000000100000f37 15 0 1 0 0 is_stmt end_sequence + +CHECK: file_names[ 1]: +CHECK-NEXT: name: "basic3.c" +CHECK-NEXT: dir_index: 0 +CHECK: Address Line Column File ISA Discriminator Flags +CHECK-NEXT: ------------------ ------ ------ ------ --- ------------- ------------- +CHECK-NEXT: 0x0000000100000f40 16 0 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000f4b 17 0 1 0 0 is_stmt prologue_end +CHECK-NEXT: 0x0000000100000f58 18 0 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000f6c 19 0 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000f7b 20 0 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000f84 20 0 1 0 0 is_stmt end_sequence +CHECK-NEXT: 0x0000000100000f90 11 0 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000f9b 12 0 1 0 0 is_stmt prologue_end +CHECK-NEXT: 0x0000000100000fa9 12 0 1 0 0 is_stmt end_sequence diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-lto-dw4-linking-x86.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-lto-dw4-linking-x86.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-lto-dw4-linking-x86.test @@ -0,0 +1,185 @@ +RUN: cat %p/../../Inputs/basic-lto-dw4.macho.x86_64 > %t1 +RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../.. %t1 +RUN: llvm-dwarfdump --verify %t1.dwarf | FileCheck %s --check-prefix=VERIFY +RUN: llvm-dwarfdump -a %t1.dwarf | FileCheck %s + +VERIFY: No errors. + +CHECK: file format Mach-O 64-bit x86-64 + +CHECK: debug_info contents + +CHECK: Compile Unit: {{.*}} version = 0x0004 +CHECK: DW_TAG_compile_unit +CHECK: DW_AT_producer ("clang version 3.7.0 ") +CHECK: DW_AT_language (DW_LANG_C99) +CHECK: DW_AT_name ("basic1.c") +CHECK: DW_AT_stmt_list (0x00000000) +CHECK: DW_AT_comp_dir ("/Inputs") +CHECK: DW_AT_low_pc (0x0000000100000f40) +CHECK: DW_AT_high_pc (0x0000000100000f4b) +CHECK: DW_TAG_subprogram +CHECK: DW_AT_low_pc (0x0000000100000f40) +CHECK: DW_AT_high_pc (0x0000000100000f4b) +CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) +CHECK: DW_AT_name ("main") +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") +CHECK: DW_AT_prototyped (true) +CHECK: DW_AT_type (0x00000000000000a1 +CHECK: DW_AT_external (true) +CHECK: DW_TAG_formal_parameter +CHECK: DW_AT_location (DW_OP_reg5 RDI, DW_OP_piece 0x4) +CHECK: DW_AT_name ("argc") +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") +CHECK: DW_AT_type (0x00000000000000a1 +CHECK: DW_TAG_formal_parameter +CHECK: DW_AT_location (DW_OP_reg4 RSI) +CHECK: DW_AT_name ("argv") +CHECK: DW_AT_type (0x00000060 +CHECK: NULL +CHECK: DW_TAG_pointer_type +CHECK: DW_AT_type (0x00000065 +CHECK: DW_TAG_pointer_type +CHECK: DW_TAG_const_type +CHECK: DW_TAG_base_type +CHECK: DW_AT_name ("char") +CHECK: DW_AT_encoding (DW_ATE_signed_char) +CHECK: DW_AT_byte_size (0x01) +CHECK: NULL + +CHECK: Compile Unit:{{.*}} version = 0x0004 + +CHECK: DW_TAG_compile_unit +CHECK: DW_AT_producer ("clang version 3.7.0 ") +CHECK: DW_AT_language (DW_LANG_C99) +CHECK: DW_AT_name ("basic2.c") +CHECK: DW_AT_stmt_list (0x00000044) +CHECK: DW_AT_low_pc (0x0000000100000f50) +CHECK: DW_AT_high_pc (0x0000000100000f87) +CHECK: DW_TAG_base_type +CHECK: DW_AT_name ("int") +CHECK: DW_TAG_variable +CHECK: DW_AT_name ("baz") +CHECK: DW_AT_location (DW_OP_addr 0x100001000) +CHECK: DW_TAG_variable +CHECK: DW_AT_name ("private_int") +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c") +CHECK: DW_AT_location (DW_OP_addr 0x100001008) +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("inc") +CHECK: DW_AT_type (0x000000a1 +CHECK: DW_AT_inline (DW_INL_inlined) +CHECK: DW_TAG_subprogram +CHECK: DW_AT_low_pc (0x0000000100000f50) +CHECK: DW_AT_high_pc (0x0000000100000f87) +CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) +CHECK: DW_AT_name ("foo") +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c") +CHECK: DW_AT_prototyped (true) +CHECK: DW_AT_type (0x000000a1 +CHECK: DW_TAG_formal_parameter +CHECK: DW_AT_location (0x00000000 +CHECK: [0x0000000100000f50, 0x0000000100000f5c): DW_OP_reg5 RDI, DW_OP_piece 0x4) +CHECK: DW_AT_name ("arg") +CHECK: DW_AT_type (0x000000a1 +CHECK: DW_TAG_inlined_subroutine +CHECK: DW_AT_abstract_origin (0x000000d2 "inc") +CHECK: DW_AT_low_pc (0x0000000100000f61) +CHECK: DW_AT_high_pc (0x0000000100000f70) +CHECK: NULL +CHECK: NULL + +CHECK: Compile Unit: {{.*}} version = 0x0004 + +CHECK: DW_TAG_compile_unit +CHECK: DW_AT_producer ("clang version 3.7.0 ") +CHECK: DW_AT_name ("basic3.c") +CHECK: DW_AT_stmt_list (0x0000009a) +CHECK: DW_AT_low_pc (0x0000000100000f90) +CHECK: DW_AT_high_pc (0x0000000100000fb4) +CHECK: DW_TAG_variable +CHECK: DW_AT_name ("val") +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic3.c") +CHECK: DW_AT_location (DW_OP_addr 0x100001004) +CHECK: DW_TAG_volatile_type +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("inc") +CHECK: DW_AT_inline (DW_INL_inlined) +CHECK: DW_TAG_subprogram +CHECK: DW_AT_low_pc (0x0000000100000f90) +CHECK: DW_AT_high_pc (0x0000000100000fb4) +CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) +CHECK: DW_AT_name ("bar") +CHECK: DW_TAG_formal_parameter +CHECK: DW_AT_location (0x00000025 +CHECK: [0x0000000100000f90, 0x0000000100000f9f): DW_OP_reg5 RDI, DW_OP_piece 0x4 +CHECK: [0x0000000100000fa9, 0x0000000100000fad): DW_OP_reg5 RDI, DW_OP_piece 0x4) +CHECK: DW_AT_name ("arg") +CHECK: DW_TAG_inlined_subroutine +CHECK: DW_AT_abstract_origin (0x0000015f "inc") +CHECK: DW_AT_ranges (0x00000000 +CHECK: [0x0000000100000f94, 0x0000000100000f9a) +CHECK: [0x0000000100000f9f, 0x0000000100000fa7)) + +CHECK: NULL +CHECK: NULL + + +CHECK: .debug_loc contents: +CHECK-NEXT: 0x00000000: +CHECK-NEXT: (0x0000000000000000, 0x000000000000000c): DW_OP_reg5 RDI, DW_OP_piece 0x4 +CHECK-NOT: : +CHECK: 0x00000025: +CHECK-NEXT: (0x0000000000000000, 0x000000000000000f): DW_OP_reg5 RDI, DW_OP_piece 0x4 +CHECK-NEXT: (0x0000000000000019, 0x000000000000001d): DW_OP_reg5 RDI, DW_OP_piece 0x4 + + +CHECK: .debug_aranges contents: +CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000000, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT: [0x0000000100000f40, 0x0000000100000f4b) +CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000077, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT: [0x0000000100000f50, 0x0000000100000f87) +CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x0000011b, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT: [0x0000000100000f90, 0x0000000100000fb4) + +CHECK: .debug_line contents: +CHECK: file_names[ 1]: +CHECK-NEXT: name: "basic1.c" +CHECK-NEXT: dir_index: 0 +CHECK: Address Line Column File ISA Discriminator Flags +CHECK-NEXT: ------------------ ------ ------ ------ --- ------------- ------------- +CHECK-NEXT: 0x0000000100000f40 26 0 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000f44 27 10 1 0 0 is_stmt prologue_end +CHECK-NEXT: 0x0000000100000f49 27 3 1 0 0 +CHECK-NEXT: 0x0000000100000f4b 27 3 1 0 0 end_sequence + +CHECK: file_names[ 1]: +CHECK-NEXT: name: "basic2.c" +CHECK-NEXT: dir_index: 0 +CHECK: Address Line Column File ISA Discriminator Flags +CHECK-NEXT: ------------------ ------ ------ ------ --- ------------- ------------- +CHECK-NEXT: 0x0000000100000f50 19 0 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000f54 20 18 1 0 0 is_stmt prologue_end +CHECK-NEXT: 0x0000000100000f5a 20 17 1 0 0 +CHECK-NEXT: 0x0000000100000f5c 20 10 1 0 0 +CHECK-NEXT: 0x0000000100000f61 15 10 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000f70 20 23 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000f74 20 36 1 0 0 +CHECK-NEXT: 0x0000000100000f83 20 31 1 0 0 +CHECK-NEXT: 0x0000000100000f85 20 3 1 0 0 +CHECK-NEXT: 0x0000000100000f87 20 3 1 0 0 end_sequence + +CHECK: file_names[ 1]: +CHECK-NEXT: name: "basic3.c" +CHECK-NEXT: dir_index: 0 +CHECK: Address Line Column File ISA Discriminator Flags +CHECK-NEXT: ------------------ ------ ------ ------ --- ------------- ------------- +CHECK-NEXT: 0x0000000100000f90 16 0 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000f94 12 10 1 0 0 is_stmt prologue_end +CHECK-NEXT: 0x0000000100000f9a 17 7 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000f9f 12 10 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000fa7 20 1 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000fa9 19 18 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000fab 19 10 1 0 0 +CHECK-NEXT: 0x0000000100000fb2 20 1 1 0 0 is_stmt +CHECK-NEXT: 0x0000000100000fb4 20 1 1 0 0 is_stmt end_sequence diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-lto-linking-x86.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-lto-linking-x86.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-lto-linking-x86.test @@ -0,0 +1,185 @@ +RUN: cat %p/../../Inputs/basic-lto.macho.x86_64 > %t1 +RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../.. %t1 +RUN: llvm-dwarfdump --verify %t1.dwarf | FileCheck %s --check-prefix=VERIFY +RUN: llvm-dwarfdump -a %t1.dwarf | FileCheck %s + +RUN: dsymutil --linker llvm -oso-prepend-path=%p/../.. -dump-debug-map %t1 | dsymutil -f -o - -y - | llvm-dwarfdump -a - | FileCheck %s + +VERIFY: No errors. + +CHECK: file format Mach-O 64-bit x86-64 + +CHECK: debug_info contents + +CHECK: Compile Unit: + +CHECK: DW_TAG_compile_unit +CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)") +CHECK: DW_AT_language (DW_LANG_C99) +CHECK: DW_AT_name ("basic1.c") +CHECK: DW_AT_stmt_list (0x00000000) +CHECK: DW_AT_comp_dir ("/Inputs") +CHECK: DW_AT_low_pc (0x0000000100000f40) +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("main") +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c") +CHECK: DW_AT_decl_line (23) +CHECK: DW_AT_prototyped (0x01) +CHECK: DW_AT_type (0x00000063 +CHECK: DW_AT_external (0x01) +CHECK: DW_AT_accessibility (DW_ACCESS_public) +CHECK: DW_AT_low_pc (0x0000000100000f40) +CHECK: DW_AT_high_pc (0x0000000100000f4b) +CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) +CHECK: DW_TAG_formal_parameter +CHECK: DW_AT_name ("argc") +CHECK: DW_AT_type (0x00000063 +CHECK: DW_AT_location (DW_OP_reg5 RDI, DW_OP_piece 0x4) +CHECK: DW_TAG_formal_parameter +CHECK: DW_AT_name ("argv") +CHECK: DW_AT_type (0x0000006a +CHECK: DW_AT_location (DW_OP_reg4 RSI) +CHECK: NULL +CHECK: DW_TAG_base_type +CHECK: DW_AT_name ("int") +CHECK: DW_AT_encoding (DW_ATE_signed) +CHECK: DW_AT_byte_size (0x04) +CHECK: DW_TAG_pointer_type +CHECK: DW_AT_type (0x0000006f +CHECK: DW_TAG_pointer_type +CHECK: DW_AT_type (0x00000074 +CHECK: DW_TAG_const_type +CHECK: DW_AT_type (0x00000079 +CHECK: DW_TAG_base_type +CHECK: DW_AT_name ("char") +CHECK: DW_AT_encoding (DW_ATE_signed_char) +CHECK: DW_AT_byte_size (0x01) +CHECK: NULL + +CHECK: Compile Unit: + +CHECK: DW_TAG_compile_unit +CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)") +CHECK: DW_AT_name ("basic2.c") +CHECK: DW_AT_stmt_list (0x0000003e) +CHECK: DW_AT_comp_dir ("/Inputs") +CHECK: DW_AT_low_pc (0x0000000100000f50) +CHECK: DW_TAG_variable +CHECK: DW_AT_name ("private_int") +CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c") +CHECK: DW_AT_location (DW_OP_addr 0x100001008) +CHECK: DW_TAG_variable +CHECK: DW_AT_name ("baz") +CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_location (DW_OP_addr 0x100001000) +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("foo") +CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_low_pc (0x0000000100000f50) +CHECK: DW_AT_high_pc (0x0000000100000f89) +CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) +CHECK: DW_TAG_formal_parameter +CHECK: DW_AT_name ("arg") +CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_location (0x00000000 +CHECK: [0x0000000100000f50, 0x0000000100000f5e): DW_OP_reg5 RDI, DW_OP_piece 0x4) +CHECK:[[INC1:0x[0-9a-f]*]]{{.*}}DW_TAG_inlined_subroutine +CHECK: DW_AT_abstract_origin (0x00000128 "inc") +CHECK: DW_AT_low_pc (0x0000000100000f63) +CHECK: DW_AT_high_pc (0x0000000100000f72) +CHECK: DW_AT_call_line (20) +CHECK: NULL +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("inc") +CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_inline (DW_INL_inlined) +CHECK: NULL + +CHECK: Compile Unit: + +CHECK: DW_TAG_compile_unit +CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)") +CHECK: DW_AT_name ("basic3.c") +CHECK: DW_AT_stmt_list (0x0000007e) +CHECK: DW_AT_comp_dir ("/Inputs") +CHECK: DW_AT_low_pc (0x0000000100000f90) +CHECK: DW_TAG_variable +CHECK: DW_AT_name ("val") +CHECK: DW_AT_type (0x00000176 +CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic3.c") +CHECK: DW_AT_location (DW_OP_addr 0x100001004) +CHECK: DW_TAG_volatile_type +CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("bar") +CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_low_pc (0x0000000100000f90) +CHECK: DW_AT_high_pc (0x0000000100000fb4) +CHECK: DW_AT_frame_base (DW_OP_reg6 RBP) +CHECK: DW_TAG_formal_parameter +CHECK: DW_AT_name ("arg") +CHECK: DW_AT_type (0x0000000000000063 +CHECK: DW_AT_location (0x00000025 +CHECK: [0x0000000100000f90, 0x0000000100000f9f): DW_OP_reg5 RDI, DW_OP_piece 0x4 +CHECK: [0x0000000100000fa9, 0x0000000100000fad): DW_OP_reg5 RDI, DW_OP_piece 0x4) +CHECK: DW_TAG_lexical_block +CHECK: DW_AT_low_pc (0x0000000100000f94) +CHECK: DW_AT_high_pc (0x0000000100000fa7) +CHECK:[[INC2:0x[0-9a-f]*]]{{.*}}DW_TAG_inlined_subroutine +CHECK: DW_AT_abstract_origin (0x000001d4 "inc") +CHECK: DW_AT_ranges (0x00000000 +CHECK: [0x0000000100000f94, 0x0000000100000f9a) +CHECK: [0x0000000100000f9f, 0x0000000100000fa7)) +CHECK: NULL +CHECK: NULL +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("inc") +CHECK: DW_AT_type (0x0000000000000063 +CHECK: NULL + +CHECK: .debug_loc contents: +CHECK-NEXT: 0x00000000: +CHECK-NEXT: (0x0000000000000000, 0x000000000000000e): DW_OP_reg5 RDI, DW_OP_piece 0x4 +CHECK-NOT: : +CHECK: 0x00000025: +CHECK-NEXT: (0x0000000000000000, 0x000000000000000f): DW_OP_reg5 RDI, DW_OP_piece 0x4 +CHECK-NEXT: (0x0000000000000019, 0x000000000000001d): DW_OP_reg5 RDI, DW_OP_piece 0x4 + +CHECK: .debug_aranges contents: +CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000000, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT: [0x0000000100000f40, 0x0000000100000f4b) +CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000081, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT: [0x0000000100000f50, 0x0000000100000f89) +CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x0000013a, addr_size = 0x08, seg_size = 0x00 +CHECK-NEXT: [0x0000000100000f90, 0x0000000100000fb4) + + +CHECK: .debug_line contents +CHECK: file_names[ 1]: +CHECK-NEXT: name: "basic1.c" +CHECK-NEXT: dir_index: 0 +CHECK: 0x0000000100000f40 23 0 1 0 0 is_stmt +CHECK: 0x0000000100000f44 24 0 1 0 0 is_stmt prologue_end +CHECK: 0x0000000100000f4b 24 0 1 0 0 is_stmt end_sequence + +CHECK: file_names[ 1]: +CHECK-NEXT: name: "basic2.c" +CHECK-NEXT: dir_index: 0 +CHECK: 0x0000000100000f50 19 0 1 0 0 is_stmt +CHECK: 0x0000000100000f54 20 0 1 0 0 is_stmt prologue_end +CHECK: 0x0000000100000f63 15 0 1 0 0 is_stmt +CHECK: 0x0000000100000f72 20 0 1 0 0 is_stmt +CHECK: 0x0000000100000f89 20 0 1 0 0 is_stmt end_sequence + +CHECK: file_names[ 1]: +CHECK-NEXT: name: "basic3.c" +CHECK-NEXT: dir_index: 0 +CHECK: 0x0000000100000f90 16 0 1 0 0 is_stmt +CHECK: 0x0000000100000f94 12 0 1 0 0 is_stmt prologue_end +CHECK: 0x0000000100000f9a 17 0 1 0 0 is_stmt +CHECK: 0x0000000100000f9f 12 0 1 0 0 is_stmt +CHECK: 0x0000000100000fa7 20 0 1 0 0 is_stmt +CHECK: 0x0000000100000fa9 19 0 1 0 0 is_stmt +CHECK: 0x0000000100000fb2 20 0 1 0 0 is_stmt +CHECK: 0x0000000100000fb4 20 0 1 0 0 is_stmt end_sequence diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-with-libfat-test.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-with-libfat-test.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-with-libfat-test.test @@ -0,0 +1,12 @@ +RUN: cat %p/../../Inputs/basic-with-libfat-test.macho.x86_64 > %t1 +RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../.. %t1 +RUN: llvm-dwarfdump %t1.dwarf | FileCheck %s + +The test binary was created by force-linking the libfat-test.a fat archive +with the basic linking test archive, like so: +$ clang -all_load libfat-test.a libbasic.a basic1.macho.x86_64.o -Wl,-dead_strip -u _x86_64_var + +CHECK: DW_AT_name{{.*}}"x86_64_var" +CHECK: DW_AT_name{{.*}}"basic2.c" +CHECK: DW_AT_name{{.*}}"basic3.c" +CHECK: DW_AT_name{{.*}}"basic1.c" diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/empty_range.s b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/empty_range.s new file mode 100644 --- /dev/null +++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/empty_range.s @@ -0,0 +1,53 @@ +# This test verifies that an empty range list in the .debug_ranges section +# doesn't crash dsymutil. As clang does not produce this kind of debug +# info anymore, we used this hand-crafted assembly file to produce a testcase +# Compile with: +# llvm-mc -triple x86_64-apple-darwin -filetype=obj -o 1.o empty_range.o + +# RUN: dsymutil --linker llvm -f -y %p/../dummy-debug-map.map -oso-prepend-path %p/../../Inputs/empty_range -o - | llvm-dwarfdump -debug-info - | FileCheck %s + + .section __TEXT,__text,regular,pure_instructions + .macosx_version_min 10, 11 + .globl __Z3foov + .align 4, 0x90 +__Z3foov: ## @_Z3foov +Lfunc_begin0: + pushq %rbp + movq %rsp, %rbp + popq %rbp + retq +Lfunc_end0: + .section __DWARF,__debug_abbrev,regular,debug +Lsection_abbrev: + .byte 1 ## Abbreviation Code + .byte 17 ## DW_TAG_compile_unit + .byte 1 ## DW_CHILDREN_yes + .byte 0 ## EOM(1) + .byte 0 ## EOM(2) + .byte 2 ## Abbreviation Code + .byte 46 ## DW_TAG_subprogram + .byte 0 ## DW_CHILDREN_no + .byte 17 ## DW_AT_low_pc + .byte 1 ## DW_FORM_addr + .byte 0x55 ## DW_AT_ranges + .byte 6 ## DW_FORM_data4 + .byte 0 ## EOM(1) + .byte 0 ## EOM(2) + .byte 0 ## EOM(3) + .section __DWARF,__debug_info,regular,debug +Lsection_info: + .long 22 ## Length of Unit + .short 2 ## DWARF version number + .long 0 ## Offset Into Abbrev. Section + .byte 8 ## Address Size (in bytes) + .byte 1 ## Abbrev [1] DW_TAG_compile_unit + .byte 2 ## Abbrev [2] DW_TAG_subprogram + .quad Lfunc_begin0 ## DW_AT_low_pc + .long 0 ## DW_AT_ranges (pointing at an empty entry) + .byte 0 ## End Of Children Mark + .section __DWARF,__debug_ranges,regular,debug +Ldebug_range: + .long 0 + .long 0 + +# CHECK-NOT: DW_TAG_compile_unit diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/frame-1.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/frame-1.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/frame-1.test @@ -0,0 +1,36 @@ +# RUN: rm -rf %t +# RUN: mkdir -p %t +# RUN: llc -filetype=obj %p/../../Inputs/frame-dw2.ll -o %t/frame-dw2.o +# RUN: dsymutil --linker llvm -f -oso-prepend-path=%t -y %s -o - | \ +# RUN: llvm-dwarfdump -debug-frame - | FileCheck %s + +# This test is meant to verify that identical CIEs will get reused +# in the same file but not inbetween files. For this to happen, we +# link twice the same file using this made-up debug map: + +--- +triple: 'i386-apple-darwin' +objects: + - filename: frame-dw2.o + symbols: + - { sym: _bar, objAddr: 0x0, binAddr: 0x1000, size: 0x12 } + - { sym: _baz, objAddr: 0x0, binAddr: 0x2000, size: 0x12 } + - filename: frame-dw2.o + symbols: + - { sym: _bar, objAddr: 0x0, binAddr: 0x3000, size: 0x12 } + - { sym: _baz, objAddr: 0x0, binAddr: 0x4000, size: 0x12 } +... + +# CHECK: .debug_frame contents: +# CHECK: 00000000 {{[0-9a-f]*}} ffffffff CIE +# CHECK-NOT: FDE +# CHECK: FDE cie=00000000 pc=00001000...00001 +# CHECK-NOT: FDE +# CHECK: FDE cie=00000000 pc=00002000...00002 +# CHECK: [[CIECU2:[0-9a-f]*]] {{[0-9a-f]*}} ffffffff CIE +# CHECK-NOT: FDE +# CHECK: FDE cie=[[CIECU2]] pc=00003000...00003 +# CHECK-NOT: FDE +# CHECK: FDE cie=[[CIECU2]] pc=00004000...00004 +# CHECK-NOT: FDE +# CHECK: .eh_frame contents: diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/frame-2.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/frame-2.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/frame-2.test @@ -0,0 +1,46 @@ +# RUN: rm -rf %t +# RUN: mkdir -p %t +# RUN: llc -filetype=obj %p/../../Inputs/frame-dw2.ll -o %t/frame-dw2.o +# RUN: llc -filetype=obj %p/../../Inputs/frame-dw4.ll -o %t/frame-dw4.o +# RUN: dsymutil --linker llvm -f -oso-prepend-path=%t -y %s -o - | \ +# RUN: llvm-dwarfdump -debug-frame - | FileCheck %s + +# Check the handling of multiple different CIEs. To have CIEs that +# appear to be different, use a dwarf2 version of the file along with +# a dwarf 4 version. The CIE header version (and layout) will be different. +--- +triple: 'i386-apple-darwin' +objects: + - filename: frame-dw2.o + symbols: + - { sym: _bar, objAddr: 0x0, binAddr: 0x1000, size: 0x12 } + - { sym: _baz, objAddr: 0x0, binAddr: 0x2000, size: 0x12 } + - filename: frame-dw4.o + symbols: + - { sym: _baz, objAddr: 0x0, binAddr: 0x3000, size: 0x12 } + - filename: frame-dw2.o + symbols: + - { sym: _bar, objAddr: 0x0, binAddr: 0x4000, size: 0x12 } +... + +# CHECK: .debug_frame contents: +# CHECK: 00000000 {{[0-9a-f]*}} ffffffff CIE +# CHECK-NEXT: Format: DWARF32 +# CHECK-NEXT: Version:{{.*}}1 +# CHECK-NOT: FDE +# CHECK: FDE cie=00000000 pc=00001000...00001 +# CHECK-NOT: FDE +# CHECK: FDE cie=00000000 pc=00002000...00002 +# CHECK-NOT: FDE +# CHECK: [[CIEDW4:[0-9a-f]*]] 00000010 ffffffff CIE +# CHECK-NEXT: Format: DWARF32 +# CHECK-NEXT: Version:{{.*}}4 +# CHECK-NOT: FDE +# CHECK: FDE cie=[[CIEDW4]] pc=00003000...00003 +# CHECK: [[CIEDW1_2:[0-9a-f]*]] 00000010 ffffffff CIE +# CHECK-NEXT: Format: DWARF32 +# CHECK-NEXT: Version:{{.*}}1 +# CHECK-NOT: FDE +# CHECK: FDE cie=[[CIEDW1_2]] pc=00004000...00004 +# CHECK-NOT: FDE +# CHECK: .eh_frame contents: diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/multiple-inputs.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/multiple-inputs.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/multiple-inputs.test @@ -0,0 +1,31 @@ +RUN: rm -rf %t +RUN: mkdir -p %t + +RUN: cat %p/../../Inputs/basic.macho.x86_64 > %t/basic.macho.x86_64 +RUN: cat %p/../../Inputs/basic-archive.macho.x86_64 > %t/basic-archive.macho.x86_64 +RUN: cat %p/../../Inputs/basic-lto.macho.x86_64 > %t/basic-lto.macho.x86_64 +RUN: cat %p/../../Inputs/basic-lto-dw4.macho.x86_64 > %t/basic-lto-dw4.macho.x86_64 + +# Multiple inputs in flat mode +RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../.. %t/basic.macho.x86_64 %t/basic-archive.macho.x86_64 %t/basic-lto.macho.x86_64 %t/basic-lto-dw4.macho.x86_64 +RUN: llvm-dwarfdump -a %t/basic.macho.x86_64.dwarf \ +RUN: | FileCheck %p/basic-linking-x86.test --check-prefixes=CHECK,BASIC +RUN: llvm-dwarfdump -a %t/basic-archive.macho.x86_64.dwarf \ +RUN: | FileCheck %p/basic-linking-x86.test --check-prefixes=CHECK,ARCHIVE +RUN: llvm-dwarfdump -a %t/basic-lto.macho.x86_64.dwarf | FileCheck %S/basic-lto-linking-x86.test +RUN: llvm-dwarfdump -a %t/basic-lto-dw4.macho.x86_64.dwarf | FileCheck %S/basic-lto-dw4-linking-x86.test + +# Multiple inputs that end up in the same named bundle +RUN: dsymutil --linker llvm -oso-prepend-path=%p/../.. %t/basic.macho.x86_64 %t/basic-archive.macho.x86_64 %t/basic-lto.macho.x86_64 %t/basic-lto-dw4.macho.x86_64 -o %t.dSYM +RUN: llvm-dwarfdump -a %t.dSYM/Contents/Resources/DWARF/basic.macho.x86_64 \ +RUN: | FileCheck %p/basic-linking-x86.test --check-prefixes=CHECK,BASIC +RUN: llvm-dwarfdump -a %t.dSYM/Contents/Resources/DWARF/basic-archive.macho.x86_64 \ +RUN: | FileCheck %p/basic-linking-x86.test --check-prefixes=CHECK,ARCHIVE +RUN: llvm-dwarfdump -a %t.dSYM/Contents/Resources/DWARF/basic-lto.macho.x86_64 | FileCheck %S/basic-lto-linking-x86.test +RUN: llvm-dwarfdump -a %t.dSYM/Contents/Resources/DWARF/basic-lto-dw4.macho.x86_64 | FileCheck %S/basic-lto-dw4-linking-x86.test + + +# Multiple inputs in a named bundle in flat mode... impossible. +RUN: not dsymutil --linker llvm -f -oso-prepend-path=%p/../.. %t/basic.macho.x86_64 %t/basic-archive.macho.x86_64 %t/basic-lto.macho.x86_64 %t/basic-lto-dw4.macho.x86_64 -o %t.dSYM 2>&1 | FileCheck %s + +CHECK: error: cannot use -o with multiple inputs in flat mode diff --git a/llvm/test/tools/dsymutil/X86/alias.test b/llvm/test/tools/dsymutil/X86/alias.test --- a/llvm/test/tools/dsymutil/X86/alias.test +++ b/llvm/test/tools/dsymutil/X86/alias.test @@ -1,5 +1,9 @@ # RUN: dsymutil -f -oso-prepend-path=%p/../Inputs/alias \ # RUN: %p/../Inputs/alias/foobar -o - | llvm-dwarfdump - 2>&1 | FileCheck %s + +# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs/alias \ +# RUN: %p/../Inputs/alias/foobar -o - | llvm-dwarfdump - 2>&1 | FileCheck %s + # CHECK-NOT: could not find object file symbol for symbol # CHECK: DW_AT_name ("foo.c") # CHECK: DW_AT_name ("bar.c") diff --git a/llvm/test/tools/dsymutil/X86/call-site-entry-linking.test b/llvm/test/tools/dsymutil/X86/call-site-entry-linking.test --- a/llvm/test/tools/dsymutil/X86/call-site-entry-linking.test +++ b/llvm/test/tools/dsymutil/X86/call-site-entry-linking.test @@ -1,4 +1,7 @@ RUN: dsymutil -oso-prepend-path=%p %p/Inputs/call-site-entry.macho.x86_64 -o %t.dSYM RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_return_pc +RUN: dsymutil --linker llvm -oso-prepend-path=%p %p/Inputs/call-site-entry.macho.x86_64 -o %t.dSYM +RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_return_pc + CHECK: DW_AT_call_return_pc (0x0000000100000fa4) diff --git a/llvm/test/tools/dsymutil/X86/call-site-entry-reloc.test b/llvm/test/tools/dsymutil/X86/call-site-entry-reloc.test --- a/llvm/test/tools/dsymutil/X86/call-site-entry-reloc.test +++ b/llvm/test/tools/dsymutil/X86/call-site-entry-reloc.test @@ -22,5 +22,8 @@ RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/call_return_pc/call -o %t.dSYM RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_return_pc +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/call_return_pc/call -o %t.dSYM +RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_return_pc + CHECK: DW_AT_call_return_pc (0x0000000100000f72) CHECK: DW_AT_call_return_pc (0x0000000100000f78) diff --git a/llvm/test/tools/dsymutil/X86/common-sym-multi.test b/llvm/test/tools/dsymutil/X86/common-sym-multi.test --- a/llvm/test/tools/dsymutil/X86/common-sym-multi.test +++ b/llvm/test/tools/dsymutil/X86/common-sym-multi.test @@ -1,6 +1,9 @@ RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/common/common.x86_64 -f -o - | llvm-dwarfdump -debug-info - | FileCheck %s RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/common/common.x86_64 -dump-debug-map | FileCheck %s --check-prefix DEBUGMAP +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/common/common.x86_64 -f -o - | llvm-dwarfdump -debug-info - | FileCheck %s +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/common/common.x86_64 -dump-debug-map | FileCheck %s --check-prefix DEBUGMAP + The test was compiled from two source files: $ cd /private/tmp/common $ cat common1.c diff --git a/llvm/test/tools/dsymutil/X86/common-sym.test b/llvm/test/tools/dsymutil/X86/common-sym.test --- a/llvm/test/tools/dsymutil/X86/common-sym.test +++ b/llvm/test/tools/dsymutil/X86/common-sym.test @@ -1,5 +1,7 @@ RUN: dsymutil -oso-prepend-path %p/.. %p/../Inputs/common.macho.x86_64 -f -o - | llvm-dwarfdump -v -debug-info - | FileCheck %s +RUN: dsymutil --linker llvm -oso-prepend-path %p/.. %p/../Inputs/common.macho.x86_64 -f -o - | llvm-dwarfdump -v -debug-info - | FileCheck %s + The test was compiled from a single source: $ cat common.c char common[16]; diff --git a/llvm/test/tools/dsymutil/X86/custom-line-table.test b/llvm/test/tools/dsymutil/X86/custom-line-table.test --- a/llvm/test/tools/dsymutil/X86/custom-line-table.test +++ b/llvm/test/tools/dsymutil/X86/custom-line-table.test @@ -1,4 +1,6 @@ # RUN: dsymutil -oso-prepend-path %p/../Inputs -y %s -f -o - | llvm-dwarfdump - --debug-line | FileCheck %s +# +# RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs -y %s -f -o - | llvm-dwarfdump - --debug-line | FileCheck %s # This test runs dsymutil on an object file with non-standard (as far # as llvm is concerned) line table settings. diff --git a/llvm/test/tools/dsymutil/X86/darwin-bundle.test b/llvm/test/tools/dsymutil/X86/darwin-bundle.test --- a/llvm/test/tools/dsymutil/X86/darwin-bundle.test +++ b/llvm/test/tools/dsymutil/X86/darwin-bundle.test @@ -11,6 +11,13 @@ RUN: dsymutil -oso-prepend-path=%p/.. %t/basic.macho.x86_64 -toolchain "toolchain&and'some CHECK-NEXT: CHECK-NEXT: diff --git a/llvm/test/tools/dsymutil/X86/dead-stripped.cpp b/llvm/test/tools/dsymutil/X86/dead-stripped.cpp --- a/llvm/test/tools/dsymutil/X86/dead-stripped.cpp +++ b/llvm/test/tools/dsymutil/X86/dead-stripped.cpp @@ -1,5 +1,10 @@ // RUN: dsymutil -f -y %p/dummy-debug-map.map -oso-prepend-path %p/../Inputs/dead-stripped -o - | llvm-dwarfdump - --debug-info | FileCheck %s --implicit-check-not "{{DW_AT_low_pc|DW_AT_high_pc|DW_AT_location|DW_TAG|NULL}}" +// RUN: dsymutil --linker llvm -f -y %p/dummy-debug-map.map -oso-prepend-path \ +// RUN: %p/../Inputs/dead-stripped -o - | llvm-dwarfdump - --debug-info | \ +// RUN: FileCheck %s --implicit-check-not \ +// RUN: "{{DW_AT_low_pc|DW_AT_high_pc|DW_AT_location|DW_TAG|NULL}}" + // The test was compiled with: // clang++ -O2 -g -c dead-strip.cpp -o 1.o diff --git a/llvm/test/tools/dsymutil/X86/debug-loc-base-addr.test b/llvm/test/tools/dsymutil/X86/debug-loc-base-addr.test --- a/llvm/test/tools/dsymutil/X86/debug-loc-base-addr.test +++ b/llvm/test/tools/dsymutil/X86/debug-loc-base-addr.test @@ -1,5 +1,7 @@ RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/baseaddr/loc1.x86_64 -f -o - | llvm-dwarfdump --debug-info - | FileCheck %s +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/baseaddr/loc1.x86_64 -f -o - | llvm-dwarfdump --debug-info - | FileCheck %s + The test was compiled from a single source: $ cat loc1.cpp int f1(int i, int j) { diff --git a/llvm/test/tools/dsymutil/X86/dwarf4-linetable.test b/llvm/test/tools/dsymutil/X86/dwarf4-linetable.test --- a/llvm/test/tools/dsymutil/X86/dwarf4-linetable.test +++ b/llvm/test/tools/dsymutil/X86/dwarf4-linetable.test @@ -1,5 +1,7 @@ # RUN: dsymutil -f -oso-prepend-path=%p/../Inputs/ -y %s -o - | llvm-dwarfdump -debug-line - | FileCheck %s +# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs/ -y %s -o - | llvm-dwarfdump -debug-line - | FileCheck %s + # Source: # int main() { # return 0; diff --git a/llvm/test/tools/dsymutil/X86/dwarf5-addrx.test b/llvm/test/tools/dsymutil/X86/dwarf5-addrx.test --- a/llvm/test/tools/dsymutil/X86/dwarf5-addrx.test +++ b/llvm/test/tools/dsymutil/X86/dwarf5-addrx.test @@ -52,6 +52,17 @@ RUN: llvm-dwarfdump --verbose %t.dSYM | FileCheck %s --check-prefix UPDATE-DWARF +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/dwarf5/dwarf5-addrx.out -o %t.dSYM 2>&1 | FileCheck %s --allow-empty +RUN: llvm-dwarfdump --verify %t.dSYM 2>&1 | FileCheck %s + +RUN: llvm-dwarfdump --verbose %t.dSYM | FileCheck %s --check-prefix DWARF + +RUN: dsymutil --linker llvm --update -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/dwarf5/dwarf5-addrx.out -o %t.dSYM 2>&1 | FileCheck %s --allow-empty +RUN: llvm-dwarfdump --verify %t.dSYM 2>&1 | FileCheck %s + +RUN: llvm-dwarfdump --verbose %t.dSYM | FileCheck %s --check-prefix UPDATE-DWARF + + CHECK-NOT: error: DWARF: DW_TAG_compile_unit diff --git a/llvm/test/tools/dsymutil/X86/dwarf5-call-site-entry-reloc.test b/llvm/test/tools/dsymutil/X86/dwarf5-call-site-entry-reloc.test --- a/llvm/test/tools/dsymutil/X86/dwarf5-call-site-entry-reloc.test +++ b/llvm/test/tools/dsymutil/X86/dwarf5-call-site-entry-reloc.test @@ -22,6 +22,9 @@ #RUN: dsymutil -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM #RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_return_pc +#RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM +#RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_return_pc + #CHECK: DW_AT_call_return_pc (0x0000000100000f72) #CHECK: DW_AT_call_return_pc (0x0000000100000f78) diff --git a/llvm/test/tools/dsymutil/X86/dwarf5-dw-op-addrx.test b/llvm/test/tools/dsymutil/X86/dwarf5-dw-op-addrx.test --- a/llvm/test/tools/dsymutil/X86/dwarf5-dw-op-addrx.test +++ b/llvm/test/tools/dsymutil/X86/dwarf5-dw-op-addrx.test @@ -25,6 +25,14 @@ #RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s #RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix UPD-DWARF-CHECK +#RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM +#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s +#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix DWARF-CHECK + +#RUN: dsymutil --linker llvm --update -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM +#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s +#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix UPD-DWARF-CHECK + #CHECK: No errors. #DWARF-CHECK: DW_TAG_compile_unit diff --git a/llvm/test/tools/dsymutil/X86/dwarf5-linetable.test b/llvm/test/tools/dsymutil/X86/dwarf5-linetable.test --- a/llvm/test/tools/dsymutil/X86/dwarf5-linetable.test +++ b/llvm/test/tools/dsymutil/X86/dwarf5-linetable.test @@ -1,5 +1,7 @@ # RUN: dsymutil -f -oso-prepend-path=%p/../Inputs/ -y %s -o - | llvm-dwarfdump -debug-line -debug-line-str --verbose - | FileCheck %s +# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs/ -y %s -o - | llvm-dwarfdump -debug-line -debug-line-str --verbose - | FileCheck %s + # Source: # int main() { # return 0; diff --git a/llvm/test/tools/dsymutil/X86/dwarf5-loclists.test b/llvm/test/tools/dsymutil/X86/dwarf5-loclists.test --- a/llvm/test/tools/dsymutil/X86/dwarf5-loclists.test +++ b/llvm/test/tools/dsymutil/X86/dwarf5-loclists.test @@ -24,6 +24,14 @@ #RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s #RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix UPD-DWARF-CHECK +#RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM +#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s +#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix DWARF-CHECK + +#RUN: dsymutil --linker llvm --update -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM +#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s +#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix UPD-DWARF-CHECK + #CHECK: No errors. #DWARF-CHECK: DW_TAG_formal_parameter diff --git a/llvm/test/tools/dsymutil/X86/dwarf5-rnglists.test b/llvm/test/tools/dsymutil/X86/dwarf5-rnglists.test --- a/llvm/test/tools/dsymutil/X86/dwarf5-rnglists.test +++ b/llvm/test/tools/dsymutil/X86/dwarf5-rnglists.test @@ -32,11 +32,19 @@ #RUN: dsymutil -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM #RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s #RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix DWARF-CHECK -# + #RUN: dsymutil --update -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM #RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s #RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix UPD-DWARF-CHECK +#RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM +#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s +#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix DWARF-CHECK + +#RUN: dsymutil --linker llvm --update -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM +#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s +#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix UPD-DWARF-CHECK + #CHECK: No errors. #DWARF-CHECK: DW_TAG_compile_unit diff --git a/llvm/test/tools/dsymutil/X86/eh_frame.test b/llvm/test/tools/dsymutil/X86/eh_frame.test --- a/llvm/test/tools/dsymutil/X86/eh_frame.test +++ b/llvm/test/tools/dsymutil/X86/eh_frame.test @@ -18,5 +18,10 @@ RUN: llvm-otool -s __TEXT __eh_frame %p/../Inputs/private/tmp/eh_frame/eh_frame.out | FileCheck %s RUN: llvm-otool -s __TEXT __eh_frame %t.dSYM/Contents/Resources/DWARF/eh_frame.out | FileCheck %s +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/eh_frame/eh_frame.out -o %t.dSYM +RUN: llvm-dwarfdump --verify %t.dSYM +RUN: llvm-otool -s __TEXT __eh_frame %p/../Inputs/private/tmp/eh_frame/eh_frame.out | FileCheck %s +RUN: llvm-otool -s __TEXT __eh_frame %t.dSYM/Contents/Resources/DWARF/eh_frame.out | FileCheck %s + CHECK: 14 00 00 00 00 00 00 00 01 7a 52 00 01 78 10 01 CHECK: 10 0c 07 08 90 01 00 00 diff --git a/llvm/test/tools/dsymutil/X86/empty-CU.test b/llvm/test/tools/dsymutil/X86/empty-CU.test --- a/llvm/test/tools/dsymutil/X86/empty-CU.test +++ b/llvm/test/tools/dsymutil/X86/empty-CU.test @@ -1,6 +1,8 @@ RUN: llvm-mc %p/../Inputs/empty-CU.s -filetype obj -triple x86_64-apple-darwin -o %t.o RUN: dsymutil --update -f %t.o -o - | llvm-dwarfdump -v - -debug-info | FileCheck %s +RUN: dsymutil --linker llvm --update -f %t.o -o - | llvm-dwarfdump -v - -debug-info | FileCheck %s + CHECK: .debug_info contents: CHECK: 0x00000000: Compile Unit: length = 0x00000008, format = DWARF32, version = 0x0003, abbr_offset = 0x0000, addr_size = 0x04 (next unit at 0x0000000c) diff --git a/llvm/test/tools/dsymutil/X86/fat-archive-input-i386.test b/llvm/test/tools/dsymutil/X86/fat-archive-input-i386.test --- a/llvm/test/tools/dsymutil/X86/fat-archive-input-i386.test +++ b/llvm/test/tools/dsymutil/X86/fat-archive-input-i386.test @@ -1,5 +1,7 @@ # RUN: dsymutil -f -oso-prepend-path=%p/../Inputs -y %s -o - | llvm-dwarfdump -debug-info - | FileCheck %s +# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs -y %s -o - | llvm-dwarfdump -debug-info - | FileCheck %s + --- triple: 'i386-apple-darwin' objects: diff --git a/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64.test b/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64.test --- a/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64.test +++ b/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64.test @@ -1,5 +1,7 @@ # RUN: dsymutil -f -oso-prepend-path=%p/../Inputs -y %s -o - | llvm-dwarfdump -debug-info - | FileCheck %s +# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs -y %s -o - | llvm-dwarfdump -debug-info - | FileCheck %s + --- triple: 'x86_64-apple-darwin' objects: diff --git a/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64h.test b/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64h.test --- a/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64h.test +++ b/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64h.test @@ -1,5 +1,7 @@ # RUN: dsymutil -f -oso-prepend-path=%p/../Inputs -y %s -o - | llvm-dwarfdump -debug-info - | FileCheck %s +# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs -y %s -o - | llvm-dwarfdump -debug-info - | FileCheck %s + --- triple: 'x86_64h-apple-darwin' objects: diff --git a/llvm/test/tools/dsymutil/X86/generate-empty-CU.test b/llvm/test/tools/dsymutil/X86/generate-empty-CU.test --- a/llvm/test/tools/dsymutil/X86/generate-empty-CU.test +++ b/llvm/test/tools/dsymutil/X86/generate-empty-CU.test @@ -1,7 +1,9 @@ # RUN: dsymutil -f -o - -oso-prepend-path=%p/.. -y %s | llvm-dwarfdump -v - | FileCheck %s +# RUN: dsymutil --linker llvm -f -o - -oso-prepend-path=%p/.. -y %s | llvm-dwarfdump -v - | FileCheck %s + # This test on links the Dwarf for an LTO binary and on purpose doesn't retain -# any symbol in the second CU out of 3. To be valid DWARF ssymutil must not +# any symbol in the second CU out of 3. To be valid DWARF dsymutil must not # generate an empty CU but omit it. --- @@ -22,10 +24,12 @@ CHECK: DW_TAG_subprogram DW_AT_name {{.*}} "main" -CHECK: 0x00000081: Compile Unit: length = 0x00000089, format = DWARF32, version = 0x0002, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x0000010e) +CHECK: 0x00000081: Compile Unit: length = 0x00000089, format = DWARF32, version = 0x0002, abbr_offset = 0x00{{00|53}}, addr_size = 0x08 (next unit at 0x0000010e) CHECK: DW_TAG_compile_unit CHECK: DW_AT_name {{.*}} "basic3.c" -CHECK: DW_TAG_subprogram [7] * +CHECK: DW_TAG_subprogram CHECK: DW_AT_name {{.*}} = "bar" + +CHECK-NOT: DW_TAG_compile_unit diff --git a/llvm/test/tools/dsymutil/X86/global_downgraded_to_static.c b/llvm/test/tools/dsymutil/X86/global_downgraded_to_static.c --- a/llvm/test/tools/dsymutil/X86/global_downgraded_to_static.c +++ b/llvm/test/tools/dsymutil/X86/global_downgraded_to_static.c @@ -1,5 +1,9 @@ // REQUIRES : system-darwin -// RUN: dsymutil -oso-prepend-path %p/.. -dump-debug-map %p/../Inputs/global_downgraded_to_static.x86_64 2>&1 | FileCheck %s +// RUN: dsymutil -oso-prepend-path %p/.. -dump-debug-map \ +// RUN: %p/../Inputs/global_downgraded_to_static.x86_64 2>&1 | FileCheck %s +// +// RUN: dsymutil --linker llvm -oso-prepend-path %p/.. -dump-debug-map \ +// RUN: %p/../Inputs/global_downgraded_to_static.x86_64 2>&1 | FileCheck %s // // To build: // clang -g -c -DFILE1 global_downgraded_to_static.c -o 1.o diff --git a/llvm/test/tools/dsymutil/X86/inlined-static-variable.cpp b/llvm/test/tools/dsymutil/X86/inlined-static-variable.cpp --- a/llvm/test/tools/dsymutil/X86/inlined-static-variable.cpp +++ b/llvm/test/tools/dsymutil/X86/inlined-static-variable.cpp @@ -1,5 +1,10 @@ // RUN: dsymutil -f -y %p/dummy-debug-map.map -oso-prepend-path %p/../Inputs/inlined-static-variable -o - | llvm-dwarfdump - | FileCheck %s --implicit-check-not "{{DW_AT_low_pc|DW_AT_high_pc|DW_AT_location|DW_TAG|NULL}}" +// RUN: dsymutil --linker llvm -f -y %p/dummy-debug-map.map -oso-prepend-path \ +// RUN: %p/../Inputs/inlined-static-variable -o - | llvm-dwarfdump - | \ +// RUN: FileCheck %s --implicit-check-not \ +// RUN: "{{DW_AT_low_pc|DW_AT_high_pc|DW_AT_location|DW_TAG|NULL}}" + // clang -g -c inlined-static-variable.cpp -o 4.o // The functions removed and not_removed are not in the debug map and are diff --git a/llvm/test/tools/dsymutil/X86/keep-func.test b/llvm/test/tools/dsymutil/X86/keep-func.test --- a/llvm/test/tools/dsymutil/X86/keep-func.test +++ b/llvm/test/tools/dsymutil/X86/keep-func.test @@ -25,6 +25,11 @@ RUN: llvm-dwarfdump %t.omit.dSYM | FileCheck %s --check-prefix OMIT RUN: llvm-dwarfdump %t.keep.dSYM | FileCheck %s --check-prefix KEEP +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/keep_func/main.out -o %t.omit.dSYM +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/keep_func/main.out -o %t.keep.dSYM -keep-function-for-static +RUN: llvm-dwarfdump %t.omit.dSYM | FileCheck %s --check-prefix OMIT +RUN: llvm-dwarfdump %t.keep.dSYM | FileCheck %s --check-prefix KEEP + KEEP: DW_AT_name ("MyDummyVar") KEEP: DW_AT_name ("FOO_VAR_TYPE") KEEP: DW_AT_name ("x1") diff --git a/llvm/test/tools/dsymutil/X86/label.test b/llvm/test/tools/dsymutil/X86/label.test --- a/llvm/test/tools/dsymutil/X86/label.test +++ b/llvm/test/tools/dsymutil/X86/label.test @@ -1,5 +1,7 @@ # RUN: dsymutil -oso-prepend-path %p/../Inputs -y %s -f -o - | llvm-dwarfdump - --debug-info | FileCheck %s +# RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs -y %s -f -o - | llvm-dwarfdump - --debug-info | FileCheck %s + # Compile with: # echo -e ".global _foo;\nfoo:\nnop" | clang -x assembler -g - -c -o /tmp/label.o diff --git a/llvm/test/tools/dsymutil/X86/label2.test b/llvm/test/tools/dsymutil/X86/label2.test --- a/llvm/test/tools/dsymutil/X86/label2.test +++ b/llvm/test/tools/dsymutil/X86/label2.test @@ -14,6 +14,9 @@ RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/label/label.out -o %t.dSYM RUN: llvm-dwarfdump %t.dSYM | FileCheck %s +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/label/label.out -o %t.dSYM +RUN: llvm-dwarfdump %t.dSYM | FileCheck %s + CHECK: DW_TAG_label CHECK-NEXT: DW_AT_name ("foobar") CHECK-NEXT: DW_AT_decl_file ("/tmp/label{{[/\\]}}label.c") diff --git a/llvm/test/tools/dsymutil/X86/lc_build_version.test b/llvm/test/tools/dsymutil/X86/lc_build_version.test --- a/llvm/test/tools/dsymutil/X86/lc_build_version.test +++ b/llvm/test/tools/dsymutil/X86/lc_build_version.test @@ -1,6 +1,9 @@ # RUN: dsymutil -f %p/../Inputs/lc_build_version.x86_64 -o - \ # RUN: -oso-prepend-path=%p/.. | obj2yaml | FileCheck %s +# RUN: dsymutil --linker llvm -f %p/../Inputs/lc_build_version.x86_64 -o - \ +# RUN: -oso-prepend-path=%p/.. | obj2yaml | FileCheck %s + CHECK: LoadCommands: CHECK: - cmd: LC_BUILD_VERSION CHECK-NEXT: cmdsize: 24 diff --git a/llvm/test/tools/dsymutil/X86/location-expression.test b/llvm/test/tools/dsymutil/X86/location-expression.test --- a/llvm/test/tools/dsymutil/X86/location-expression.test +++ b/llvm/test/tools/dsymutil/X86/location-expression.test @@ -11,6 +11,7 @@ # RUN: echo ' - { sym: __Z3foov, objAddr: 0x0, binAddr: 0x10000, size: 0x10 }' >> %t2.map # RUN: echo '...' >> %t2.map # RUN: dsymutil -y %t2.map -f -o - | llvm-dwarfdump -a --verbose - | FileCheck %s +# RUN: dsymutil --linker llvm -y %t2.map -f -o - | llvm-dwarfdump -a --verbose - | FileCheck %s # CHECK: file format Mach-O 64-bit x86-64 # CHECK: .debug_info contents: @@ -20,11 +21,11 @@ # CHECK: 0x0000001b: DW_TAG_variable # CHECK: DW_AT_name {{.*}}"var1" # CHECK: DW_AT_type {{.*}}"class1" -# CHECK: DW_AT_location [DW_FORM_block1] (DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address) +# CHECK: DW_AT_location [DW_FORM_block1] (DW_OP_const8u 0x{{.*}}, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address) # CHECK: 0x000000ab: DW_TAG_variable # CHECK: DW_AT_name {{.*}}"var2" # CHECK: DW_AT_type {{.*}}"class1" -# CHECK: DW_AT_location [DW_FORM_block] (DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address) +# CHECK: DW_AT_location [DW_FORM_block] (DW_OP_const8u 0x{{.*}}, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address) # CHECK: 0x00000146: DW_TAG_variable # CHECK: DW_AT_name {{.*}}"var3" # CHECK: DW_AT_type {{.*}}"class1" @@ -72,13 +73,21 @@ offset: 0x00000410 align: 0 reloff: 0x00000600 - nreloc: 1 + nreloc: 2 flags: 0x02000000 reserved1: 0x00000000 reserved2: 0x00000000 reserved3: 0x00000000 relocations: - - address: 0x1FC + - address: 0x30 + symbolnum: 1 + pcrel: true + length: 3 + extern: true + type: 0 + scattered: false + value: 0 + - address: 0xc2 symbolnum: 1 pcrel: true length: 3 diff --git a/llvm/test/tools/dsymutil/X86/mismatch.m b/llvm/test/tools/dsymutil/X86/mismatch.m --- a/llvm/test/tools/dsymutil/X86/mismatch.m +++ b/llvm/test/tools/dsymutil/X86/mismatch.m @@ -19,6 +19,8 @@ // RUN: cp %p/../Inputs/mismatch/1.o %t.dir/2.o // RUN: dsymutil --verbose -f -oso-prepend-path=%t.dir \ // RUN: -y %p/dummy-debug-map.map -o %t.bin 2>&1 | FileCheck %s +// RUN: dsymutil --linker llvm --verbose -f -oso-prepend-path=%t.dir \ +// RUN: -y %p/dummy-debug-map.map -o %t.bin 2>&1 | FileCheck %s @import mismatch; diff --git a/llvm/test/tools/dsymutil/X86/modules-dwarf-version.m b/llvm/test/tools/dsymutil/X86/modules-dwarf-version.m --- a/llvm/test/tools/dsymutil/X86/modules-dwarf-version.m +++ b/llvm/test/tools/dsymutil/X86/modules-dwarf-version.m @@ -11,6 +11,9 @@ // RUN: dsymutil -verify -f -oso-prepend-path=%t.dir \ // RUN: -y %p/dummy-debug-map.map -o - \ // RUN: | llvm-dwarfdump --debug-info - | FileCheck %s +// RUN: dsymutil --linker llvm -verify -f -oso-prepend-path=%t.dir \ +// RUN: -y %p/dummy-debug-map.map -o - \ +// RUN: | llvm-dwarfdump --debug-info - | FileCheck %s @import Bar; int main(int argc, char **argv) { diff --git a/llvm/test/tools/dsymutil/X86/modules-empty.m b/llvm/test/tools/dsymutil/X86/modules-empty.m --- a/llvm/test/tools/dsymutil/X86/modules-empty.m +++ b/llvm/test/tools/dsymutil/X86/modules-empty.m @@ -12,11 +12,16 @@ // RUN: rm -rf %t.dir // RUN: mkdir %t.dir -// RUN: cp %p/../Inputs/modules-empty/1.o %p/../Inputs/modules-empty/Empty.pcm %t.dir +// RUN: cp %p/../Inputs/modules-empty/1.o %p/../Inputs/modules-empty/Empty.pcm \ +// RUN: %t.dir // RUN: dsymutil -f -oso-prepend-path=%t.dir \ // RUN: -verify \ // RUN: -y %p/dummy-debug-map.map -o - \ // RUN: | llvm-dwarfdump --debug-info - | FileCheck %s +// RUN: dsymutil --linker llvm -f -oso-prepend-path=%t.dir \ +// RUN: -verify \ +// RUN: -y %p/dummy-debug-map.map -o - \ +// RUN: | llvm-dwarfdump --debug-info - | FileCheck %s #include "Empty.h" int main() { diff --git a/llvm/test/tools/dsymutil/X86/multiple-inputs.test b/llvm/test/tools/dsymutil/X86/multiple-inputs.test --- a/llvm/test/tools/dsymutil/X86/multiple-inputs.test +++ b/llvm/test/tools/dsymutil/X86/multiple-inputs.test @@ -28,4 +28,3 @@ RUN: not dsymutil -f -oso-prepend-path=%p/.. %t/basic.macho.x86_64 %t/basic-archive.macho.x86_64 %t/basic-lto.macho.x86_64 %t/basic-lto-dw4.macho.x86_64 -o %t.dSYM 2>&1 | FileCheck %s CHECK: error: cannot use -o with multiple inputs in flat mode - diff --git a/llvm/test/tools/dsymutil/X86/object-prefix-path.test b/llvm/test/tools/dsymutil/X86/object-prefix-path.test --- a/llvm/test/tools/dsymutil/X86/object-prefix-path.test +++ b/llvm/test/tools/dsymutil/X86/object-prefix-path.test @@ -7,5 +7,10 @@ RUN: -object-prefix-map=/ModuleCache=/ModuleCacheRenamed \ RUN: 2>&1 | FileCheck %s +RUN: dsymutil --linker llvm -verify -f -oso-prepend-path=%t.dir -y \ +RUN: %p/dummy-debug-map.map -o %t \ +RUN: -object-prefix-map=/ModuleCache=/ModuleCacheRenamed \ +RUN: 2>&1 | FileCheck %s + CHECK: warning: {{.*}}Bar.pcm: CHECK-NOT: warning: {{.*}}Foo.pcm: diff --git a/llvm/test/tools/dsymutil/X86/op-convert-offset.test b/llvm/test/tools/dsymutil/X86/op-convert-offset.test --- a/llvm/test/tools/dsymutil/X86/op-convert-offset.test +++ b/llvm/test/tools/dsymutil/X86/op-convert-offset.test @@ -24,6 +24,14 @@ RUN: llvm-dwarfdump %p/../Inputs/private/tmp/op-convert-offset/op-convert-offset.o 2>&1 | FileCheck %s --check-prefix OBJ RUN: llvm-dwarfdump %t.dSYM 2>&1 | FileCheck %s --check-prefix DSYM +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs \ +RUN: %p/../Inputs/private/tmp/op-convert-offset/op-convert-offset \ +RUN: -o %t.dSYM 2>&1 +RUN: llvm-dwarfdump \ +RUN: %p/../Inputs/private/tmp/op-convert-offset/op-convert-offset.o 2>&1 \ +RUN: | FileCheck %s --check-prefix OBJ +RUN: llvm-dwarfdump %t.dSYM 2>&1 | FileCheck %s --check-prefix DSYM + OBJ: 0x0000007d: DW_TAG_base_type OBJ: DW_AT_name ("DW_ATE_unsigned_1") OBJ: DW_AT_encoding (DW_ATE_unsigned) @@ -42,4 +50,4 @@ DSYM: 0x000000d5: DW_TAG_formal_parameter DSYM: DW_AT_location (DW_OP_breg2 RCX+0, DW_OP_constu 0xff, DW_OP_and, DW_OP_convert (0x000000ae) "DW_ATE_unsigned_1", DW_OP_convert (0x000000b5) "DW_ATE_unsigned_8", DW_OP_stack_value) DSYM: DW_AT_name ("b") -DSYM: DW_AT_type (0x000000f3 "_Bool") +DSYM: DW_AT_type (0x000000f{{3|5}} "_Bool") diff --git a/llvm/test/tools/dsymutil/X86/op-convert.test b/llvm/test/tools/dsymutil/X86/op-convert.test --- a/llvm/test/tools/dsymutil/X86/op-convert.test +++ b/llvm/test/tools/dsymutil/X86/op-convert.test @@ -1,6 +1,9 @@ # RUN: dsymutil -f -o %t --verify -oso-prepend-path=%p/../Inputs -y %s # RUN: llvm-dwarfdump %t | FileCheck %s +# RUN: dsymutil --linker llvm -f -o %t --verify -oso-prepend-path=%p/../Inputs -y %s +# RUN: llvm-dwarfdump %t | FileCheck %s + --- triple: 'x86_64-apple-darwin' objects: diff --git a/llvm/test/tools/dsymutil/X86/papertrail-warnings.test b/llvm/test/tools/dsymutil/X86/papertrail-warnings.test --- a/llvm/test/tools/dsymutil/X86/papertrail-warnings.test +++ b/llvm/test/tools/dsymutil/X86/papertrail-warnings.test @@ -1,5 +1,7 @@ RUN: env RC_DEBUG_OPTIONS=1 dsymutil -f %p/../Inputs/basic.macho.x86_64 -o - | llvm-dwarfdump -v - | FileCheck -DMSG=%errc_ENOENT %s +RUN: env RC_DEBUG_OPTIONS=1 dsymutil --linker llvm -f %p/../Inputs/basic.macho.x86_64 -o - | llvm-dwarfdump -v - | FileCheck -DMSG=%errc_ENOENT %s + CHECK: .debug_info contents: CHECK: Compile Unit: CHECK: DW_TAG_compile_unit [1] * diff --git a/llvm/test/tools/dsymutil/X86/reflection-dump.test b/llvm/test/tools/dsymutil/X86/reflection-dump.test --- a/llvm/test/tools/dsymutil/X86/reflection-dump.test +++ b/llvm/test/tools/dsymutil/X86/reflection-dump.test @@ -9,6 +9,9 @@ RUN: dsymutil -oso-prepend-path=%t.dir %t.dir/main -o %t.dir/main.dSYM RUN: llvm-objdump -s %t.dir/main.dSYM/Contents/Resources/DWARF/main | FileCheck %s +RUN: dsymutil --linker llvm -oso-prepend-path=%t.dir %t.dir/main -o %t.dir/main.dSYM +RUN: llvm-objdump -s %t.dir/main.dSYM/Contents/Resources/DWARF/main | FileCheck %s + REQUIRES: host-byteorder-little-endian diff --git a/llvm/test/tools/dsymutil/X86/remarks-linking-archive.text b/llvm/test/tools/dsymutil/X86/remarks-linking-archive.text --- a/llvm/test/tools/dsymutil/X86/remarks-linking-archive.text +++ b/llvm/test/tools/dsymutil/X86/remarks-linking-archive.text @@ -7,10 +7,17 @@ Check that the remark file in the bundle exists and is sane: RUN: llvm-bcanalyzer -dump %t/basic.macho.remarks.archive.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.archive.x86_64 | FileCheck %s +RUN: dsymutil --linker llvm -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.archive.x86_64 + +Check that the remark file in the bundle exists and is sane: +RUN: llvm-bcanalyzer -dump %t/basic.macho.remarks.archive.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.archive.x86_64 | FileCheck %s + Check that we don't error if we're missing remark files from an archive, but we warn instead. Instead of creating a new binary, just remove the remarks prepend path. RUN: dsymutil -oso-prepend-path=%p/../Inputs %t/basic.macho.remarks.archive.x86_64 2>&1 | FileCheck %s --check-prefix=CHECK-MISSING +RUN: dsymutil --linker llvm -oso-prepend-path=%p/../Inputs %t/basic.macho.remarks.archive.x86_64 2>&1 | FileCheck %s --check-prefix=CHECK-MISSING + CHECK: &1 + +RUN: dsymutil --linker llvm -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.empty.x86_64 + +Check that the remark file in the bundle does not exist: +RUN: not cat %t/basic.macho.remarks.empty.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.empty.x86_64 2>&1 diff --git a/llvm/test/tools/dsymutil/X86/remarks-linking-bundle.test b/llvm/test/tools/dsymutil/X86/remarks-linking-bundle.test --- a/llvm/test/tools/dsymutil/X86/remarks-linking-bundle.test +++ b/llvm/test/tools/dsymutil/X86/remarks-linking-bundle.test @@ -7,10 +7,18 @@ Check that the remark file in the bundle exists and is sane: RUN: llvm-bcanalyzer -dump %t/basic.macho.remarks.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.x86_64 | FileCheck %s +RUN: dsymutil --linker llvm -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.x86_64 + +Check that the remark file in the bundle exists and is sane: +RUN: llvm-bcanalyzer -dump %t/basic.macho.remarks.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.x86_64 | FileCheck %s + Now emit it in a different format: YAML. RUN: dsymutil -remarks-output-format=yaml -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.x86_64 RUN: cat %t/basic.macho.remarks.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.x86_64 | FileCheck %s --check-prefix=CHECK-YAML +RUN: dsymutil --linker llvm -remarks-output-format=yaml -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.x86_64 +RUN: cat %t/basic.macho.remarks.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.x86_64 | FileCheck %s --check-prefix=CHECK-YAML + CHECK: &1 | FileCheck %s --check-prefix=ERROR +RUN: not dsymutil --linker llvm -f -o %t.error -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 2>&1 | FileCheck %s --check-prefix=ERROR + # Use the reproducer. RUN: dsymutil -use-reproducer %t.repro -f -o - -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s +RUN: dsymutil --linker llvm -use-reproducer %t.repro -f -o - -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s + # Using a reproducer takes precedence. RUN: dsymutil -gen-reproducer -use-reproducer %t.repro -f -o - -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s +RUN: dsymutil --linker llvm -gen-reproducer -use-reproducer %t.repro -f -o - -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s + CHECK: .debug_info CHECK: DW_TAG_compile_unit CHECK-NEXT: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)") diff --git a/llvm/test/tools/dsymutil/X86/statistics.test b/llvm/test/tools/dsymutil/X86/statistics.test --- a/llvm/test/tools/dsymutil/X86/statistics.test +++ b/llvm/test/tools/dsymutil/X86/statistics.test @@ -1,4 +1,5 @@ # RUN: dsymutil -statistics -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 %p/../Inputs/basic-archive.macho.x86_64 %p/../Inputs/basic-lto.macho.x86_64 %p/../Inputs/basic-lto-dw4.macho.x86_64 -o %t 2>&1 | FileCheck %s +# RUN: dsymutil --linker llvm -statistics -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 %p/../Inputs/basic-archive.macho.x86_64 %p/../Inputs/basic-lto.macho.x86_64 %p/../Inputs/basic-lto-dw4.macho.x86_64 -o %t 2>&1 | FileCheck %s # # CHECK: ------------------------------------------------------------------------------- # CHECK-NEXT: Filename Object dSYM Change diff --git a/llvm/test/tools/dsymutil/X86/swift-ast-x86_64.test b/llvm/test/tools/dsymutil/X86/swift-ast-x86_64.test --- a/llvm/test/tools/dsymutil/X86/swift-ast-x86_64.test +++ b/llvm/test/tools/dsymutil/X86/swift-ast-x86_64.test @@ -3,6 +3,12 @@ RUN: llvm-readobj --sections --section-data %T/swift-ast.dSYM/Contents/Resources/DWARF/swift-ast.macho.x86_64 | FileCheck %s --check-prefix=READOBJ RUN: llvm-dwarfdump --show-section-sizes %T/swift-ast.dSYM/Contents/Resources/DWARF/swift-ast.macho.x86_64 | FileCheck %s --check-prefix=DWARFDUMP +RUN: dsymutil --linker llvm -oso-prepend-path %p/.. %p/../Inputs/swift-ast.macho.x86_64 -o %T/swift-ast.dSYM -verbose -no-swiftmodule-timestamp | FileCheck %s --check-prefix=DSYMUTIL +RUN: dsymutil --linker llvm -oso-prepend-path %p/.. %p/../Inputs/swift-ast.macho.x86_64 -o %T/swift-ast.dSYM -verbose | FileCheck %s --check-prefix=DSYMUTIL +RUN: llvm-readobj --sections --section-data %T/swift-ast.dSYM/Contents/Resources/DWARF/swift-ast.macho.x86_64 | FileCheck %s --check-prefix=READOBJ +RUN: llvm-dwarfdump --show-section-sizes %T/swift-ast.dSYM/Contents/Resources/DWARF/swift-ast.macho.x86_64 | FileCheck %s --check-prefix=DWARFDUMP + + The tested object file has been created by the dummy Swift code: let x = 1 diff --git a/llvm/test/tools/dsymutil/X86/swift-dwarf-loc.test b/llvm/test/tools/dsymutil/X86/swift-dwarf-loc.test --- a/llvm/test/tools/dsymutil/X86/swift-dwarf-loc.test +++ b/llvm/test/tools/dsymutil/X86/swift-dwarf-loc.test @@ -1,5 +1,7 @@ RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/swift-dwarf-loc.macho.x86_64 -no-output -verbose | FileCheck %s +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/swift-dwarf-loc.macho.x86_64 -no-output -verbose | FileCheck %s + This test checks that dsymutil generates a valid dwarf location for a symbol with no flags set. The following IR was compiled for x86_64-apple: diff --git a/llvm/test/tools/dsymutil/X86/tail-call-linking.test b/llvm/test/tools/dsymutil/X86/tail-call-linking.test --- a/llvm/test/tools/dsymutil/X86/tail-call-linking.test +++ b/llvm/test/tools/dsymutil/X86/tail-call-linking.test @@ -1,4 +1,7 @@ RUN: dsymutil -oso-prepend-path=%p %p/Inputs/tail-call.macho.x86_64 -o %t.dSYM RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_pc +RUN: dsymutil --linker llvm -oso-prepend-path=%p %p/Inputs/tail-call.macho.x86_64 -o %t.dSYM +RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_pc + CHECK: DW_AT_call_pc (0x0000000100000f95) diff --git a/llvm/test/tools/dsymutil/X86/thinlto.test b/llvm/test/tools/dsymutil/X86/thinlto.test --- a/llvm/test/tools/dsymutil/X86/thinlto.test +++ b/llvm/test/tools/dsymutil/X86/thinlto.test @@ -20,5 +20,8 @@ $ xcrun clang++ -flto=thin foo.o bar.o -Xlinker -object_path_lto -Xlinker lto -shared -o foobar.dylib RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/thinlto/foobar.dylib -o %t.dSYM 2>&1 | FileCheck %s --allow-empty + +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/thinlto/foobar.dylib -o %t.dSYM 2>&1 | FileCheck %s --allow-empty + CHECK-NOT: could not find object file symbol for symbol __ZZ9function2vE12magic_static CHECK-NOT: could not find object file symbol for symbol __ZGVZ9function2vE12magic_static diff --git a/llvm/test/tools/dsymutil/X86/timestamp-mismatch.test b/llvm/test/tools/dsymutil/X86/timestamp-mismatch.test --- a/llvm/test/tools/dsymutil/X86/timestamp-mismatch.test +++ b/llvm/test/tools/dsymutil/X86/timestamp-mismatch.test @@ -5,6 +5,8 @@ RUN: cp %p/../Inputs/basic3.macho.x86_64.o %t/Inputs RUN: dsymutil -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 -o %t.dSYM 2>&1 | FileCheck %s +RUN: dsymutil --linker llvm -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 -o %t.dSYM 2>&1 | FileCheck %s + CHECK: warning: {{.*}}/Inputs/basic1.macho.x86_64.o: timestamp mismatch between object file ({{.*}}) and debug map ({{.*}}) CHECK: warning: {{.*}}/Inputs/basic2.macho.x86_64.o: timestamp mismatch between object file ({{.*}}) and debug map ({{.*}}) CHECK: warning: {{.*}}/Inputs/basic3.macho.x86_64.o: timestamp mismatch between object file ({{.*}}) and debug map ({{.*}}) diff --git a/llvm/test/tools/dsymutil/X86/tls-variable.test b/llvm/test/tools/dsymutil/X86/tls-variable.test --- a/llvm/test/tools/dsymutil/X86/tls-variable.test +++ b/llvm/test/tools/dsymutil/X86/tls-variable.test @@ -10,6 +10,7 @@ # RUN: echo ' - { sym: __Z3foov, objAddr: 0x0, binAddr: 0x10000, size: 0x10 }' >> %t2.map # RUN: echo '...' >> %t2.map # RUN: dsymutil -y %t2.map --keep-function-for-static -f -o - | llvm-dwarfdump -a - | FileCheck %s +# RUN: dsymutil --linker llvm -y %t2.map --keep-function-for-static -f -o - | llvm-dwarfdump -a - | FileCheck %s # CHECK: file format Mach-O 64-bit x86-64 # CHECK: .debug_info contents: diff --git a/llvm/test/tools/dsymutil/X86/union-fwd-decl.test b/llvm/test/tools/dsymutil/X86/union-fwd-decl.test --- a/llvm/test/tools/dsymutil/X86/union-fwd-decl.test +++ b/llvm/test/tools/dsymutil/X86/union-fwd-decl.test @@ -48,6 +48,9 @@ RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/union/a.out -o %t.dSYM RUN: llvm-dwarfdump %t.dSYM | FileCheck %s +RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/union/a.out -o %t.dSYM +RUN: llvm-dwarfdump %t.dSYM | FileCheck %s + CHECK: DW_TAG_compile_unit CHECK: DW_AT_name ("Container_ivars") diff --git a/llvm/test/tools/dsymutil/X86/verify.test b/llvm/test/tools/dsymutil/X86/verify.test --- a/llvm/test/tools/dsymutil/X86/verify.test +++ b/llvm/test/tools/dsymutil/X86/verify.test @@ -25,6 +25,25 @@ # QUIET-SUCCESS-NOT: input verification failed # QUIET-SUCCESS-NOT: output verification failed # BOGUS: error: invalid verify type specified: 'bogus' +# +# Positive tests in regular and verbose mode. +# RUN: dsymutil --linker llvm -verify -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 %p/../Inputs/basic-archive.macho.x86_64 %p/../Inputs/basic-lto.macho.x86_64 %p/../Inputs/basic-lto-dw4.macho.x86_64 -o %t 2>&1 | FileCheck %s --allow-empty --check-prefix=QUIET-SUCCESS +# RUN: dsymutil --linker llvm -verify -verbose -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 %p/../Inputs/basic-archive.macho.x86_64 %p/../Inputs/basic-lto.macho.x86_64 %p/../Inputs/basic-lto-dw4.macho.x86_64 -o %t 2>&1 | FileCheck %s --check-prefixes=QUIET-SUCCESS,VERBOSE +# +# # Negative output tests in regular and verbose mode. +# (Invalid object generated from ../Inputs/invalid.s by modified the low PC.) +# RUN: not dsymutil --linker llvm -verify -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefix=QUIET-OUTPUT-FAIL +# RUN: not dsymutil --linker llvm -verify-dwarf=output -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefix=QUIET-OUTPUT-FAIL +# RUN: not dsymutil --linker llvm -verify -verbose -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefixes=QUIET-OUTPUT-FAIL,VERBOSE +# +# # Negative input & output tests in regular and verbose mode. Only output failures result in a non-zero exit code. +# RUN: dsymutil --linker llvm -verify-dwarf=input -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefix=QUIET-INPUT-FAIL +# RUN: dsymutil --linker llvm -verify-dwarf=input -verbose -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefix=QUIET-INPUT-FAIL +# RUN: dsymutil --linker llvm -verify-dwarf=none -verbose -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefixes=QUIET-SUCCESS +# RUN: not dsymutil --linker llvm -verify-dwarf=bogus -verbose -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefixes=BOGUS +# RUN: not dsymutil --linker llvm -verify-dwarf=all -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefixes=QUIET-OUTPUT-FAIL,QUIET-INPUT-FAIL +# RUN: not dsymutil --linker llvm -verify-dwarf=all -verbose -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefixes=VERBOSE-INPUT-FAIL + --- triple: 'x86_64-apple-darwin' diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/DWARFLinkerParallel/gc-default.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/DWARFLinkerParallel/gc-default.test deleted file mode 100644 --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/DWARFLinkerParallel/gc-default.test +++ /dev/null @@ -1,134 +0,0 @@ -## 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 --linker llvm %t.o %t1.out 2>&1 | FileCheck %s --allow-empty - -#CHECK: LLVM parallel dwarflinker is not implemented yet. - ---- !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 - Size: 0x1b -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/X86/dwarf4-macro-short.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro-short.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro-short.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro-short.test @@ -7,6 +7,10 @@ # RUN: llvm-dwarfdump --verify %t1 | FileCheck --check-prefix=VERIFY %s # RUN: llvm-dwarfdump -a %t1 | FileCheck %s +# RUN: llvm-dwarfutil --linker llvm %t.o %t1 +# RUN: llvm-dwarfdump --verify %t1 | FileCheck --check-prefix=VERIFY %s +# RUN: llvm-dwarfdump -a %t1 | FileCheck %s + # VERIFY: No errors. ## Content: "03000201064400020744000400": diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro.test @@ -39,16 +39,28 @@ #RUN: llvm-dwarfdump -verify %t1 | FileCheck %s #RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACINFO +#RUN: llvm-dwarfutil --linker llvm --garbage-collection %p/Inputs/dwarf4-macro.out %t1 +#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACINFO + ## Check that macro table preserved during simple copying. # #RUN: llvm-dwarfutil --no-garbage-collection %p/Inputs/dwarf4-macro.out %t1 #RUN: llvm-dwarfdump -verify %t1 | FileCheck %s #RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACINFO +#RUN: llvm-dwarfutil --linker llvm --no-garbage-collection %p/Inputs/dwarf4-macro.out %t1 +#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACINFO + ## Check that macro table preserved during updating accelerator tables. #RUN: llvm-dwarfutil --no-garbage-collection --build-accelerator=DWARF %p/Inputs/dwarf4-macro.out %t1 #RUN: llvm-dwarfdump -verify %t1 | FileCheck %s -#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefixes=MACINFO,NAMES +#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefixes=MACINFO + +#RUN: llvm-dwarfutil --linker llvm --no-garbage-collection --build-accelerator=DWARF %p/Inputs/dwarf4-macro.out %t1 +#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefixes=MACINFO #CHECK: No errors. @@ -2228,6 +2240,3 @@ #MACINFO-NEXT: DW_MACINFO_define - lineno: 0 macro: __STDC_UTF_32__ 1 #MACINFO-NEXT: DW_MACINFO_define - lineno: 0 macro: __GCC_HAVE_DWARF2_CFI_ASM 1 - -#NAMES: .debug_names contents -#NAMES: Name Index diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-addresses.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-addresses.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-addresses.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-addresses.test @@ -12,15 +12,27 @@ # RUN: llvm-dwarfutil %t.o %t1 # RUN: llvm-dwarfdump -verify %t1 | FileCheck %s # RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix DWARF-CHECK -# + +# RUN: llvm-dwarfutil --linker llvm %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix DWARF-CHECK + # RUN: llvm-dwarfutil --no-garbage-collection %t.o %t1 # RUN: llvm-dwarfdump -verify %t1 | FileCheck %s # RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK -# + +# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK + # RUN: llvm-dwarfutil --no-garbage-collection --build-accelerator=DWARF %t.o %t1 # RUN: llvm-dwarfdump -verify %t1 | FileCheck %s # RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK +# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection --build-accelerator=DWARF %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK + #CHECK: No errors. #DWARF-CHECK: DW_TAG_compile_unit diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-attributes.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-attributes.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-attributes.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-attributes.test @@ -6,15 +6,27 @@ # RUN: llvm-dwarfutil %t.o %t1 # RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK # RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s -# + +# RUN: llvm-dwarfutil --linker llvm %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s + # RUN: llvm-dwarfutil --no-garbage-collection %t.o %t1 # RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK # RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s -# + +# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s + # RUN: llvm-dwarfutil --no-garbage-collection --build-accelerator=DWARF %t.o %t1 # RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK # RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s +# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection --build-accelerator=DWARF %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s + #VERIFY-CHECK: No errors. #CHECK: .debug_abbrev diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-line-str.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-line-str.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-line-str.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-line-str.test @@ -6,15 +6,27 @@ # RUN: llvm-dwarfutil %t.o %t1 # RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK # RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s -# + # RUN: llvm-dwarfutil --no-garbage-collection %t.o %t1 # RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK # RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s -# + # RUN: llvm-dwarfutil --no-garbage-collection --build-accelerator=DWARF %t.o %t1 # RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK # RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s +# RUN: llvm-dwarfutil --linker llvm %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s + +# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s + +# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection --build-accelerator=DWARF %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s + #VERIFY-CHECK: No errors. #CHECK: .debug_info diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-loclists.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-loclists.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-loclists.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-loclists.test @@ -16,6 +16,18 @@ # RUN: llvm-dwarfdump -verify %t1 | FileCheck %s # RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK +# RUN: llvm-dwarfutil --linker llvm %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix DWARF-CHECK + +# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK + +# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection --build-accelerator=DWARF %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK + #CHECK: No errors. #DWARF-CHECK: DW_TAG_compile_unit diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-opcodeop.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-opcodeop.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-opcodeop.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-opcodeop.test @@ -6,6 +6,10 @@ # RUN: llvm-dwarfdump --verify %t1 | FileCheck --check-prefix=VERIFY %s # RUN: llvm-dwarfdump -a %t1 | FileCheck %s +# RUN: llvm-dwarfutil --linker llvm %t.o %t1 2>&1 | FileCheck --check-prefix=ERR %s +# RUN: llvm-dwarfdump --verify %t1 | FileCheck --check-prefix=VERIFY %s +# RUN: llvm-dwarfdump -a %t1 | FileCheck %s + # ERR: error: opcode_operands_table is not supported # VERIFY: No errors. diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-short.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-short.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-short.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-short.test @@ -8,6 +8,10 @@ # RUN: llvm-dwarfdump --verify %t1 | FileCheck --check-prefix=VERIFY %s # RUN: llvm-dwarfdump -a %t1 | FileCheck %s +# RUN: llvm-dwarfutil --linker llvm %t.o %t1 2>&1 | FileCheck --check-prefix=WARN %s +# RUN: llvm-dwarfdump --verify %t1 | FileCheck --check-prefix=VERIFY %s +# RUN: llvm-dwarfdump -a %t1 | FileCheck %s + # WARN: warning: DW_MACRO_define_strx unsupported yet. Convert to DW_MACRO_define_strp. # WARN: warning: DW_MACRO_undef_strx unsupported yet. Convert to DW_MACRO_undef_strp. # WARN: warning: DW_MACRO_import and DW_MACRO_import_sup are unsupported yet. remove. diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro.test @@ -39,16 +39,28 @@ #RUN: llvm-dwarfdump -verify %t1 | FileCheck %s #RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACRO +#RUN: llvm-dwarfutil --linker llvm --garbage-collection %p/Inputs/dwarf5-macro.out %t1 +#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACRO + ## Check that macro table preserved during simple copying. # #RUN: llvm-dwarfutil --no-garbage-collection %p/Inputs/dwarf5-macro.out %t1 #RUN: llvm-dwarfdump -verify %t1 | FileCheck %s #RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACRO +#RUN: llvm-dwarfutil --linker llvm --no-garbage-collection %p/Inputs/dwarf5-macro.out %t1 +#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACRO + ## Check that macro table preserved during updating accelerator tables. #RUN: llvm-dwarfutil --no-garbage-collection --build-accelerator=DWARF %p/Inputs/dwarf5-macro.out %t1 #RUN: llvm-dwarfdump -verify %t1 | FileCheck %s -#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefixes=MACRO,NAMES +#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefixes=MACRO + +#RUN: llvm-dwarfutil --linker llvm --no-garbage-collection --build-accelerator=DWARF %p/Inputs/dwarf5-macro.out %t1 +#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefixes=MACRO #CHECK: No errors. @@ -2233,6 +2245,3 @@ #MACRO-NEXT: DW_MACRO_define_str{{[px]}} - lineno: 0 macro: __STDC_UTF_16__ 1 #MACRO-NEXT: DW_MACRO_define_str{{[px]}} - lineno: 0 macro: __STDC_UTF_32__ 1 #MACRO-NEXT: DW_MACRO_define_str{{[px]}} - lineno: 0 macro: __GCC_HAVE_DWARF2_CFI_ASM 1 - -#NAMES: .debug_names contents -#NAMES: Name Index diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-rnglists.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-rnglists.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-rnglists.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-rnglists.test @@ -8,14 +8,26 @@ # RUN: llvm-dwarfdump -verify %t1 | FileCheck %s # RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix DWARF-CHECK +# RUN: llvm-dwarfutil --linker llvm %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix DWARF-CHECK + # RUN: llvm-dwarfutil --no-garbage-collection %t.o %t1 # RUN: llvm-dwarfdump -verify %t1 | FileCheck %s # RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK +# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK + # RUN: llvm-dwarfutil --no-garbage-collection --build-accelerator=DWARF %t.o %t1 # RUN: llvm-dwarfdump -verify %t1 | FileCheck %s # RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK +# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection --build-accelerator=DWARF %t.o %t1 +# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s +# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK + #CHECK: No errors. #DWARF-CHECK: DW_TAG_compile_unit diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-default.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-default.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-default.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-default.test @@ -7,14 +7,20 @@ # RUN: llvm-dwarfutil --linker apple %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC +# RUN: llvm-dwarfutil --linker llvm %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 --linker llvm --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 +# RUN: llvm-dwarfutil --linker llvm %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 diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-func-overlapping-address-ranges.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-func-overlapping-address-ranges.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-func-overlapping-address-ranges.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-func-overlapping-address-ranges.test @@ -5,6 +5,9 @@ # RUN: llvm-dwarfutil --garbage-collection %t.o %t1 # RUN: llvm-dwarfdump -a %t1 | FileCheck %s +# RUN: llvm-dwarfutil --linker llvm --garbage-collection %t.o %t1 +# RUN: llvm-dwarfdump -a %t1 | FileCheck %s + # CHECK: DW_TAG_compile_unit # CHECK: DW_AT_name{{.*}}"CU1" # CHECK: DW_AT_low_pc{{.*}}0000000000001000 diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-maxpc.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-maxpc.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-maxpc.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-maxpc.test @@ -6,6 +6,9 @@ # 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 +# RUN: llvm-dwarfutil --linker llvm --tombstone=maxpc --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s +# RUN: llvm-dwarfutil --linker llvm --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 diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-no-garbage.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-no-garbage.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-no-garbage.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-no-garbage.test @@ -4,6 +4,8 @@ # RUN: yaml2obj %s -o %t.o # RUN: llvm-dwarfutil --tombstone=maxpc --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s +# RUN: llvm-dwarfutil --linker llvm --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 diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-unit-overlapping-address-ranges.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-unit-overlapping-address-ranges.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-unit-overlapping-address-ranges.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-unit-overlapping-address-ranges.test @@ -6,6 +6,9 @@ # RUN: llvm-dwarfutil --garbage-collection %t.o %t1 # RUN: llvm-dwarfdump -a %t1 | FileCheck %s +# RUN: llvm-dwarfutil --linker llvm --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 diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/verify.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/verify.test --- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/verify.test +++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/verify.test @@ -7,18 +7,23 @@ ## Verify resulting debug info after --garbage-collection optimisation. # RUN: llvm-dwarfutil %t.o %t1 --verify +# RUN: llvm-dwarfutil --linker llvm %t.o %t1 --verify ## Verify separate debug file after --garbage-collection optimisation. # RUN: llvm-dwarfutil %t.o --separate-debug-file %t1 --verify +# RUN: llvm-dwarfutil --linker llvm %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 -DFILE=%t1 +# RUN: not llvm-dwarfutil --linker llvm --no-garbage-collection %t.o %t1 --verify 2>&1 | FileCheck %s -DFILE=%t1 ## 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 -DFILE=%t1.debug +# RUN: not llvm-dwarfutil --linker llvm --no-garbage-collection %t.o --separate-debug-file %t1 --verify 2>&1 | FileCheck %s -DFILE=%t1.debug ## Check that verification is disabled when destination is stdout. # RUN: llvm-dwarfutil %t.o - --verify 2>&1 | FileCheck %s --check-prefix=CHECK-STDOUT +# RUN: llvm-dwarfutil --linker llvm %t.o - --verify 2>&1 | FileCheck %s --check-prefix=CHECK-STDOUT # CHECK: error: '[[FILE]]': output verification failed # CHECK-STDOUT: warning: verification skipped because writing to stdout diff --git a/llvm/tools/dsymutil/BinaryHolder.h b/llvm/tools/dsymutil/BinaryHolder.h --- a/llvm/tools/dsymutil/BinaryHolder.h +++ b/llvm/tools/dsymutil/BinaryHolder.h @@ -126,6 +126,7 @@ getObjectEntry(StringRef Filename, TimestampTy Timestamp = TimestampTy()); void clear(); + void eraseObjectEntry(StringRef Filename); private: /// Cache of static archives. Objects that are part of a static archive are diff --git a/llvm/tools/dsymutil/BinaryHolder.cpp b/llvm/tools/dsymutil/BinaryHolder.cpp --- a/llvm/tools/dsymutil/BinaryHolder.cpp +++ b/llvm/tools/dsymutil/BinaryHolder.cpp @@ -276,5 +276,20 @@ ObjectCache.clear(); } +void BinaryHolder::eraseObjectEntry(StringRef Filename) { + if (Verbose) + WithColor::note() << "erasing '" << Filename << "' from cache\n"; + + if (isArchive(Filename)) { + StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first; + std::lock_guard Lock(ArchiveCacheMutex); + ArchiveCache.erase(ArchiveFilename); + return; + } + + std::lock_guard Lock(ObjectCacheMutex); + ObjectCache.erase(Filename); +} + } // namespace dsymutil } // namespace llvm 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 @@ -123,16 +123,22 @@ /// specific \p DIE related to the warning. void DwarfLinkerForBinary::reportWarning(Twine Warning, Twine Context, const DWARFDie *DIE) const { - std::lock_guard Guard(ErrorHandlerMutex); - warn(Warning, Context); - dumpDIE(DIE, Options.Verbose); + // FIXME: implement warning logging which does not block other threads. + if (ErrorHandlerMutex.try_lock()) { + warn(Warning, Context); + dumpDIE(DIE, Options.Verbose); + ErrorHandlerMutex.unlock(); + } } void DwarfLinkerForBinary::reportError(Twine Error, Twine Context, const DWARFDie *DIE) const { - std::lock_guard Guard(ErrorHandlerMutex); - error(Error, Context); - dumpDIE(DIE, Options.Verbose); + // FIXME: implement error logging which does not block other threads. + if (ErrorHandlerMutex.try_lock()) { + error(Error, Context); + dumpDIE(DIE, Options.Verbose); + ErrorHandlerMutex.unlock(); + } } ErrorOr @@ -233,9 +239,23 @@ if (ErrorOrObj) { Res = std::make_unique( - Obj.getObjectFilename(), DWARFContext::create(*ErrorOrObj), + Obj.getObjectFilename(), + DWARFContext::create( + *ErrorOrObj, DWARFContext::ProcessDebugRelocations::Process, + nullptr, "", + [&](Error Err) { + handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) { + reportError(Info.message()); + }); + }, + [&](Error Warning) { + handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) { + reportWarning(Info.message()); + }); + }), std::make_unique(*this, *ErrorOrObj, Obj), - Obj.empty() ? Obj.getWarnings() : EmptyWarnings); + Obj.empty() ? Obj.getWarnings() : EmptyWarnings, + [&](StringRef FileName) { BinHolder.eraseObjectEntry(FileName); }); Error E = RL.link(*ErrorOrObj); if (Error NewE = handleErrors( @@ -1036,8 +1056,10 @@ default: { assert(false && "Specified operation does not have address operand"); } break; + case dwarf::DW_OP_const2u: case dwarf::DW_OP_const4u: case dwarf::DW_OP_const8u: + case dwarf::DW_OP_const2s: case dwarf::DW_OP_const4s: case dwarf::DW_OP_const8s: case dwarf::DW_OP_addr: { diff --git a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp --- a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp +++ b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp @@ -98,15 +98,18 @@ return std::nullopt; } - std::optional getExprOpAddressRelocAdjustment( - DWARFUnit &U, const DWARFExpression::Operation &Op, uint64_t StartOffset, - uint64_t EndOffset) override { + std::optional + getExprOpAddressRelocAdjustment(DWARFUnit &U, + const DWARFExpression::Operation &Op, + uint64_t, uint64_t) override { switch (Op.getCode()) { default: { assert(false && "Specified operation does not have address operand"); } break; + case dwarf::DW_OP_const2u: case dwarf::DW_OP_const4u: case dwarf::DW_OP_const8u: + case dwarf::DW_OP_const2s: case dwarf::DW_OP_const4s: case dwarf::DW_OP_const8s: case dwarf::DW_OP_addr: { @@ -287,23 +290,33 @@ template Error linkDebugInfoImpl(object::ObjectFile &File, const Options &Options, raw_pwrite_stream &OutStream) { + std::mutex ErrorHandlerMutex; + auto ReportWarn = [&](const Twine &Message, StringRef Context, const DWARFDie *Die) { - warning(Message, Context); - - if (!Options.Verbose || !Die) + // FIXME: implement warning logging which does not block other threads. + if (!ErrorHandlerMutex.try_lock()) return; - DIDumpOptions DumpOpts; - DumpOpts.ChildRecurseDepth = 0; - DumpOpts.Verbose = Options.Verbose; + warning(Message, Context); + if (Options.Verbose && Die) { + DIDumpOptions DumpOpts; + DumpOpts.ChildRecurseDepth = 0; + DumpOpts.Verbose = Options.Verbose; - WithColor::note() << " in DIE:\n"; - Die->dump(errs(), /*Indent=*/6, DumpOpts); + WithColor::note() << " in DIE:\n"; + Die->dump(errs(), /*Indent=*/6, DumpOpts); + } + ErrorHandlerMutex.unlock(); }; auto ReportErr = [&](const Twine &Message, StringRef Context, const DWARFDie *) { + // FIXME: implement error logging which does not block other threads. + if (!ErrorHandlerMutex.try_lock()) + return; + WithColor::error(errs(), Context) << Message << '\n'; + ErrorHandlerMutex.unlock(); }; // Create DWARF linker. @@ -325,7 +338,18 @@ std::vector EmptyWarnings; // Add object files to the DWARFLinker. - std::unique_ptr Context = DWARFContext::create(File); + std::unique_ptr Context = DWARFContext::create( + File, DWARFContext::ProcessDebugRelocations::Process, nullptr, "", + [&](Error Err) { + handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) { + ReportErr(Info.message(), "", nullptr); + }); + }, + [&](Error Warning) { + handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) { + ReportWarn(Info.message(), "", nullptr); + }); + }); std::unique_ptr> AddressesMap( std::make_unique>(*Context, Options, File)); diff --git a/llvm/unittests/CodeGen/DwarfStringPoolEntryRefTest.cpp b/llvm/unittests/CodeGen/DwarfStringPoolEntryRefTest.cpp --- a/llvm/unittests/CodeGen/DwarfStringPoolEntryRefTest.cpp +++ b/llvm/unittests/CodeGen/DwarfStringPoolEntryRefTest.cpp @@ -61,40 +61,25 @@ } TEST(DwarfStringPoolEntryRefTest, TestShortEntry) { - BumpPtrAllocator Allocator; - DwarfStringPoolEntry DwarfEntry1 = {nullptr, 0, 0}; - StringMapEntry *StringEntry1 = - StringMapEntry::create("Key1", Allocator, - &DwarfEntry1); - - EXPECT_TRUE(StringEntry1->getKey() == "Key1"); - EXPECT_TRUE(StringEntry1->second->Symbol == nullptr); - EXPECT_TRUE(StringEntry1->second->Offset == 0); - EXPECT_TRUE(StringEntry1->second->Index == 0); + DwarfStringPoolEntryWithExtString DwarfEntry1 = {{nullptr, 0, 0}, "Key1"}; - DwarfStringPoolEntryRef Ref1(*StringEntry1); + DwarfStringPoolEntryRef Ref1(DwarfEntry1); EXPECT_TRUE(Ref1.getString() == "Key1"); EXPECT_TRUE(Ref1.getOffset() == 0); EXPECT_TRUE(Ref1.getIndex() == 0); EXPECT_TRUE(isEntryEqual(Ref1.getEntry(), DwarfEntry1)); - DwarfStringPoolEntryRef Ref2(*StringEntry1); + DwarfStringPoolEntryRef Ref2(DwarfEntry1); EXPECT_TRUE(Ref2.getString() == "Key1"); EXPECT_TRUE(Ref2.getOffset() == 0); EXPECT_TRUE(isEntryEqual(Ref2.getEntry(), DwarfEntry1)); EXPECT_TRUE(Ref1 == Ref2); EXPECT_FALSE(Ref1 != Ref2); - DwarfStringPoolEntry DwarfEntry2 = {nullptr, 0x1000, 1}; - StringMapEntry *StringEntry2 = - StringMapEntry::create("Key2", Allocator, - &DwarfEntry2); - EXPECT_TRUE(StringEntry2->getKey() == "Key2"); - EXPECT_TRUE(StringEntry2->second->Symbol == nullptr); - EXPECT_TRUE(StringEntry2->second->Offset == 0x1000); - EXPECT_TRUE(StringEntry2->second->Index == 1); + DwarfStringPoolEntryWithExtString DwarfEntry2 = {{nullptr, 0x1000, 1}, + "Key2"}; - DwarfStringPoolEntryRef Ref3(*StringEntry2); + DwarfStringPoolEntryRef Ref3(DwarfEntry2); EXPECT_TRUE(Ref3.getString() == "Key2"); EXPECT_TRUE(Ref3.getOffset() == 0x1000); EXPECT_TRUE(Ref3.getIndex() == 1); @@ -105,11 +90,8 @@ TEST(DwarfStringPoolEntryRefTest, CompareFullAndShort) { BumpPtrAllocator Allocator; - DwarfStringPoolEntry DwarfEntry1 = {nullptr, 0, 0}; - StringMapEntry *StringEntry1 = - StringMapEntry::create("Key1", Allocator, - &DwarfEntry1); - DwarfStringPoolEntryRef Ref1(*StringEntry1); + DwarfStringPoolEntryWithExtString DwarfEntry1 = {{nullptr, 0, 0}, "Key1"}; + DwarfStringPoolEntryRef Ref1(DwarfEntry1); StringMapEntry *StringEntry2 = StringMapEntry::create( diff --git a/llvm/unittests/DWARFLinkerParallel/CMakeLists.txt b/llvm/unittests/DWARFLinkerParallel/CMakeLists.txt --- a/llvm/unittests/DWARFLinkerParallel/CMakeLists.txt +++ b/llvm/unittests/DWARFLinkerParallel/CMakeLists.txt @@ -6,7 +6,6 @@ add_llvm_unittest(DWARFLinkerParallelTests DWARFLinkerTest.cpp StringPoolTest.cpp - StringTableTest.cpp ) target_link_libraries(DWARFLinkerParallelTests PRIVATE LLVMTestingSupport) diff --git a/llvm/unittests/DWARFLinkerParallel/StringPoolTest.cpp b/llvm/unittests/DWARFLinkerParallel/StringPoolTest.cpp --- a/llvm/unittests/DWARFLinkerParallel/StringPoolTest.cpp +++ b/llvm/unittests/DWARFLinkerParallel/StringPoolTest.cpp @@ -28,20 +28,17 @@ std::pair Entry = Strings.insert("test"); EXPECT_TRUE(Entry.second); EXPECT_TRUE(Entry.first->getKey() == "test"); - EXPECT_TRUE(Entry.first->second == nullptr); StringEntry *EntryPtr = Entry.first; Entry = Strings.insert("test"); EXPECT_FALSE(Entry.second); EXPECT_TRUE(Entry.first->getKey() == "test"); - EXPECT_TRUE(Entry.first->second == nullptr); EXPECT_TRUE(EntryPtr == Entry.first); Entry = Strings.insert("test2"); EXPECT_TRUE(Entry.second); EXPECT_TRUE(Entry.first->getKey() == "test2"); - EXPECT_TRUE(Entry.first->second == nullptr); EXPECT_TRUE(EntryPtr != Entry.first); }); } @@ -54,7 +51,6 @@ std::pair Entry = Strings.insert(std::to_string(Idx)); EXPECT_TRUE(Entry.second); EXPECT_TRUE(Entry.first->getKey() == std::to_string(Idx)); - EXPECT_TRUE(Entry.first->second == nullptr); }); // Check data. @@ -62,7 +58,6 @@ std::pair Entry = Strings.insert(std::to_string(Idx)); EXPECT_FALSE(Entry.second); EXPECT_TRUE(Entry.first->getKey() == std::to_string(Idx)); - EXPECT_TRUE(Entry.first->second == nullptr); }); } diff --git a/llvm/unittests/DWARFLinkerParallel/StringTableTest.cpp b/llvm/unittests/DWARFLinkerParallel/StringTableTest.cpp deleted file mode 100644 --- a/llvm/unittests/DWARFLinkerParallel/StringTableTest.cpp +++ /dev/null @@ -1,118 +0,0 @@ -//===- llvm/unittest/DWARFLinkerParallel/StringTableTest.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 "llvm/DWARFLinkerParallel/StringTable.h" -#include "llvm/Support/Parallel.h" -#include "gtest/gtest.h" -#include - -using namespace llvm; -using namespace dwarflinker_parallel; - -namespace { - -TEST(StringPoolTest, TestStringTable) { - struct StringDescription { - const char *Str = nullptr; - uint64_t Idx = 0; - uint64_t Offset = 0; - }; - - SmallVector InputStrings = { - {"first", 0, 0}, {"second", 1, 6}, {"third", 2, 13}}; - - StringPool Strings; - StringTable OutStrings(Strings, nullptr); - - // StringPool uses PerThreadBumpPtrAllocator which should be accessed from - // threads created by ThreadPoolExecutor. Use TaskGroup to run on - // ThreadPoolExecutor threads. - parallel::TaskGroup tg; - - tg.spawn([&]() { - // Check string insertion. - StringEntry *FirstPtr = Strings.insert(InputStrings[0].Str).first; - StringEntry *SecondPtr = Strings.insert(InputStrings[1].Str).first; - StringEntry *ThirdPtr = Strings.insert(InputStrings[2].Str).first; - - FirstPtr = OutStrings.add(FirstPtr); - SecondPtr = OutStrings.add(SecondPtr); - ThirdPtr = OutStrings.add(ThirdPtr); - - // Check fields of inserted strings. - EXPECT_TRUE(FirstPtr->getKey() == InputStrings[0].Str); - EXPECT_TRUE(FirstPtr->getValue()->Offset == InputStrings[0].Offset); - EXPECT_TRUE(FirstPtr->getValue()->Index == InputStrings[0].Idx); - - EXPECT_TRUE(SecondPtr->getKey() == InputStrings[1].Str); - EXPECT_TRUE(SecondPtr->getValue()->Offset == InputStrings[1].Offset); - EXPECT_TRUE(SecondPtr->getValue()->Index == InputStrings[1].Idx); - - EXPECT_TRUE(ThirdPtr->getKey() == InputStrings[2].Str); - EXPECT_TRUE(ThirdPtr->getValue()->Offset == InputStrings[2].Offset); - EXPECT_TRUE(ThirdPtr->getValue()->Index == InputStrings[2].Idx); - - // Check order enumerated strings. - uint64_t CurIdx = 0; - std::function checkStr = - [&](DwarfStringPoolEntryRef Entry) { - EXPECT_TRUE(Entry.getEntry().isIndexed()); - EXPECT_TRUE(Entry.getIndex() == CurIdx); - EXPECT_TRUE(Entry.getOffset() == InputStrings[CurIdx].Offset); - EXPECT_TRUE(Entry.getString() == InputStrings[CurIdx].Str); - - CurIdx++; - }; - - OutStrings.forEach(checkStr); - }); -} - -TEST(StringPoolTest, TestStringTableWithTranslator) { - std::string Word; - std::function TranslatorFunc = - [&](StringRef InputString) -> StringRef { - Word.clear(); - for (auto Sym : InputString) - Word.insert(Word.begin(), Sym); - Word += '0'; - return Word; - }; - - StringPool Strings; - StringTable OutStrings(Strings, TranslatorFunc); - - // StringPool uses PerThreadBumpPtrAllocator which should be accessed from - // threads created by ThreadPoolExecutor. Use TaskGroup to run on - // ThreadPoolExecutor threads. - parallel::TaskGroup tg; - - tg.spawn([&]() { - StringEntry *FirstPtr = Strings.insert("first").first; - StringEntry *SecondPtr = Strings.insert("second").first; - StringEntry *ThirdPtr = Strings.insert("third").first; - - FirstPtr = OutStrings.add(FirstPtr); - SecondPtr = OutStrings.add(SecondPtr); - ThirdPtr = OutStrings.add(ThirdPtr); - - EXPECT_TRUE(FirstPtr->getKey() == "tsrif0"); - EXPECT_TRUE(FirstPtr->getValue()->Offset == 0); - EXPECT_TRUE(FirstPtr->getValue()->Index == 0); - - EXPECT_TRUE(SecondPtr->getKey() == "dnoces0"); - EXPECT_TRUE(SecondPtr->getValue()->Offset == 7); - EXPECT_TRUE(SecondPtr->getValue()->Index == 1); - - EXPECT_TRUE(ThirdPtr->getKey() == "driht0"); - EXPECT_TRUE(ThirdPtr->getValue()->Offset == 15); - EXPECT_TRUE(ThirdPtr->getValue()->Index == 2); - }); -} - -} // anonymous namespace