Index: llvm/include/llvm/DebugInfo/DWARF/DWARFAttribute.h =================================================================== --- llvm/include/llvm/DebugInfo/DWARF/DWARFAttribute.h +++ llvm/include/llvm/DebugInfo/DWARF/DWARFAttribute.h @@ -42,6 +42,10 @@ return isValid(); } + /// Identifies DWARF attributes that may contain a reference to a + /// DWARF expression. + static bool mayHaveLocationDescription(dwarf::Attribute Attr); + void clear() { Offset = 0; ByteSize = 0; Index: llvm/lib/DebugInfo/DWARF/DWARFDie.cpp =================================================================== --- llvm/lib/DebugInfo/DWARF/DWARFDie.cpp +++ llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -316,9 +316,7 @@ else formValue.dump(OS, DumpOpts); } - } else if (Attr == DW_AT_location || Attr == DW_AT_frame_base || - Attr == DW_AT_data_member_location || - Attr == DW_AT_GNU_call_site_value) + } else if (DWARFAttribute::mayHaveLocationDescription(Attr)) dumpLocation(OS, formValue, U, sizeof(BaseIndent) + Indent + 4, DumpOpts); else formValue.dump(OS, DumpOpts); @@ -709,3 +707,39 @@ updateForIndex(*AbbrDecl, Index + 1); return *this; } + +bool DWARFAttribute::mayHaveLocationDescription(dwarf::Attribute Attr) { + switch (Attr) { + // From the DWARF specification. + case DW_AT_location: + case DW_AT_byte_size: + case DW_AT_bit_size: + case DW_AT_string_length: + case DW_AT_lower_bound: + case DW_AT_return_addr: + case DW_AT_bit_stride: + case DW_AT_upper_bound: + case DW_AT_count: + case DW_AT_data_member_location: + case DW_AT_frame_base: + case DW_AT_segment: + case DW_AT_static_link: + case DW_AT_use_location: + case DW_AT_vtable_elem_location: + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_byte_stride: + case DW_AT_rank: + case DW_AT_call_value: + case DW_AT_call_origin: + case DW_AT_call_target: + case DW_AT_call_target_clobbered: + case DW_AT_call_data_location: + case DW_AT_call_data_value: + // Extensions. + case DW_AT_GNU_call_site_value: + return true; + default: + return false; + } +} Index: llvm/test/tools/dsymutil/Inputs/op-convert.ll =================================================================== --- /dev/null +++ llvm/test/tools/dsymutil/Inputs/op-convert.ll @@ -0,0 +1,45 @@ +; ModuleID = 'dbg.ll' +source_filename = "dbg.c" +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx" + +; Function Attrs: noinline nounwind uwtable +define signext i8 @foo(i8 signext %x) #0 !dbg !7 { +entry: + call void @llvm.dbg.value(metadata i8 42, metadata !17, metadata !DIExpression(DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_stack_value)), !dbg !12 + call void @llvm.dbg.value(metadata i8 %x, metadata !11, metadata !DIExpression()), !dbg !12 + call void @llvm.dbg.value(metadata i8 %x, metadata !13, metadata !DIExpression(DW_OP_LLVM_convert, 8, DW_ATE_signed, DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_stack_value)), !dbg !15 + ret i8 %x, !dbg !16 +} + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +attributes #0 = { noinline nounwind uwtable } +attributes #1 = { nounwind readnone speculatable } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 9.0.0 (trunk 353791) (llvm/trunk 353801)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "dbg.c", directory: "/tmp", checksumkind: CSK_MD5, checksum: "2a034da6937f5b9cf6dd2d89127f57fd") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 9.0.0 (trunk 353791) (llvm/trunk 353801)"} +!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !8, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!8 = !DISubroutineType(types: !9) +!9 = !{!10, !10} +!10 = !DIBasicType(name: "signed char", size: 8, encoding: DW_ATE_signed_char) +!11 = !DILocalVariable(name: "x", arg: 1, scope: !7, file: !1, line: 1, type: !10) +!12 = !DILocation(line: 1, column: 29, scope: !7) +!13 = !DILocalVariable(name: "y", scope: !7, file: !1, line: 3, type: !14) +!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!15 = !DILocation(line: 3, column: 14, scope: !7) +!16 = !DILocation(line: 4, column: 3, scope: !7) +!17 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 3, type: !14) Index: llvm/test/tools/dsymutil/X86/op-convert.test =================================================================== --- /dev/null +++ llvm/test/tools/dsymutil/X86/op-convert.test @@ -0,0 +1,33 @@ +# REQUIRES: object-emission +# RUN: dsymutil -f -o %t --verify -oso-prepend-path=%p/../Inputs -y %s +# RUN: llvm-dwarfdump %t | FileCheck %s + +--- +triple: 'x86_64-apple-darwin' +objects: + - filename: op-convert.macho.x86_64 + symbols: + - { sym: _foo, objAddr: 0x0, binAddr: 0x1000, size: 0x4 } +... + + +CHECK: DW_TAG_base_type +CHECK-NEXT: DW_AT_name ("DW_ATE_signed_8") +CHECK-NEXT: DW_AT_encoding (DW_ATE_signed) +CHECK-NEXT: DW_AT_byte_size (0x01) + +CHECK: DW_TAG_base_type +CHECK-NEXT: DW_AT_name ("DW_ATE_signed_32") +CHECK-NEXT: DW_AT_encoding (DW_ATE_signed) +CHECK-NEXT: DW_AT_byte_size (0x04) + +CHECK: DW_TAG_variable +CHECK-NEXT: DW_AT_location ( +CHECK-NEXT: [0x0000000000001000, 0x0000000000001002): DW_OP_breg5 RDI+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_convert (0x0000002a) "DW_ATE_signed_8", DW_OP_convert (0x00000031) "DW_ATE_signed_32", DW_OP_stack_value +CHECK-NEXT: [0x0000000000001002, 0x0000000000001003): DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_convert (0x0000002a) "DW_ATE_signed_8", DW_OP_convert (0x00000031) "DW_ATE_signed_32", DW_OP_stack_value) +CHECK-NEXT: DW_AT_name ("y") + +CHECK: DW_TAG_variable +CHECK-NEXT: DW_AT_location (DW_OP_constu 0x2a, DW_OP_convert (0x00000031) "DW_ATE_signed_32", DW_OP_stack_value) +CHECK-NEXT: DW_AT_name ("c") + Index: llvm/tools/dsymutil/DwarfLinker.h =================================================================== --- llvm/tools/dsymutil/DwarfLinker.h +++ llvm/tools/dsymutil/DwarfLinker.h @@ -136,7 +136,7 @@ CompileUnit::DIEInfo &Info); bool applyValidRelocs(MutableArrayRef Data, uint32_t BaseOffset, - bool isLittleEndian); + bool IsLittleEndian); }; /// Keeps track of data associated with one object during linking. @@ -184,8 +184,8 @@ /// The return value indicates whether the DIE is incomplete. void lookForDIEsToKeep(RelocationManager &RelocMgr, RangesTy &Ranges, const UnitListTy &Units, const DWARFDie &DIE, - const DebugMapObject &DMO, CompileUnit &CU, - unsigned Flags); + const DebugMapObject &DMO, const DWARFObject &Obj, + CompileUnit &CU, unsigned Flags, bool IsLittleEndian); /// If this compile unit is really a skeleton CU that points to a /// clang module, register it in ClangModules and return true. @@ -200,7 +200,8 @@ UniquingStringPool &UniquingStringPoolStringPool, DeclContextTree &ODRContexts, uint64_t ModulesEndOffset, unsigned &UnitID, - unsigned Indent = 0, bool Quiet = false); + bool IsLittleEndian, unsigned Indent = 0, + bool Quiet = false); /// Recursively add the debug info in this clang module .pcm /// file (and all the modules imported by it in a bottom-up fashion) @@ -211,8 +212,8 @@ RangesTy &Ranges, OffsetsStringPool &OffsetsStringPool, UniquingStringPool &UniquingStringPool, DeclContextTree &ODRContexts, uint64_t ModulesEndOffset, - unsigned &UnitID, unsigned Indent = 0, - bool Quiet = false); + unsigned &UnitID, bool IsLittleEndian, + unsigned Indent = 0, bool Quiet = false); /// Flags passed to DwarfLinker::lookForDIEsToKeep enum TraversalFlags { @@ -224,18 +225,33 @@ TF_SkipPC = 1 << 5, ///< Skip all location attributes. }; + void scanExpressionDIERefs(RelocationManager &RelocMgr, RangesTy &Ranges, + const UnitListTy &Units, const DWARFDie &DIE, + DWARFExpression Expression, + const DebugMapObject &DMO, const DWARFObject &Obj, + CompileUnit &CU, bool IsLittleEndian); + + void scanExpressionDIERefs(RelocationManager &RelocMgr, RangesTy &Ranges, + const UnitListTy &Units, const DWARFDie &DIE, + const DWARFFormValue &AttrValue, + const DebugMapObject &DMO, const DWARFObject &Obj, + CompileUnit &CU, bool IsLittleEndian); + /// Mark the passed DIE as well as all the ones it depends on as kept. void keepDIEAndDependencies(RelocationManager &RelocMgr, RangesTy &Ranges, const UnitListTy &Units, const DWARFDie &DIE, CompileUnit::DIEInfo &MyInfo, - const DebugMapObject &DMO, CompileUnit &CU, - bool UseODR); + const DebugMapObject &DMO, const DWARFObject &Obj, + CompileUnit &CU, bool UseODR, + bool IsLittleEndian); unsigned shouldKeepDIE(RelocationManager &RelocMgr, RangesTy &Ranges, const DWARFDie &DIE, const DebugMapObject &DMO, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, unsigned Flags); + /// Check if a variable describing DIE should be kept. + /// \returns updated TraversalFlags. unsigned shouldKeepVariableDIE(RelocationManager &RelocMgr, const DWARFDie &DIE, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, unsigned Flags); @@ -286,14 +302,15 @@ DIE *cloneDIE(const DWARFDie &InputDIE, const DebugMapObject &DMO, CompileUnit &U, OffsetsStringPool &StringPool, int64_t PCOffset, uint32_t OutOffset, unsigned Flags, - DIE *Die = nullptr); + bool IsLittleEndian, DIE *Die = nullptr); /// Construct the output DIE tree by cloning the DIEs we /// chose to keep above. If there are no valid relocs, then there's /// nothing to clone/emit. void cloneAllCompileUnits(DWARFContext &DwarfContext, const DebugMapObject &DMO, RangesTy &Ranges, - OffsetsStringPool &StringPool); + OffsetsStringPool &StringPool, + bool IsLittleEndian); private: using AttributeSpec = DWARFAbbreviationDeclaration::AttributeSpec; @@ -335,7 +352,7 @@ OffsetsStringPool &StringPool, const DWARFFormValue &Val, const AttributeSpec AttrSpec, unsigned AttrSize, - AttributesInfo &AttrInfo); + AttributesInfo &AttrInfo, bool IsLittleEndian); /// Clone a string attribute described by \p AttrSpec and add /// it to \p Die. @@ -355,11 +372,23 @@ const DebugMapObject &DMO, CompileUnit &Unit); + /// Clone a DWARF expression that may be referencing another DIE. + void cloneExpression(DataExtractor &Data, DWARFExpression Expression, + const DebugMapObject &DMO, CompileUnit &Unit, + std::function PushByte); + + /// Clone a DWARF expression that may be referencing another DIE. + void cloneExpression(DIEValueList *Attr, ArrayRef Expr, + const DebugMapObject &DMO, CompileUnit &Unit, + bool IsLittleEndian); + /// Clone an attribute referencing another DIE and add /// it to \p Die. /// \returns the size of the new attribute. - unsigned cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec, - const DWARFFormValue &Val, unsigned AttrSize); + unsigned cloneBlockAttribute(DIE &Die, const DebugMapObject &DMO, + CompileUnit &Unit, AttributeSpec AttrSpec, + const DWARFFormValue &Val, unsigned AttrSize, + bool IsLittleEndian); /// Clone an attribute referencing another DIE and add /// it to \p Die. Index: llvm/tools/dsymutil/DwarfLinker.cpp =================================================================== --- llvm/tools/dsymutil/DwarfLinker.cpp +++ llvm/tools/dsymutil/DwarfLinker.cpp @@ -120,12 +120,9 @@ static DWARFDie resolveDIEReference(const DwarfLinker &Linker, const DebugMapObject &DMO, const UnitListTy &Units, - const DWARFFormValue &RefValue, + uint64_t RefOffset, const DWARFUnit &Unit, const DWARFDie &DIE, CompileUnit *&RefCU) { - assert(RefValue.isFormClass(DWARFFormValue::FC_Reference)); - uint64_t RefOffset = *RefValue.getAsReference(); - if ((RefCU = getUnitForOffset(Units, RefOffset))) if (const auto RefDie = RefCU->getOrigUnit().getDIEForOffset(RefOffset)) { // In a file with broken references, an attribute might point to a NULL @@ -138,6 +135,17 @@ return DWARFDie(); } +static DWARFDie resolveDIEReference(const DwarfLinker &Linker, + const DebugMapObject &DMO, + const UnitListTy &Units, + const DWARFFormValue &RefValue, + const DWARFUnit &Unit, const DWARFDie &DIE, + CompileUnit *&RefCU) { + assert(RefValue.isFormClass(DWARFFormValue::FC_Reference)); + uint64_t RefOffset = *RefValue.getAsReference(); + return resolveDIEReference(Linker, DMO, Units, RefOffset, Unit, DIE, RefCU); +} + /// \returns whether the passed \a Attr type might contain a DIE reference /// suitable for ODR uniquing. static bool isODRAttribute(uint16_t Attr) { @@ -585,7 +593,7 @@ MyInfo.InDebugMap = true; return Flags | TF_Keep; } - + Optional LocationIdx = Abbrev->findAttributeIndex(dwarf::DW_AT_location); if (!LocationIdx) @@ -706,6 +714,80 @@ return Flags; } +void DwarfLinker::scanExpressionDIERefs( + RelocationManager &RelocMgr, RangesTy &Ranges, const UnitListTy &Units, + const DWARFDie &DIE, DWARFExpression Expression, const DebugMapObject &DMO, + const DWARFObject &Obj, CompileUnit &CU, bool IsLittleEndian) { + uint32_t OpOffset = 0; + for (auto &Op : Expression) { + if (Op.getCode() == dwarf::DW_OP_convert) { + assert(OpOffset < Op.getEndOffset()); + uint64_t RefOffset = Op.getRawOperand(0); + CompileUnit *ReferencedCU; + auto RefDie = resolveDIEReference(*this, DMO, Units, RefOffset, + CU.getOrigUnit(), DIE, ReferencedCU); + if (!RefDie) + continue; + assert(ReferencedCU == &CU); + lookForDIEsToKeep(RelocMgr, Ranges, Units, RefDie, DMO, Obj, + *ReferencedCU, TF_Keep | TF_DependencyWalk, + IsLittleEndian); + bool UseODR = false; + uint32_t RefIdx = ReferencedCU->getOrigUnit().getDIEIndex(RefDie); + CompileUnit::DIEInfo &Info = ReferencedCU->getInfo(RefIdx); + keepDIEAndDependencies(RelocMgr, Ranges, Units, RefDie, Info, DMO, Obj, + CU, UseODR, IsLittleEndian); + } + OpOffset = Op.getEndOffset(); + } +} + +void DwarfLinker::scanExpressionDIERefs( + RelocationManager &RelocMgr, RangesTy &Ranges, const UnitListTy &Units, + const DWARFDie &DIE, const DWARFFormValue &AttrValue, + const DebugMapObject &DMO, const DWARFObject &Obj, CompileUnit &CU, + bool IsLittleEndian) { + + const DWARFUnit &Unit = CU.getOrigUnit(); + unsigned AddressSize = Unit.getAddressByteSize(); + + auto ScanExpressionData = [&](StringRef Data) { + DataExtractor DE(Data, IsLittleEndian, AddressSize); + scanExpressionDIERefs( + RelocMgr, Ranges, Units, DIE, + DWARFExpression(DE, Unit.getVersion(), Unit.getAddressByteSize()), DMO, + Obj, CU, IsLittleEndian); + }; + + if (AttrValue.isFormClass(DWARFFormValue::FC_Block) || + AttrValue.isFormClass(DWARFFormValue::FC_Exprloc)) { + ArrayRef Expr = *AttrValue.getAsBlock(); + ScanExpressionData(StringRef((const char *)Expr.data(), Expr.size())); + } else if (AttrValue.isFormClass(DWARFFormValue::FC_SectionOffset)) { + uint32_t Offset = *AttrValue.getAsSectionOffset(); + if (!Unit.isDWOUnit() && !Unit.getLocSection()->Data.empty()) { + DWARFDebugLoc DebugLoc; + DWARFDataExtractor Data(Obj, *Unit.getLocSection(), IsLittleEndian, + AddressSize); + if (auto LocList = DebugLoc.parseOneLocationList(Data, &Offset)) + for (const auto &E : LocList->Entries) + ScanExpressionData(StringRef(E.Loc.data(), E.Loc.size())); + } else { + bool UseLocLists = !Unit.isDWOUnit(); + StringRef LoclistsSectionData = UseLocLists + ? Obj.getLoclistsSection().Data + : Unit.getLocSectionData(); + if (!LoclistsSectionData.empty()) { + DataExtractor Data(LoclistsSectionData, IsLittleEndian, AddressSize); + if (auto LocList = DWARFDebugLoclists::parseOneLocationList( + Data, &Offset, UseLocLists ? Unit.getVersion() : 4)) + for (const auto &E : LocList->Entries) + ScanExpressionData(StringRef(E.Loc.data(), E.Loc.size())); + } + } + } +} + /// Mark the passed DIE as well as all the ones it depends on /// as kept. /// @@ -715,9 +797,10 @@ /// TraversalFlags to inform it that it's not doing the primary DIE /// tree walk. void DwarfLinker::keepDIEAndDependencies( - RelocationManager &RelocMgr, RangesTy &Ranges, const UnitListTy &Units, + RelocationManager & RelocMgr, RangesTy & Ranges, const UnitListTy &Units, const DWARFDie &Die, CompileUnit::DIEInfo &MyInfo, - const DebugMapObject &DMO, CompileUnit &CU, bool UseODR) { + const DebugMapObject &DMO, const DWARFObject &Obj, CompileUnit &CU, + bool UseODR, bool IsLittleEndian) { DWARFUnit &Unit = CU.getOrigUnit(); MyInfo.Keep = true; @@ -730,9 +813,9 @@ unsigned AncestorIdx = MyInfo.ParentIdx; while (!CU.getInfo(AncestorIdx).Keep) { unsigned ODRFlag = UseODR ? TF_ODR : 0; - lookForDIEsToKeep(RelocMgr, Ranges, Units, Unit.getDIEAtIndex(AncestorIdx), - DMO, CU, - TF_ParentWalk | TF_Keep | TF_DependencyWalk | ODRFlag); + lookForDIEsToKeep( + RelocMgr, Ranges, Units, Unit.getDIEAtIndex(AncestorIdx), DMO, Obj, CU, + TF_ParentWalk | TF_Keep | TF_DependencyWalk | ODRFlag, IsLittleEndian); AncestorIdx = CU.getInfo(AncestorIdx).ParentIdx; } @@ -746,6 +829,12 @@ for (const auto &AttrSpec : Abbrev->attributes()) { DWARFFormValue Val(AttrSpec.Form); + // Scan for DIE references in location descriptions. + if (DWARFAttribute::mayHaveLocationDescription(AttrSpec.Attr)) + scanExpressionDIERefs(RelocMgr, Ranges, Units, Die, + *Die.find(AttrSpec.Attr), DMO, Obj, CU, + IsLittleEndian); + if (!Val.isFormClass(DWARFFormValue::FC_Reference) || AttrSpec.Attr == dwarf::DW_AT_sibling) { DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, @@ -781,8 +870,9 @@ Info.Prune = false; unsigned ODRFlag = UseODR ? TF_ODR : 0; - lookForDIEsToKeep(RelocMgr, Ranges, Units, RefDie, DMO, *ReferencedCU, - TF_Keep | TF_DependencyWalk | ODRFlag); + lookForDIEsToKeep(RelocMgr, Ranges, Units, RefDie, DMO, Obj, + *ReferencedCU, TF_Keep | TF_DependencyWalk | ODRFlag, + IsLittleEndian); // The incomplete property is propagated if the current DIE is complete // but references an incomplete DIE. @@ -858,8 +948,9 @@ void DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr, RangesTy &Ranges, const UnitListTy &Units, const DWARFDie &Die, - const DebugMapObject &DMO, CompileUnit &CU, - unsigned Flags) { + const DebugMapObject &DMO, + const DWARFObject &Obj, CompileUnit &CU, + unsigned Flags, bool IsLittleEndian) { // LIFO work list. SmallVector Worklist; Worklist.emplace_back(Die, Flags); @@ -903,7 +994,7 @@ ? (Current.Flags & TF_ODR) : CU.hasODR(); keepDIEAndDependencies(RelocMgr, Ranges, Units, Current.Die, MyInfo, DMO, - CU, UseOdr); + Obj, CU, UseOdr, IsLittleEndian); } // The TF_ParentWalk flag tells us that we are currently walking up @@ -1052,10 +1143,62 @@ return AttrSize; } -unsigned DwarfLinker::DIECloner::cloneBlockAttribute(DIE &Die, - AttributeSpec AttrSpec, - const DWARFFormValue &Val, - unsigned AttrSize) { +void DwarfLinker::DIECloner::cloneExpression( + DataExtractor &Data, DWARFExpression Expression, const DebugMapObject &DMO, + CompileUnit &Unit, std::function PushByte) { + uint32_t OpOffset = 0; + for (auto &Op : Expression) { + if (Op.getCode() == dwarf::DW_OP_convert) { + assert(OpOffset < Op.getEndOffset()); + uint32_t ULEBsize = Op.getEndOffset() - OpOffset - 1; + assert(ULEBsize <= 16); + + uint64_t RefOffset = Op.getRawOperand(0); + auto RefDie = Unit.getOrigUnit().getDIEForOffset(RefOffset); + uint32_t RefIdx = Unit.getOrigUnit().getDIEIndex(RefDie); + CompileUnit::DIEInfo &Info = Unit.getInfo(RefIdx); + DIE *Clone = Info.Clone; + assert(Clone && "base type not created at beginning of CU"); + uint32_t Offset = Clone->getOffset(); + PushByte(dwarf::DW_OP_convert); + uint8_t Buffer[16]; + unsigned RealSize = encodeULEB128(Offset, Buffer, ULEBsize); + if (RealSize > ULEBsize) { + memset(Buffer, dwarf::DW_OP_nop, ULEBsize); + Linker.reportWarning("DW_OP_convert type ref doesn't fit.", DMO); + } + for (unsigned I = 0; I < ULEBsize; ++I) + PushByte(Buffer[I]); + } else { + // Copy over everything else unmodified. + for (unsigned I = OpOffset; I < Op.getEndOffset(); ++I) + PushByte(Data.getData()[I]); + } + OpOffset = Op.getEndOffset(); + } +} + +void DwarfLinker::DIECloner::cloneExpression(DIEValueList *Attr, + ArrayRef Expr, + const DebugMapObject &DMO, + CompileUnit &Unit, + bool IsLittleEndian) { + DWARFUnit &OrigUnit = Unit.getOrigUnit(); + DataExtractor Data(StringRef((const char *)Expr.data(), Expr.size()), + IsLittleEndian, OrigUnit.getAddressByteSize()); + cloneExpression(Data, + DWARFExpression(Data, OrigUnit.getVersion(), + OrigUnit.getAddressByteSize()), + DMO, Unit, [&](uint8_t Byte) { + Attr->addValue(DIEAlloc, static_cast(0), + dwarf::DW_FORM_data1, DIEInteger(Byte)); + }); +} + +unsigned DwarfLinker::DIECloner::cloneBlockAttribute( + DIE &Die, const DebugMapObject &DMO, CompileUnit &Unit, + AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize, + bool IsLittleEndian) { DIEValueList *Attr; DIEValue Value; DIELoc *Loc = nullptr; @@ -1078,9 +1221,15 @@ Value = DIEValue(dwarf::Attribute(AttrSpec.Attr), dwarf::Form(AttrSpec.Form), Block); ArrayRef Bytes = *Val.getAsBlock(); - for (auto Byte : Bytes) - Attr->addValue(DIEAlloc, static_cast(0), - dwarf::DW_FORM_data1, DIEInteger(Byte)); + if (DWARFAttribute::mayHaveLocationDescription(AttrSpec.Attr) && + (Val.isFormClass(DWARFFormValue::FC_Block) || + Val.isFormClass(DWARFFormValue::FC_Exprloc))) { + cloneExpression(Attr, Bytes, DMO, Unit, IsLittleEndian); + } else { + for (auto Byte : Bytes) + Attr->addValue(DIEAlloc, static_cast(0), + dwarf::DW_FORM_data1, DIEInteger(Byte)); + } // FIXME: If DIEBlock and DIELoc just reuses the Size field of // the DIE class, this if could be replaced by // Attr->setSize(Bytes.size()). @@ -1198,8 +1347,9 @@ // A more generic way to check for location attributes would be // nice, but it's very unlikely that any other attribute needs a // location list. + // FIXME: use DWARFAttribute::mayHaveLocationDescription(). else if (AttrSpec.Attr == dwarf::DW_AT_location || - AttrSpec.Attr == dwarf::DW_AT_frame_base) + AttrSpec.Attr == dwarf::DW_AT_frame_base) Unit.noteLocationAttribute(Patch, Info.PCOffset); else if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value) Info.IsDeclaration = true; @@ -1213,7 +1363,8 @@ unsigned DwarfLinker::DIECloner::cloneAttribute( DIE &Die, const DWARFDie &InputDIE, const DebugMapObject &DMO, CompileUnit &Unit, OffsetsStringPool &StringPool, const DWARFFormValue &Val, - const AttributeSpec AttrSpec, unsigned AttrSize, AttributesInfo &Info) { + const AttributeSpec AttrSpec, unsigned AttrSize, AttributesInfo &Info, + bool IsLittleEndian) { const DWARFUnit &U = Unit.getOrigUnit(); switch (AttrSpec.Form) { @@ -1232,7 +1383,8 @@ case dwarf::DW_FORM_block2: case dwarf::DW_FORM_block4: case dwarf::DW_FORM_exprloc: - return cloneBlockAttribute(Die, AttrSpec, Val, AttrSize); + return cloneBlockAttribute(Die, DMO, Unit, AttrSpec, Val, AttrSize, + IsLittleEndian); case dwarf::DW_FORM_addr: return cloneAddressAttribute(Die, AttrSpec, Val, Unit, Info); case dwarf::DW_FORM_data1: @@ -1264,7 +1416,7 @@ /// /// \returns whether any reloc has been applied. bool DwarfLinker::RelocationManager::applyValidRelocs( - MutableArrayRef Data, uint32_t BaseOffset, bool isLittleEndian) { + MutableArrayRef Data, uint32_t BaseOffset, bool IsLittleEndian) { assert((NextValidReloc == 0 || BaseOffset > ValidRelocs[NextValidReloc - 1].Offset) && "BaseOffset should only be increasing."); @@ -1288,7 +1440,7 @@ uint64_t Value = ValidReloc.Mapping->getValue().BinaryAddress; Value += ValidReloc.Addend; for (unsigned i = 0; i != ValidReloc.Size; ++i) { - unsigned Index = isLittleEndian ? i : (ValidReloc.Size - i - 1); + unsigned Index = IsLittleEndian ? i : (ValidReloc.Size - i - 1); Buf[i] = uint8_t(Value >> (Index * 8)); } assert(ValidReloc.Size <= sizeof(Buf)); @@ -1370,12 +1522,10 @@ } } -DIE *DwarfLinker::DIECloner::cloneDIE(const DWARFDie &InputDIE, - const DebugMapObject &DMO, - CompileUnit &Unit, - OffsetsStringPool &StringPool, - int64_t PCOffset, uint32_t OutOffset, - unsigned Flags, DIE *Die) { +DIE *DwarfLinker::DIECloner::cloneDIE( + const DWARFDie &InputDIE, const DebugMapObject &DMO, CompileUnit &Unit, + OffsetsStringPool &StringPool, int64_t PCOffset, uint32_t OutOffset, + unsigned Flags, bool IsLittleEndian, DIE *Die) { DWARFUnit &U = Unit.getOrigUnit(); unsigned Idx = U.getDIEIndex(InputDIE); CompileUnit::DIEInfo &Info = Unit.getInfo(Idx); @@ -1481,7 +1631,7 @@ AttrSize = Offset - AttrSize; OutOffset += cloneAttribute(*Die, InputDIE, DMO, Unit, StringPool, Val, - AttrSpec, AttrSize, AttrInfo); + AttrSpec, AttrSize, AttrInfo, IsLittleEndian); } // Look for accelerator entries. @@ -1556,7 +1706,7 @@ // Recursively clone children. for (auto Child : InputDIE.children()) { if (DIE *Clone = cloneDIE(Child, DMO, Unit, StringPool, PCOffset, OutOffset, - Flags)) { + Flags, IsLittleEndian)) { Die->addChild(Clone); OutOffset = Clone->getOffset() + Clone->getSize(); } @@ -2033,7 +2183,8 @@ const DWARFDie &CUDie, const DWARFUnit &Unit, DebugMap &ModuleMap, const DebugMapObject &DMO, RangesTy &Ranges, OffsetsStringPool &StringPool, UniquingStringPool &UniquingStringPool, DeclContextTree &ODRContexts, - uint64_t ModulesEndOffset, unsigned &UnitID, unsigned Indent, bool Quiet) { + uint64_t ModulesEndOffset, unsigned &UnitID, bool IsLittleEndian, + unsigned Indent, bool Quiet) { std::string PCMfile = dwarf::toString( CUDie.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), ""); if (PCMfile.empty()) @@ -2075,10 +2226,10 @@ // 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, DwoId}); - if (Error E = - loadClangModule(PCMfile, PCMpath, Name, DwoId, ModuleMap, DMO, Ranges, - StringPool, UniquingStringPool, ODRContexts, - ModulesEndOffset, UnitID, Indent + 2, Quiet)) { + if (Error E = loadClangModule(PCMfile, PCMpath, Name, DwoId, ModuleMap, DMO, + Ranges, StringPool, UniquingStringPool, + ODRContexts, ModulesEndOffset, UnitID, + IsLittleEndian, Indent + 2, Quiet)) { consumeError(std::move(E)); return false; } @@ -2112,7 +2263,8 @@ uint64_t DwoId, DebugMap &ModuleMap, const DebugMapObject &DMO, RangesTy &Ranges, OffsetsStringPool &StringPool, UniquingStringPool &UniquingStringPool, DeclContextTree &ODRContexts, - uint64_t ModulesEndOffset, unsigned &UnitID, unsigned Indent, bool Quiet) { + uint64_t ModulesEndOffset, unsigned &UnitID, bool IsLittleEndian, + unsigned Indent, bool Quiet) { SmallString<80> Path(Options.PrependPath); if (sys::path::is_relative(Filename)) sys::path::append(Path, ModulePath, Filename); @@ -2174,7 +2326,8 @@ continue; if (!registerModuleReference(CUDie, *CU, ModuleMap, DMO, Ranges, StringPool, UniquingStringPool, ODRContexts, - ModulesEndOffset, UnitID, Indent, Quiet)) { + ModulesEndOffset, UnitID, IsLittleEndian, + Indent, Quiet)) { if (Unit) { std::string Err = (Filename + @@ -2218,13 +2371,14 @@ UnitListTy CompileUnits; CompileUnits.push_back(std::move(Unit)); DIECloner(*this, RelocMgr, DIEAlloc, CompileUnits, Options) - .cloneAllCompileUnits(*DwarfContext, DMO, Ranges, StringPool); + .cloneAllCompileUnits(*DwarfContext, DMO, Ranges, StringPool, + IsLittleEndian); return Error::success(); } void DwarfLinker::DIECloner::cloneAllCompileUnits( DWARFContext &DwarfContext, const DebugMapObject &DMO, RangesTy &Ranges, - OffsetsStringPool &StringPool) { + OffsetsStringPool &StringPool, bool IsLittleEndian) { if (!Linker.Streamer) return; @@ -2240,7 +2394,8 @@ // already has a DIE inside of it. CurrentUnit->createOutputDIE(); cloneDIE(InputDIE, DMO, *CurrentUnit, StringPool, 0 /* PC offset */, - 11 /* Unit Header size */, 0, CurrentUnit->getOutputUnitDIE()); + 11 /* Unit Header size */, 0, IsLittleEndian, + CurrentUnit->getOutputUnitDIE()); } Linker.OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset(); @@ -2260,7 +2415,17 @@ continue; Linker.patchRangesForUnit(*CurrentUnit, DwarfContext, DMO); - Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext); + auto ProcessExpr = [&](StringRef Bytes, std::vector &Buffer) { + DWARFUnit &OrigUnit = CurrentUnit->getOrigUnit(); + DataExtractor Data(Bytes, IsLittleEndian, OrigUnit.getAddressByteSize()); + cloneExpression(Data, + DWARFExpression(Data, OrigUnit.getVersion(), + OrigUnit.getAddressByteSize()), + DMO, *CurrentUnit, + [&](uint8_t Byte) { Buffer.push_back(Byte); }); + }; + Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext, + ProcessExpr); } if (Linker.Options.NoOutput) @@ -2490,7 +2655,8 @@ if (CUDie && !LLVM_UNLIKELY(Options.Update)) registerModuleReference(CUDie, *CU, ModuleMap, LinkContext.DMO, LinkContext.Ranges, OffsetsStringPool, - UniquingStringPool, ODRContexts, 0, UnitID); + UniquingStringPool, ODRContexts, 0, UnitID, + LinkContext.DwarfContext->isLittleEndian()); } } @@ -2569,10 +2735,11 @@ Streamer->copyInvariantDebugSection(*LinkContext.ObjectFile); } else { for (auto &CurrentUnit : LinkContext.CompileUnits) - lookForDIEsToKeep(LinkContext.RelocMgr, LinkContext.Ranges, - LinkContext.CompileUnits, - CurrentUnit->getOrigUnit().getUnitDIE(), - LinkContext.DMO, *CurrentUnit, 0); + lookForDIEsToKeep( + LinkContext.RelocMgr, LinkContext.Ranges, LinkContext.CompileUnits, + CurrentUnit->getOrigUnit().getUnitDIE(), LinkContext.DMO, + LinkContext.DwarfContext->getDWARFObj(), *CurrentUnit, 0, + LinkContext.DwarfContext->isLittleEndian()); } // The calls to applyValidRelocs inside cloneDIE will walk the reloc @@ -2583,7 +2750,8 @@ DIECloner(*this, LinkContext.RelocMgr, DIEAlloc, LinkContext.CompileUnits, Options) .cloneAllCompileUnits(*LinkContext.DwarfContext, LinkContext.DMO, - LinkContext.Ranges, OffsetsStringPool); + LinkContext.Ranges, OffsetsStringPool, + LinkContext.DwarfContext->isLittleEndian()); if (!Options.NoOutput && !LinkContext.CompileUnits.empty() && LLVM_LIKELY(!Options.Update)) patchFrameInfoForObject( Index: llvm/tools/dsymutil/DwarfStreamer.h =================================================================== --- llvm/tools/dsymutil/DwarfStreamer.h +++ llvm/tools/dsymutil/DwarfStreamer.h @@ -95,7 +95,9 @@ /// Emit the debug_loc contribution for \p Unit by copying the entries from /// \p Dwarf and offsetting them. Update the location attributes to point to /// the new entries. - void emitLocationsForUnit(const CompileUnit &Unit, DWARFContext &Dwarf); + void emitLocationsForUnit( + const CompileUnit &Unit, DWARFContext &Dwarf, + std::function &)> ProcessExpr); /// Emit the line table described in \p Rows into the debug_line section. void emitLineTableForUnit(MCDwarfLineTableParams Params, Index: llvm/tools/dsymutil/DwarfStreamer.cpp =================================================================== --- llvm/tools/dsymutil/DwarfStreamer.cpp +++ llvm/tools/dsymutil/DwarfStreamer.cpp @@ -384,8 +384,9 @@ /// Emit location lists for \p Unit and update attributes to point to the new /// entries. -void DwarfStreamer::emitLocationsForUnit(const CompileUnit &Unit, - DWARFContext &Dwarf) { +void DwarfStreamer::emitLocationsForUnit( + const CompileUnit &Unit, DWARFContext &Dwarf, + std::function &)> ProcessExpr) { const auto &Attributes = Unit.getLocationAttributes(); if (Attributes.empty()) @@ -402,6 +403,7 @@ if (auto OrigLowPc = dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc))) UnitPcOffset = int64_t(*OrigLowPc) - Unit.getLowPc(); + std::vector Buffer; for (const auto &Attr : Attributes) { uint32_t Offset = Attr.first.get(); Attr.first.set(LocSectionSize); @@ -421,9 +423,13 @@ Asm->OutStreamer->EmitIntValue(High + LocPcOffset, AddressSize); uint64_t Length = Data.getU16(&Offset); Asm->OutStreamer->EmitIntValue(Length, 2); - // Just copy the bytes over. + // Copy the bytes int to the buffer, process them, emit them. + Buffer.reserve(Length); + Buffer.resize(0); + StringRef Input = InputSec.Data.substr(Offset, Length); + ProcessExpr(Input, Buffer); Asm->OutStreamer->EmitBytes( - StringRef(InputSec.Data.substr(Offset, Length))); + StringRef((const char *)Buffer.data(), Length)); Offset += Length; LocSectionSize += Length + 2; }