| //===- 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 "llvm/DWARFLinker/DWARFLinkerCompileUnit.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/DWARFLinker/DWARFLinkerDeclContext.h" |
| #include "llvm/DebugInfo/DWARF/DWARFContext.h" |
| #include "llvm/DebugInfo/DWARF/DWARFExpression.h" |
| #include "llvm/Support/FormatVariadic.h" |
| |
| namespace llvm { |
| |
| #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
| LLVM_DUMP_METHOD void CompileUnit::DIEInfo::dump() { |
| llvm::errs() << "{\n"; |
| llvm::errs() << " AddrAdjust: " << AddrAdjust << '\n'; |
| llvm::errs() << " Ctxt: " << formatv("{0:x}", Ctxt) << '\n'; |
| llvm::errs() << " Clone: " << formatv("{0:x}", Clone) << '\n'; |
| llvm::errs() << " ParentIdx: " << ParentIdx << '\n'; |
| llvm::errs() << " Keep: " << Keep << '\n'; |
| llvm::errs() << " InDebugMap: " << InDebugMap << '\n'; |
| llvm::errs() << " Prune: " << Prune << '\n'; |
| llvm::errs() << " Incomplete: " << Incomplete << '\n'; |
| llvm::errs() << " InModuleScope: " << InModuleScope << '\n'; |
| llvm::errs() << " ODRMarkingDone: " << ODRMarkingDone << '\n'; |
| llvm::errs() << " UnclonedReference: " << UnclonedReference << '\n'; |
| llvm::errs() << "}\n"; |
| } |
| #endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
| |
| /// Check if the DIE at \p Idx is in the scope of a function. |
| static bool inFunctionScope(CompileUnit &U, unsigned Idx) { |
| while (Idx) { |
| if (U.getOrigUnit().getDIEAtIndex(Idx).getTag() == dwarf::DW_TAG_subprogram) |
| return true; |
| Idx = U.getInfo(Idx).ParentIdx; |
| } |
| return false; |
| } |
| |
| uint16_t CompileUnit::getLanguage() { |
| if (!Language) { |
| DWARFDie CU = getOrigUnit().getUnitDIE(); |
| Language = dwarf::toUnsigned(CU.find(dwarf::DW_AT_language), 0); |
| } |
| return Language; |
| } |
| |
| StringRef CompileUnit::getSysRoot() { |
| if (SysRoot.empty()) { |
| DWARFDie CU = getOrigUnit().getUnitDIE(); |
| SysRoot = dwarf::toStringRef(CU.find(dwarf::DW_AT_LLVM_sysroot)).str(); |
| } |
| return SysRoot; |
| } |
| |
| void CompileUnit::markEverythingAsKept() { |
| unsigned Idx = 0; |
| |
| for (auto &I : Info) { |
| // Mark everything that wasn't explicit marked for pruning. |
| I.Keep = !I.Prune; |
| auto DIE = OrigUnit.getDIEAtIndex(Idx++); |
| DWARFUnit *U = DIE.getDwarfUnit(); |
| |
| // Try to guess which DIEs must go to the accelerator tables. We do that |
| // just for variables, because functions will be handled depending on |
| // whether they carry a DW_AT_low_pc attribute or not. |
| if (DIE.getTag() != dwarf::DW_TAG_variable && |
| DIE.getTag() != dwarf::DW_TAG_constant) |
| continue; |
| |
| std::optional<DWARFFormValue> Value; |
| if (!(Value = DIE.find(dwarf::DW_AT_location))) { |
| if ((Value = DIE.find(dwarf::DW_AT_const_value)) && |
| !inFunctionScope(*this, I.ParentIdx)) |
| I.InDebugMap = true; |
| continue; |
| } |
| |
| if (auto ExprLockBlock = Value->getAsBlock()) { |
| // Parse 'exprloc' expression. |
| DataExtractor Data(toStringRef(*ExprLockBlock), |
| U->getContext().isLittleEndian(), |
| U->getAddressByteSize()); |
| DWARFExpression Expression(Data, U->getAddressByteSize(), |
| U->getFormParams().Format); |
| |
| for (DWARFExpression::iterator It = Expression.begin(); |
| (It != Expression.end()) && !I.InDebugMap; ++It) { |
| DWARFExpression::iterator NextIt = It; |
| ++NextIt; |
| |
| switch (It->getCode()) { |
| case dwarf::DW_OP_const2u: |
| case dwarf::DW_OP_const4u: |
| case dwarf::DW_OP_const8u: |
| case dwarf::DW_OP_const2s: |
| case dwarf::DW_OP_const4s: |
| case dwarf::DW_OP_const8s: |
| if (NextIt == Expression.end() || |
| NextIt->getCode() != dwarf::DW_OP_form_tls_address) |
| break; |
| [[fallthrough]]; |
| case dwarf::DW_OP_constx: |
| case dwarf::DW_OP_addr: |
| case dwarf::DW_OP_addrx: |
| I.InDebugMap = true; |
| break; |
| default: |
| // Nothing to do. |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| uint64_t CompileUnit::computeNextUnitOffset(uint16_t DwarfVersion) { |
| NextUnitOffset = StartOffset; |
| if (NewUnit) { |
| NextUnitOffset += (DwarfVersion >= 5) ? 12 : 11; // Header size |
| NextUnitOffset += NewUnit->getUnitDie().getSize(); |
| } |
| return NextUnitOffset; |
| } |
| |
| /// Keep track of a forward cross-cu reference from this unit |
| /// to \p Die that lives in \p RefUnit. |
| void CompileUnit::noteForwardReference(DIE *Die, const CompileUnit *RefUnit, |
| DeclContext *Ctxt, PatchLocation Attr) { |
| ForwardDIEReferences.emplace_back(Die, RefUnit, Ctxt, Attr); |
| } |
| |
| void CompileUnit::fixupForwardReferences() { |
| for (const auto &Ref : ForwardDIEReferences) { |
| DIE *RefDie; |
| const CompileUnit *RefUnit; |
| PatchLocation Attr; |
| DeclContext *Ctxt; |
| std::tie(RefDie, RefUnit, Ctxt, Attr) = Ref; |
| if (Ctxt && Ctxt->hasCanonicalDIE()) { |
| assert(Ctxt->getCanonicalDIEOffset() && |
| "Canonical die offset is not set"); |
| Attr.set(Ctxt->getCanonicalDIEOffset()); |
| } else { |
| assert(RefDie->getOffset() && "Referenced die offset is not set"); |
| Attr.set(RefDie->getOffset() + RefUnit->getStartOffset()); |
| } |
| } |
| } |
| |
| void CompileUnit::addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset) { |
| Labels.insert({LabelLowPc, PcOffset}); |
| } |
| |
| void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc, |
| int64_t PcOffset) { |
| 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::noteRangeAttribute(const DIE &Die, PatchLocation Attr) { |
| if (Die.getTag() == dwarf::DW_TAG_compile_unit) { |
| UnitRangeAttribute = Attr; |
| return; |
| } |
| |
| RangeAttributes.emplace_back(Attr); |
| } |
| |
| void CompileUnit::noteLocationAttribute(PatchLocation Attr) { |
| LocationAttributes.emplace_back(Attr); |
| } |
| |
| void CompileUnit::addNamespaceAccelerator(const DIE *Die, |
| DwarfStringPoolEntryRef Name) { |
| Namespaces.emplace_back(Name, Die); |
| } |
| |
| void CompileUnit::addObjCAccelerator(const DIE *Die, |
| DwarfStringPoolEntryRef Name, |
| bool SkipPubSection) { |
| ObjC.emplace_back(Name, Die, SkipPubSection); |
| } |
| |
| void CompileUnit::addNameAccelerator(const DIE *Die, |
| DwarfStringPoolEntryRef Name, |
| bool SkipPubSection) { |
| Pubnames.emplace_back(Name, Die, SkipPubSection); |
| } |
| |
| void CompileUnit::addTypeAccelerator(const DIE *Die, |
| DwarfStringPoolEntryRef Name, |
| bool ObjcClassImplementation, |
| uint32_t QualifiedNameHash) { |
| Pubtypes.emplace_back(Name, Die, QualifiedNameHash, ObjcClassImplementation); |
| } |
| |
| } // namespace llvm |