//=== 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() { // 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 &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; 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::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() { if (std::optional 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); }); } if (std::optional DebugLocSection = getSectionDescriptor(DebugSectionKind::DebugLoc)) { (*DebugLocSection) ->ListDebugULEB128DieRefPatch.forEach( [](DebugULEB128DieRefPatch &Patch) { Patch.RefDieIdxOrClonedOffset = Patch.RefCU.getPointer()->getDieOutOffset( Patch.RefDieIdxOrClonedOffset); }); } if (std::optional 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(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 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_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 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; } std::optional 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 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 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 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 &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(), 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; 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 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)