| //=== 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/DWARFAcceleratorTable.h" |
| #include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h" |
| #include "llvm/Support/DJB.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() { |
| // Nothing to reset if stage is less than "Loaded". |
| if (getStage() < Stage::Loaded) |
| return; |
| |
| // Note: We need to do erasing for "Loaded" stage because |
| // if live analysys failed then we will have "Loaded" stage |
| // with marking from "LivenessAnalysisDone" stage partially |
| // done. That marking should be cleared. |
| |
| for (DIEInfo &Info : DieInfoArray) |
| Info.unsetFlagsWhichSetDuringLiveAnalysis(); |
| |
| LowPc = std::nullopt; |
| HighPc = 0; |
| Labels.clear(); |
| Ranges.clear(); |
| |
| if (getStage() < Stage::Cloned) { |
| setStage(Stage::Loaded); |
| return; |
| } |
| |
| AcceleratorRecords.erase(); |
| AbbreviationsSet.clear(); |
| Abbreviations.clear(); |
| OutUnitDIE = nullptr; |
| DebugAddrIndexMap.clear(); |
| |
| 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<StringEntry *>::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<DIEInfo>(); |
| OutDieOffsetArray = SmallVector<uint64_t>(); |
| 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<DWARFFormValue> Val = find(DieEntry, dwarf::DW_AT_name)) { |
| Expected<const char *> 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() { |
| if (std::optional<SectionDescriptor *> DebugInfoSection = |
| tryGetSectionDescriptor(DebugSectionKind::DebugInfo)) { |
| |
| (*DebugInfoSection) |
| ->ListDebugDieRefPatch.forEach([](DebugDieRefPatch &Patch) { |
| /// Replace stored DIE indexes with DIE output offsets. |
| Patch.RefDieIdxOrClonedOffset = |
| Patch.RefCU.getPointer()->getDieOutOffset( |
| Patch.RefDieIdxOrClonedOffset); |
| }); |
| |
| (*DebugInfoSection) |
| ->ListDebugULEB128DieRefPatch.forEach( |
| [](DebugULEB128DieRefPatch &Patch) { |
| /// Replace stored DIE indexes with DIE output offsets. |
| Patch.RefDieIdxOrClonedOffset = |
| Patch.RefCU.getPointer()->getDieOutOffset( |
| Patch.RefDieIdxOrClonedOffset); |
| }); |
| } |
| |
| if (std::optional<SectionDescriptor *> DebugLocSection = |
| tryGetSectionDescriptor(DebugSectionKind::DebugLoc)) { |
| (*DebugLocSection) |
| ->ListDebugULEB128DieRefPatch.forEach( |
| [](DebugULEB128DieRefPatch &Patch) { |
| /// Replace stored DIE indexes with DIE output offsets. |
| Patch.RefDieIdxOrClonedOffset = |
| Patch.RefCU.getPointer()->getDieOutOffset( |
| Patch.RefDieIdxOrClonedOffset); |
| }); |
| } |
| |
| if (std::optional<SectionDescriptor *> DebugLocListsSection = |
| tryGetSectionDescriptor(DebugSectionKind::DebugLocLists)) { |
| (*DebugLocListsSection) |
| ->ListDebugULEB128DieRefPatch.forEach( |
| [](DebugULEB128DieRefPatch &Patch) { |
| /// Replace stored DIE indexes with DIE output offsets. |
| Patch.RefDieIdxOrClonedOffset = |
| Patch.RefCU.getPointer()->getDieOutOffset( |
| Patch.RefDieIdxOrClonedOffset); |
| }); |
| } |
| } |
| |
| std::optional<std::pair<CompileUnit *, uint32_t>> |
| CompileUnit::resolveDIEReference( |
| const DWARFFormValue &RefValue, |
| ResolveInterCUReferencesMode CanResolveInterCUReferences) { |
| if (std::optional<DWARFFormValue::UnitOffset> Ref = |
| *RefValue.getAsRelativeReference()) { |
| if (Ref->Unit != nullptr) { |
| // Referenced DIE is in current compile unit. |
| |
| if (std::optional<uint32_t> RefDieIdx = |
| getDIEIndexForOffset(Ref->Unit->getOffset() + Ref->Offset)) |
| return std::make_pair(this, *RefDieIdx); |
| } |
| |
| if (CompileUnit *RefCU = getUnitFromOffset(Ref->Offset)) { |
| if (RefCU->getUniqueID() == getUniqueID()) { |
| // Referenced DIE is in current compile unit. |
| if (std::optional<uint32_t> RefDieIdx = |
| getDIEIndexForOffset(Ref->Offset)) |
| return std::make_pair(this, *RefDieIdx); |
| } else if (CanResolveInterCUReferences) { |
| // 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<uint32_t> RefDieIdx = |
| RefCU->getDIEIndexForOffset(Ref->Offset)) |
| return std::make_pair(RefCU, *RefDieIdx); |
| } else |
| return std::make_pair(RefCU, 0); |
| } |
| } |
| |
| return std::nullopt; |
| } |
| |
| void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc, |
| int64_t PcOffset) { |
| std::lock_guard<std::mutex> 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<std::mutex> Guard(LabelsMutex); |
| Labels.insert({LabelLowPc, PcOffset}); |
| } |
| |
| Error CompileUnit::cloneAndEmitDebugLocations() { |
| if (getGlobalData().getOptions().UpdateIndexTablesOnly) |
| return Error::success(); |
| |
| if (getOrigUnit().getVersion() < 5) { |
| emitLocations(DebugSectionKind::DebugLoc); |
| return Error::success(); |
| } |
| |
| emitLocations(DebugSectionKind::DebugLocLists); |
| return Error::success(); |
| } |
| |
| void CompileUnit::emitLocations(DebugSectionKind LocationSectionKind) { |
| SectionDescriptor &DebugInfoSection = |
| getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo); |
| |
| if (!DebugInfoSection.ListDebugLocPatch.empty()) { |
| SectionDescriptor &OutLocationSection = |
| getOrCreateSectionDescriptor(LocationSectionKind); |
| 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<DWARFLocationExpressionsVector> 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<uint64_t> 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<uint64_t> 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_addressx, 1); |
| encodeULEB128(DebugAddrIndexMap.getValueIndex(*BaseAddress), |
| OutLocationSection.OS); |
| } |
| |
| // 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::emitDebugAddrSection() { |
| if (GlobalData.getOptions().UpdateIndexTablesOnly) |
| return Error::success(); |
| |
| if (getVersion() < 5) |
| return Error::success(); |
| |
| if (DebugAddrIndexMap.empty()) |
| return Error::success(); |
| |
| SectionDescriptor &OutAddrSection = |
| getOrCreateSectionDescriptor(DebugSectionKind::DebugAddr); |
| |
| // Emit section header. |
| |
| // Emit length. |
| OutAddrSection.emitUnitLength(0xBADDEF); |
| uint64_t OffsetAfterSectionLength = OutAddrSection.OS.tell(); |
| |
| // Emit version. |
| OutAddrSection.emitIntVal(5, 2); |
| |
| // Emit address size. |
| OutAddrSection.emitIntVal(getFormParams().AddrSize, 1); |
| |
| // Emit segment size. |
| OutAddrSection.emitIntVal(0, 1); |
| |
| // Emit addresses. |
| for (uint64_t AddrValue : DebugAddrIndexMap.getValues()) |
| OutAddrSection.emitIntVal(AddrValue, getFormParams().AddrSize); |
| |
| // Patch section length. |
| OutAddrSection.apply( |
| OffsetAfterSectionLength - |
| OutAddrSection.getFormParams().getDwarfOffsetByteSize(), |
| dwarf::DW_FORM_sec_offset, |
| OutAddrSection.OS.tell() - OffsetAfterSectionLength); |
| |
| return Error::success(); |
| } |
| |
| Error CompileUnit::emitDebugStringOffsetSection() { |
| if (getVersion() < 5) |
| return Error::success(); |
| |
| if (DebugStringIndexMap.empty()) |
| return Error::success(); |
| |
| SectionDescriptor &OutDebugStrOffsetsSection = |
| getOrCreateSectionDescriptor(DebugSectionKind::DebugStrOffsets); |
| |
| // Emit section header. |
| |
| // Emit length. |
| OutDebugStrOffsetsSection.emitUnitLength(0xBADDEF); |
| uint64_t OffsetAfterSectionLength = OutDebugStrOffsetsSection.OS.tell(); |
| |
| // Emit version. |
| OutDebugStrOffsetsSection.emitIntVal(5, 2); |
| |
| // Emit padding. |
| OutDebugStrOffsetsSection.emitIntVal(0, 2); |
| |
| // Emit index to offset map. |
| for (const StringEntry *String : DebugStringIndexMap.getValues()) { |
| // Note patch for string offset value. |
| OutDebugStrOffsetsSection.notePatch( |
| DebugStrPatch{{OutDebugStrOffsetsSection.OS.tell()}, String}); |
| |
| // Emit placeholder for offset value. |
| OutDebugStrOffsetsSection.emitOffset(0xBADDEF); |
| } |
| |
| // Patch section length. |
| OutDebugStrOffsetsSection.apply( |
| OffsetAfterSectionLength - |
| OutDebugStrOffsetsSection.getFormParams().getDwarfOffsetByteSize(), |
| dwarf::DW_FORM_sec_offset, |
| OutDebugStrOffsetsSection.OS.tell() - OffsetAfterSectionLength); |
| |
| return Error::success(); |
| } |
| |
| 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(LinkedFunctionRanges); |
| |
| if (getOrigUnit().getVersion() < 5) { |
| cloneAndEmitRangeList(DebugSectionKind::DebugRange, LinkedFunctionRanges); |
| return Error::success(); |
| } |
| |
| cloneAndEmitRangeList(DebugSectionKind::DebugRngLists, LinkedFunctionRanges); |
| return Error::success(); |
| } |
| |
| void CompileUnit::cloneAndEmitRangeList(DebugSectionKind RngSectionKind, |
| AddressRanges &LinkedFunctionRanges) { |
| SectionDescriptor &DebugInfoSection = |
| getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo); |
| SectionDescriptor &OutRangeSection = |
| getOrCreateSectionDescriptor(RngSectionKind); |
| |
| if (!DebugInfoSection.ListDebugRangePatch.empty()) { |
| std::optional<AddressRangeValuePair> 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<DWARFAddressRangesVector> 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<uint64_t> 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; |
| } |
| |
| std::optional<uint64_t> BaseAddress; |
| for (const AddressRange &Range : LinkedRanges) { |
| if (!BaseAddress) { |
| BaseAddress = Range.start(); |
| |
| // Emit base address. |
| OutRangeSection.emitIntVal(dwarf::DW_RLE_base_addressx, 1); |
| encodeULEB128(getDebugAddrIndex(*BaseAddress), OutRangeSection.OS); |
| } |
| |
| // Emit type of entry. |
| OutRangeSection.emitIntVal(dwarf::DW_RLE_offset_pair, 1); |
| |
| // Emit start offset relative to base address. |
| encodeULEB128(Range.start() - *BaseAddress, OutRangeSection.OS); |
| |
| // Emit end offset relative to base address. |
| encodeULEB128(Range.end() - *BaseAddress, OutRangeSection.OS); |
| } |
| |
| // Emit the terminator entry. |
| OutRangeSection.emitIntVal(dwarf::DW_RLE_end_of_list, 1); |
| } |
| |
| void CompileUnit::emitAranges(AddressRanges &LinkedFunctionRanges) { |
| if (LinkedFunctionRanges.empty()) |
| return; |
| |
| SectionDescriptor &DebugInfoSection = |
| getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo); |
| SectionDescriptor &OutArangesSection = |
| getOrCreateSectionDescriptor(DebugSectionKind::DebugARanges); |
| |
| // 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<uint64_t> MacroAttr = |
| dwarf::toSectionOffset(OrigUnitDie.find(dwarf::DW_AT_macros))) { |
| if (const DWARFDebugMacro *Table = |
| getContaingFile().Dwarf->getDebugMacro()) { |
| emitMacroTableImpl(Table, *MacroAttr, true); |
| } |
| } |
| |
| // Check for .debug_macinfo table. |
| if (std::optional<uint64_t> MacroAttr = |
| dwarf::toSectionOffset(OrigUnitDie.find(dwarf::DW_AT_macro_info))) { |
| if (const DWARFDebugMacro *Table = |
| getContaingFile().Dwarf->getDebugMacinfo()) { |
| emitMacroTableImpl(Table, *MacroAttr, false); |
| } |
| } |
| |
| return Error::success(); |
| } |
| |
| void CompileUnit::emitMacroTableImpl(const DWARFDebugMacro *MacroTable, |
| uint64_t OffsetToMacroTable, |
| bool hasDWARFv5Header) { |
| SectionDescriptor &OutSection = |
| hasDWARFv5Header |
| ? getOrCreateSectionDescriptor(DebugSectionKind::DebugMacro) |
| : getOrCreateSectionDescriptor(DebugSectionKind::DebugMacinfo); |
| |
| 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. |
| OutSection.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<uint64_t> 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. |
| OutSection.emitIntVal(Flags, sizeof(Flags)); |
| |
| // Write offset to line table. |
| if (StmtListOffset) { |
| OutSection.notePatch(DebugOffsetPatch{ |
| OutSection.OS.tell(), |
| &getOrCreateSectionDescriptor(DebugSectionKind::DebugLine)}); |
| // TODO: check that List.Header.getOffsetByteSize() and |
| // DebugOffsetPatch agree on size. |
| OutSection.emitIntVal(0xBADDEF, List.Header.getOffsetByteSize()); |
| } |
| } |
| |
| // Write macro entries. |
| for (const DWARFDebugMacro::Entry &MacroEntry : List.Macros) { |
| if (MacroEntry.Type == 0) { |
| encodeULEB128(MacroEntry.Type, OutSection.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. |
| OutSection.emitIntVal(MacroType, 1); |
| |
| // Write vendor extension constant. |
| encodeULEB128(MacroEntry.ExtConstant, OutSection.OS); |
| |
| // Write vendor extension string. |
| OutSection.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. |
| OutSection.emitIntVal(MacroType, 1); |
| |
| // Write source line. |
| encodeULEB128(MacroEntry.Line, OutSection.OS); |
| |
| // Write macro string. |
| OutSection.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. |
| OutSection.emitIntVal(MacroType, 1); |
| |
| // Write source line. |
| encodeULEB128(MacroEntry.Line, OutSection.OS); |
| |
| // Write macro string. |
| OutSection.emitString(dwarf::DW_FORM_strp, MacroEntry.MacroStr); |
| break; |
| } |
| case dwarf::DW_MACRO_start_file: { |
| // Write macinfo type. |
| OutSection.emitIntVal(MacroType, 1); |
| // Write source line. |
| encodeULEB128(MacroEntry.Line, OutSection.OS); |
| // Write source file id. |
| encodeULEB128(MacroEntry.File, OutSection.OS); |
| } break; |
| case dwarf::DW_MACRO_end_file: { |
| // Write macinfo type. |
| OutSection.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<uint8_t> &OutputExpression, SectionDescriptor &Section, |
| std::optional<int64_t> 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<uint32_t> 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<uint8_t> ULEBbytes(ULEB, ULEBsize); |
| OutputExpression.append(ULEBbytes.begin(), ULEBbytes.end()); |
| } else if (!getGlobalData().getOptions().UpdateIndexTablesOnly && |
| Op.getCode() == dwarf::DW_OP_addrx) { |
| if (std::optional<object::SectionedAddress> 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<uint8_t> AddressBytes( |
| reinterpret_cast<const uint8_t *>(&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<object::SectionedAddress> 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<uint8_t> 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<uint8_t> AddressBytes( |
| reinterpret_cast<const uint8_t *>(&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<Triple> TargetTriple) { |
| BumpPtrAllocator Allocator; |
| |
| DWARFDie OrigUnitDIE = getOrigUnit().getUnitDIE(); |
| if (!OrigUnitDIE.isValid()) |
| return Error::success(); |
| |
| // Clone input DIE entry recursively. |
| DIE *OutCUDie = |
| cloneDIE(OrigUnitDIE.getDebugInfoEntry(), getDebugInfoHeaderSize(), |
| 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; |
| |
| if (Error Err = emitDebugAddrSection()) |
| return Err; |
| |
| // Generate Pub accelerator tables. |
| if (llvm::is_contained(GlobalData.getOptions().AccelTables, |
| DWARFLinker::AccelTableKind::Pub)) |
| emitPubAccelerators(); |
| |
| if (Error Err = emitDebugStringOffsetSection()) |
| 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<int64_t> FuncAddressAdjustment, |
| std::optional<int64_t> 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<bool, std::optional<int64_t>> 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(); |
| |
| // Remember accelerator info. |
| rememberAcceleratorEntries(InputDieEntry, OutOffset, |
| AttributesCloner.AttrInfo); |
| |
| 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<DWARFDebugLine::Row> NewRows; |
| NewRows.reserve(InputLineTable->Rows.size()); |
| |
| // Current sequence of rows being extracted, before being inserted |
| // in NewRows. |
| std::vector<DWARFDebugLine::Row> Seq; |
| |
| const auto &FunctionRanges = getFunctionRanges(); |
| std::optional<AddressRangeValuePair> 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<DWARFDebugLine::Row> &Seq, |
| std::vector<DWARFDebugLine::Row> &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) |
| |
| static uint32_t hashFullyQualifiedName(CompileUnit *InputCU, DWARFDie &InputDIE, |
| int ChildRecurseDepth = 0) { |
| const char *Name = nullptr; |
| CompileUnit *CU = InputCU; |
| std::optional<DWARFFormValue> RefVal; |
| |
| // Usually name`s depth does not exceed 3. Set maximal depth |
| // to 1000 here, to avoid infinite loop in case incorrect input |
| // DWARF. |
| size_t MaxNameDepth = 1000; |
| size_t CurNameDepth = 0; |
| |
| while (CurNameDepth++ < MaxNameDepth) { |
| if (const char *CurrentName = InputDIE.getName(DINameKind::ShortName)) |
| Name = CurrentName; |
| |
| if (!(RefVal = InputDIE.find(dwarf::DW_AT_specification)) && |
| !(RefVal = InputDIE.find(dwarf::DW_AT_abstract_origin))) |
| break; |
| |
| if (!RefVal->isFormClass(DWARFFormValue::FC_Reference)) |
| break; |
| |
| std::optional<std::pair<CompileUnit *, uint32_t>> RefDie = |
| CU->resolveDIEReference(*RefVal, ResolveInterCUReferencesMode::Resolve); |
| if (!RefDie) |
| break; |
| |
| assert(RefDie->second != 0); |
| |
| CU = RefDie->first; |
| InputDIE = RefDie->first->getDIEAtIndex(RefDie->second); |
| } |
| |
| if (!Name && InputDIE.getTag() == dwarf::DW_TAG_namespace) |
| Name = "(anonymous namespace)"; |
| |
| DWARFDie ParentDie = InputDIE.getParent(); |
| if (!ParentDie.isValid() || ParentDie.getTag() == dwarf::DW_TAG_compile_unit) |
| return djbHash(Name ? Name : "", djbHash(ChildRecurseDepth ? "" : "::")); |
| |
| return djbHash( |
| (Name ? Name : ""), |
| djbHash((Name ? "::" : ""), |
| hashFullyQualifiedName(CU, ParentDie, ++ChildRecurseDepth))); |
| } |
| |
| void CompileUnit::rememberAcceleratorEntries( |
| const DWARFDebugInfoEntry *InputDieEntry, uint64_t OutOffset, |
| AttributesInfo &AttrInfo) { |
| if (GlobalData.getOptions().AccelTables.empty()) |
| return; |
| |
| DWARFDie InputDIE = getDIE(InputDieEntry); |
| |
| // Look for short name recursively if short name is not known yet. |
| if (AttrInfo.Name == nullptr) |
| if (const char *ShortName = InputDIE.getShortName()) |
| AttrInfo.Name = getGlobalData().getStringPool().insert(ShortName).first; |
| |
| switch (InputDieEntry->getTag()) { |
| case dwarf::DW_TAG_array_type: |
| case dwarf::DW_TAG_class_type: |
| case dwarf::DW_TAG_enumeration_type: |
| case dwarf::DW_TAG_pointer_type: |
| case dwarf::DW_TAG_reference_type: |
| case dwarf::DW_TAG_string_type: |
| case dwarf::DW_TAG_structure_type: |
| case dwarf::DW_TAG_subroutine_type: |
| case dwarf::DW_TAG_typedef: |
| case dwarf::DW_TAG_union_type: |
| case dwarf::DW_TAG_ptr_to_member_type: |
| case dwarf::DW_TAG_set_type: |
| case dwarf::DW_TAG_subrange_type: |
| case dwarf::DW_TAG_base_type: |
| case dwarf::DW_TAG_const_type: |
| case dwarf::DW_TAG_constant: |
| case dwarf::DW_TAG_file_type: |
| case dwarf::DW_TAG_namelist: |
| case dwarf::DW_TAG_packed_type: |
| case dwarf::DW_TAG_volatile_type: |
| case dwarf::DW_TAG_restrict_type: |
| case dwarf::DW_TAG_atomic_type: |
| case dwarf::DW_TAG_interface_type: |
| case dwarf::DW_TAG_unspecified_type: |
| case dwarf::DW_TAG_shared_type: |
| case dwarf::DW_TAG_immutable_type: |
| case dwarf::DW_TAG_rvalue_reference_type: { |
| if (!AttrInfo.IsDeclaration && AttrInfo.Name != nullptr && |
| !AttrInfo.Name->getKey().empty()) { |
| uint32_t Hash = hashFullyQualifiedName(this, InputDIE); |
| |
| uint64_t RuntimeLang = |
| dwarf::toUnsigned(InputDIE.find(dwarf::DW_AT_APPLE_runtime_class)) |
| .value_or(0); |
| |
| bool ObjCClassIsImplementation = |
| (RuntimeLang == dwarf::DW_LANG_ObjC || |
| RuntimeLang == dwarf::DW_LANG_ObjC_plus_plus) && |
| dwarf::toUnsigned( |
| InputDIE.find(dwarf::DW_AT_APPLE_objc_complete_type)) |
| .value_or(0); |
| |
| rememberTypeForAccelerators(AttrInfo.Name, OutOffset, |
| InputDieEntry->getTag(), Hash, |
| ObjCClassIsImplementation); |
| } |
| } break; |
| case dwarf::DW_TAG_namespace: { |
| if (AttrInfo.Name == nullptr) |
| AttrInfo.Name = |
| getGlobalData().getStringPool().insert("(anonymous namespace)").first; |
| |
| rememberNamespaceForAccelerators(AttrInfo.Name, OutOffset, |
| InputDieEntry->getTag()); |
| } break; |
| case dwarf::DW_TAG_imported_declaration: { |
| if (AttrInfo.Name != nullptr) |
| rememberNamespaceForAccelerators(AttrInfo.Name, OutOffset, |
| InputDieEntry->getTag()); |
| } break; |
| case dwarf::DW_TAG_compile_unit: |
| case dwarf::DW_TAG_lexical_block: { |
| // Nothing to do. |
| } break; |
| default: |
| if (AttrInfo.HasLiveAddress || AttrInfo.HasRanges) { |
| if (AttrInfo.Name != nullptr) |
| rememberNameForAccelerators( |
| AttrInfo.Name, OutOffset, InputDieEntry->getTag(), |
| InputDieEntry->getTag() == dwarf::DW_TAG_inlined_subroutine); |
| |
| // Look for mangled name recursively if mangled name is not known yet. |
| if (AttrInfo.MangledName == nullptr) |
| if (const char *LinkageName = InputDIE.getLinkageName()) |
| AttrInfo.MangledName = |
| getGlobalData().getStringPool().insert(LinkageName).first; |
| |
| if (AttrInfo.MangledName != nullptr && |
| AttrInfo.MangledName != AttrInfo.Name) |
| rememberNameForAccelerators( |
| AttrInfo.MangledName, OutOffset, InputDieEntry->getTag(), |
| InputDieEntry->getTag() == dwarf::DW_TAG_inlined_subroutine); |
| |
| // Strip template parameters from the short name. |
| if (AttrInfo.Name != nullptr && AttrInfo.MangledName != AttrInfo.Name && |
| (InputDieEntry->getTag() != dwarf::DW_TAG_inlined_subroutine)) { |
| if (std::optional<StringRef> Name = |
| StripTemplateParameters(AttrInfo.Name->getKey())) { |
| StringEntry *NameWithoutTemplateParams = |
| getGlobalData().getStringPool().insert(*Name).first; |
| |
| rememberNameForAccelerators(NameWithoutTemplateParams, OutOffset, |
| InputDieEntry->getTag(), true); |
| } |
| } |
| |
| if (AttrInfo.Name) |
| rememberObjCAccelerator(InputDieEntry, OutOffset, AttrInfo); |
| } |
| break; |
| } |
| } |
| |
| void CompileUnit::rememberObjCAccelerator( |
| const DWARFDebugInfoEntry *InputDieEntry, uint64_t OutOffset, |
| AttributesInfo &AttrInfo) { |
| std::optional<ObjCSelectorNames> Names = |
| getObjCNamesIfSelector(AttrInfo.Name->getKey()); |
| if (!Names) |
| return; |
| |
| StringEntry *Selector = |
| getGlobalData().getStringPool().insert(Names->Selector).first; |
| rememberNameForAccelerators(Selector, OutOffset, InputDieEntry->getTag(), |
| true); |
| StringEntry *ClassName = |
| getGlobalData().getStringPool().insert(Names->ClassName).first; |
| rememberObjCNameForAccelerators(ClassName, OutOffset, |
| InputDieEntry->getTag()); |
| if (Names->ClassNameNoCategory) { |
| StringEntry *ClassNameNoCategory = getGlobalData() |
| .getStringPool() |
| .insert(*Names->ClassNameNoCategory) |
| .first; |
| rememberObjCNameForAccelerators(ClassNameNoCategory, OutOffset, |
| InputDieEntry->getTag()); |
| } |
| if (Names->MethodNameNoCategory) { |
| StringEntry *MethodNameNoCategory = |
| getGlobalData() |
| .getStringPool() |
| .insert(*Names->MethodNameNoCategory) |
| .first; |
| rememberNameForAccelerators(MethodNameNoCategory, OutOffset, |
| InputDieEntry->getTag(), true); |
| } |
| } |