mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-28 13:36:08 +00:00

This patch adds check for parallel linker to tests located in the root of test/tools/dsymutil.
1860 lines
67 KiB
C++
1860 lines
67 KiB
C++
//=== 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 "AcceleratorRecordsSaver.h"
|
|
#include "DIEAttributeCloner.h"
|
|
#include "DIEGenerator.h"
|
|
#include "DependencyTracker.h"
|
|
#include "SyntheticTypeNameBuilder.h"
|
|
#include "llvm/DWARFLinker/Utils.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h"
|
|
#include "llvm/Support/DJB.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include <utility>
|
|
|
|
using namespace llvm;
|
|
using namespace dwarf_linker;
|
|
using namespace dwarf_linker::parallel;
|
|
|
|
CompileUnit::CompileUnit(LinkingGlobalData &GlobalData, unsigned ID,
|
|
StringRef ClangModuleName, DWARFFile &File,
|
|
OffsetToUnitTy UnitFromOffset,
|
|
dwarf::FormParams Format, llvm::endianness Endianess)
|
|
: DwarfUnit(GlobalData, ID, ClangModuleName), File(File),
|
|
getUnitFromOffset(UnitFromOffset), Stage(Stage::CreatedNotLoaded),
|
|
AcceleratorRecords(&GlobalData.getAllocator()) {
|
|
UnitName = File.FileName;
|
|
setOutputFormat(Format, Endianess);
|
|
getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
|
|
}
|
|
|
|
CompileUnit::CompileUnit(LinkingGlobalData &GlobalData, DWARFUnit &OrigUnit,
|
|
unsigned ID, StringRef ClangModuleName,
|
|
DWARFFile &File, OffsetToUnitTy UnitFromOffset,
|
|
dwarf::FormParams Format, llvm::endianness Endianess)
|
|
: DwarfUnit(GlobalData, ID, ClangModuleName), File(File),
|
|
OrigUnit(&OrigUnit), getUnitFromOffset(UnitFromOffset),
|
|
Stage(Stage::CreatedNotLoaded),
|
|
AcceleratorRecords(&GlobalData.getAllocator()) {
|
|
setOutputFormat(Format, Endianess);
|
|
getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
|
|
|
|
DWARFDie CUDie = OrigUnit.getUnitDIE();
|
|
if (!CUDie)
|
|
return;
|
|
|
|
if (std::optional<DWARFFormValue> Val = CUDie.find(dwarf::DW_AT_language)) {
|
|
uint16_t LangVal = dwarf::toUnsigned(Val, 0);
|
|
if (isODRLanguage(LangVal))
|
|
Language = LangVal;
|
|
}
|
|
|
|
if (!GlobalData.getOptions().NoODR && Language.has_value())
|
|
NoODR = false;
|
|
|
|
if (const char *CUName = CUDie.getName(DINameKind::ShortName))
|
|
UnitName = CUName;
|
|
else
|
|
UnitName = File.FileName;
|
|
SysRoot = dwarf::toStringRef(CUDie.find(dwarf::DW_AT_LLVM_sysroot)).str();
|
|
}
|
|
|
|
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();
|
|
Dependencies.reset(nullptr);
|
|
|
|
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;
|
|
for (TypeEntry *&Name : TypeEntries)
|
|
Name = nullptr;
|
|
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);
|
|
if (!NoODR)
|
|
TypeEntries.resize(getOrigUnit().getNumDIEs());
|
|
return true;
|
|
}
|
|
|
|
void CompileUnit::analyzeDWARFStructureRec(const DWARFDebugInfoEntry *DieEntry,
|
|
bool IsODRUnavailableFunctionScope) {
|
|
CompileUnit::DIEInfo &DieInfo = getDIEInfo(DieEntry);
|
|
|
|
for (const DWARFDebugInfoEntry *CurChild = getFirstChildEntry(DieEntry);
|
|
CurChild && CurChild->getAbbreviationDeclarationPtr();
|
|
CurChild = getSiblingEntry(CurChild)) {
|
|
CompileUnit::DIEInfo &ChildInfo = getDIEInfo(CurChild);
|
|
bool ChildIsODRUnavailableFunctionScope = IsODRUnavailableFunctionScope;
|
|
|
|
if (DieInfo.getIsInMouduleScope())
|
|
ChildInfo.setIsInMouduleScope();
|
|
|
|
if (DieInfo.getIsInFunctionScope())
|
|
ChildInfo.setIsInFunctionScope();
|
|
|
|
if (DieInfo.getIsInAnonNamespaceScope())
|
|
ChildInfo.setIsInAnonNamespaceScope();
|
|
|
|
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();
|
|
if (!ChildIsODRUnavailableFunctionScope &&
|
|
!ChildInfo.getIsInMouduleScope()) {
|
|
if (find(CurChild,
|
|
{dwarf::DW_AT_abstract_origin, dwarf::DW_AT_specification}))
|
|
ChildIsODRUnavailableFunctionScope = true;
|
|
}
|
|
break;
|
|
case dwarf::DW_TAG_namespace: {
|
|
UnitEntryPairTy NamespaceEntry = {this, CurChild};
|
|
|
|
if (find(CurChild, dwarf::DW_AT_extension))
|
|
NamespaceEntry = NamespaceEntry.getNamespaceOrigin();
|
|
|
|
if (!NamespaceEntry.CU->find(NamespaceEntry.DieEntry, dwarf::DW_AT_name))
|
|
ChildInfo.setIsInAnonNamespaceScope();
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!isClangModule() && !getGlobalData().getOptions().UpdateIndexTablesOnly)
|
|
ChildInfo.setTrackLiveness();
|
|
|
|
if ((!ChildInfo.getIsInAnonNamespaceScope() &&
|
|
!ChildIsODRUnavailableFunctionScope && !NoODR))
|
|
ChildInfo.setODRAvailable();
|
|
|
|
if (CurChild->hasChildren())
|
|
analyzeDWARFStructureRec(CurChild, ChildIsODRUnavailableFunctionScope);
|
|
}
|
|
}
|
|
|
|
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();
|
|
FileNames.shrink_and_clear();
|
|
DieInfoArray = SmallVector<DIEInfo>();
|
|
OutDieOffsetArray = SmallVector<uint64_t>();
|
|
TypeEntries = SmallVector<TypeEntry *>();
|
|
Dependencies.reset(nullptr);
|
|
getOrigUnit().clear();
|
|
}
|
|
|
|
/// Collect references to parseable Swift interfaces in imported
|
|
/// DW_TAG_module blocks.
|
|
void CompileUnit::analyzeImportedModule(const DWARFDebugInfoEntry *DieEntry) {
|
|
if (!Language || Language != dwarf::DW_LANG_Swift)
|
|
return;
|
|
|
|
if (!GlobalData.getOptions().ParseableSwiftInterfaces)
|
|
return;
|
|
|
|
StringRef Path =
|
|
dwarf::toStringRef(find(DieEntry, dwarf::DW_AT_LLVM_include_path));
|
|
if (!Path.ends_with(".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.starts_with(SysRoot))
|
|
return;
|
|
// Don't track interfaces that are part of the toolchain.
|
|
// For example: Swift, _Concurrency, ...
|
|
SmallString<128> Toolchain = guessToolchainBaseDir(SysRoot);
|
|
if (!Toolchain.empty() && Path.starts_with(Toolchain))
|
|
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);
|
|
}
|
|
}
|
|
|
|
Error CompileUnit::assignTypeNames(TypePool &TypePoolRef) {
|
|
if (!getUnitDIE().isValid())
|
|
return Error::success();
|
|
|
|
SyntheticTypeNameBuilder NameBuilder(TypePoolRef);
|
|
return assignTypeNamesRec(getDebugInfoEntry(0), NameBuilder);
|
|
}
|
|
|
|
Error CompileUnit::assignTypeNamesRec(const DWARFDebugInfoEntry *DieEntry,
|
|
SyntheticTypeNameBuilder &NameBuilder) {
|
|
OrderedChildrenIndexAssigner ChildrenIndexAssigner(*this, DieEntry);
|
|
for (const DWARFDebugInfoEntry *CurChild = getFirstChildEntry(DieEntry);
|
|
CurChild && CurChild->getAbbreviationDeclarationPtr();
|
|
CurChild = getSiblingEntry(CurChild)) {
|
|
CompileUnit::DIEInfo &ChildInfo = getDIEInfo(CurChild);
|
|
if (!ChildInfo.needToPlaceInTypeTable())
|
|
continue;
|
|
|
|
assert(ChildInfo.getODRAvailable());
|
|
if (Error Err = NameBuilder.assignName(
|
|
{this, CurChild},
|
|
ChildrenIndexAssigner.getChildIndex(*this, CurChild)))
|
|
return Err;
|
|
|
|
if (Error Err = assignTypeNamesRec(CurChild, NameBuilder))
|
|
return Err;
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
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<UnitEntryPairTy> CompileUnit::resolveDIEReference(
|
|
const DWARFFormValue &RefValue,
|
|
ResolveInterCUReferencesMode CanResolveInterCUReferences) {
|
|
if (std::optional<DWARFFormValue::UnitOffset> Ref =
|
|
*RefValue.getAsRelativeReference()) {
|
|
if (Ref->Unit == OrigUnit) {
|
|
// Referenced DIE is in current compile unit.
|
|
if (std::optional<uint32_t> RefDieIdx =
|
|
getDIEIndexForOffset(OrigUnit->getOffset() + Ref->Offset))
|
|
return UnitEntryPairTy{this, OrigUnit->getDebugInfoEntry(*RefDieIdx)};
|
|
}
|
|
uint64_t RefDIEOffset =
|
|
Ref->Unit ? Ref->Unit->getOffset() + Ref->Offset : Ref->Offset;
|
|
if (CompileUnit *RefCU = getUnitFromOffset(RefDIEOffset)) {
|
|
if (RefCU == this) {
|
|
// Referenced DIE is in current compile unit.
|
|
if (std::optional<uint32_t> RefDieIdx =
|
|
getDIEIndexForOffset(RefDIEOffset))
|
|
return UnitEntryPairTy{this, getDebugInfoEntry(*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 UnitEntryPairTy{RefCU, nullptr};
|
|
|
|
if (std::optional<uint32_t> RefDieIdx =
|
|
RefCU->getDIEIndexForOffset(RefDIEOffset))
|
|
return UnitEntryPairTy{RefCU, RefCU->getDebugInfoEntry(*RefDieIdx)};
|
|
} else
|
|
return UnitEntryPairTy{RefCU, nullptr};
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<UnitEntryPairTy> CompileUnit::resolveDIEReference(
|
|
const DWARFDebugInfoEntry *DieEntry, dwarf::Attribute Attr,
|
|
ResolveInterCUReferencesMode CanResolveInterCUReferences) {
|
|
if (std::optional<DWARFFormValue> AttrVal = find(DieEntry, Attr))
|
|
return resolveDIEReference(*AttrVal, CanResolveInterCUReferences);
|
|
|
|
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::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() != llvm::endianness::native)
|
|
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() != llvm::endianness::native)
|
|
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<std::reference_wrapper<const Triple>> TargetTriple,
|
|
TypeUnit *ArtificialTypeUnit) {
|
|
BumpPtrAllocator Allocator;
|
|
|
|
DWARFDie OrigUnitDIE = getOrigUnit().getUnitDIE();
|
|
if (!OrigUnitDIE.isValid())
|
|
return Error::success();
|
|
|
|
TypeEntry *RootEntry = nullptr;
|
|
if (ArtificialTypeUnit)
|
|
RootEntry = ArtificialTypeUnit->getTypePool().getRoot();
|
|
|
|
// Clone input DIE entry recursively.
|
|
std::pair<DIE *, TypeEntry *> OutCUDie = cloneDIE(
|
|
OrigUnitDIE.getDebugInfoEntry(), RootEntry, getDebugInfoHeaderSize(),
|
|
std::nullopt, std::nullopt, Allocator, ArtificialTypeUnit);
|
|
setOutUnitDIE(OutCUDie.first);
|
|
|
|
if (!TargetTriple.has_value() || (OutCUDie.first == nullptr))
|
|
return Error::success();
|
|
|
|
if (Error Err = cloneAndEmitLineTable((*TargetTriple).get()))
|
|
return Err;
|
|
|
|
if (Error Err = cloneAndEmitDebugMacro())
|
|
return Err;
|
|
|
|
getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
|
|
if (Error Err = emitDebugInfo((*TargetTriple).get()))
|
|
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();
|
|
}
|
|
|
|
std::pair<DIE *, TypeEntry *> CompileUnit::cloneDIE(
|
|
const DWARFDebugInfoEntry *InputDieEntry, TypeEntry *ClonedParentTypeDIE,
|
|
uint64_t OutOffset, std::optional<int64_t> FuncAddressAdjustment,
|
|
std::optional<int64_t> VarAddressAdjustment, BumpPtrAllocator &Allocator,
|
|
TypeUnit *ArtificialTypeUnit) {
|
|
uint32_t InputDieIdx = getDIEIndex(InputDieEntry);
|
|
CompileUnit::DIEInfo &Info = getDIEInfo(InputDieIdx);
|
|
|
|
bool NeedToClonePlainDIE = Info.needToKeepInPlainDwarf();
|
|
bool NeedToCloneTypeDIE =
|
|
(InputDieEntry->getTag() != dwarf::DW_TAG_compile_unit) &&
|
|
Info.needToPlaceInTypeTable();
|
|
std::pair<DIE *, TypeEntry *> ClonedDIE;
|
|
|
|
DIEGenerator PlainDIEGenerator(Allocator, *this);
|
|
|
|
if (NeedToClonePlainDIE)
|
|
// Create a cloned DIE which would be placed into the cloned version
|
|
// of input compile unit.
|
|
ClonedDIE.first = createPlainDIEandCloneAttributes(
|
|
InputDieEntry, PlainDIEGenerator, OutOffset, FuncAddressAdjustment,
|
|
VarAddressAdjustment);
|
|
if (NeedToCloneTypeDIE) {
|
|
// Create a cloned DIE which would be placed into the artificial type
|
|
// unit.
|
|
assert(ArtificialTypeUnit != nullptr);
|
|
DIEGenerator TypeDIEGenerator(
|
|
ArtificialTypeUnit->getTypePool().getThreadLocalAllocator(), *this);
|
|
|
|
ClonedDIE.second = createTypeDIEandCloneAttributes(
|
|
InputDieEntry, TypeDIEGenerator, ClonedParentTypeDIE,
|
|
ArtificialTypeUnit);
|
|
}
|
|
TypeEntry *TypeParentForChild =
|
|
ClonedDIE.second ? ClonedDIE.second : ClonedParentTypeDIE;
|
|
|
|
bool HasPlainChildrenToClone =
|
|
(ClonedDIE.first && Info.getKeepPlainChildren());
|
|
|
|
bool HasTypeChildrenToClone =
|
|
((ClonedDIE.second ||
|
|
InputDieEntry->getTag() == dwarf::DW_TAG_compile_unit) &&
|
|
Info.getKeepTypeChildren());
|
|
|
|
// Recursively clone children.
|
|
if (HasPlainChildrenToClone || HasTypeChildrenToClone) {
|
|
for (const DWARFDebugInfoEntry *CurChild =
|
|
getFirstChildEntry(InputDieEntry);
|
|
CurChild && CurChild->getAbbreviationDeclarationPtr();
|
|
CurChild = getSiblingEntry(CurChild)) {
|
|
std::pair<DIE *, TypeEntry *> ClonedChild = cloneDIE(
|
|
CurChild, TypeParentForChild, OutOffset, FuncAddressAdjustment,
|
|
VarAddressAdjustment, Allocator, ArtificialTypeUnit);
|
|
|
|
if (ClonedChild.first) {
|
|
OutOffset =
|
|
ClonedChild.first->getOffset() + ClonedChild.first->getSize();
|
|
PlainDIEGenerator.addChild(ClonedChild.first);
|
|
}
|
|
}
|
|
assert(ClonedDIE.first == nullptr ||
|
|
HasPlainChildrenToClone == ClonedDIE.first->hasChildren());
|
|
|
|
// Account for the end of children marker.
|
|
if (HasPlainChildrenToClone)
|
|
OutOffset += sizeof(int8_t);
|
|
}
|
|
|
|
// Update our size.
|
|
if (ClonedDIE.first != nullptr)
|
|
ClonedDIE.first->setSize(OutOffset - ClonedDIE.first->getOffset());
|
|
|
|
return ClonedDIE;
|
|
}
|
|
|
|
DIE *CompileUnit::createPlainDIEandCloneAttributes(
|
|
const DWARFDebugInfoEntry *InputDieEntry, DIEGenerator &PlainDIEGenerator,
|
|
uint64_t &OutOffset, std::optional<int64_t> &FuncAddressAdjustment,
|
|
std::optional<int64_t> &VarAddressAdjustment) {
|
|
uint32_t InputDieIdx = getDIEIndex(InputDieEntry);
|
|
CompileUnit::DIEInfo &Info = getDIEInfo(InputDieIdx);
|
|
DIE *ClonedDIE = 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), false);
|
|
} else if (InputDieEntry->getTag() == dwarf::DW_TAG_label) {
|
|
// Get relocation adjustment value for the current label.
|
|
std::optional<uint64_t> lowPC =
|
|
dwarf::toAddress(find(InputDieEntry, dwarf::DW_AT_low_pc));
|
|
if (lowPC) {
|
|
LabelMapTy::iterator It = Labels.find(*lowPC);
|
|
if (It != Labels.end())
|
|
FuncAddressAdjustment = It->second;
|
|
}
|
|
} 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), false);
|
|
|
|
HasLocationExpressionAddress = LocExprAddrAndRelocAdjustment.first;
|
|
if (LocExprAddrAndRelocAdjustment.first &&
|
|
LocExprAddrAndRelocAdjustment.second)
|
|
VarAddressAdjustment = *LocExprAddrAndRelocAdjustment.second;
|
|
}
|
|
|
|
ClonedDIE = PlainDIEGenerator.createDIE(InputDieEntry->getTag(), OutOffset);
|
|
|
|
// Offset to the DIE would be used after output DIE tree is deleted.
|
|
// Thus we need to remember DIE offset separately.
|
|
rememberDieOutOffset(InputDieIdx, OutOffset);
|
|
|
|
// Clone Attributes.
|
|
DIEAttributeCloner AttributesCloner(ClonedDIE, *this, this, InputDieEntry,
|
|
PlainDIEGenerator, FuncAddressAdjustment,
|
|
VarAddressAdjustment,
|
|
HasLocationExpressionAddress);
|
|
AttributesCloner.clone();
|
|
|
|
// Remember accelerator info.
|
|
AcceleratorRecordsSaver AccelRecordsSaver(getGlobalData(), *this, this);
|
|
AccelRecordsSaver.save(InputDieEntry, ClonedDIE, AttributesCloner.AttrInfo,
|
|
nullptr);
|
|
|
|
OutOffset =
|
|
AttributesCloner.finalizeAbbreviations(Info.getKeepPlainChildren());
|
|
|
|
return ClonedDIE;
|
|
}
|
|
|
|
/// Allocates output DIE for the specified \p TypeDescriptor.
|
|
DIE *CompileUnit::allocateTypeDie(TypeEntryBody *TypeDescriptor,
|
|
DIEGenerator &TypeDIEGenerator,
|
|
dwarf::Tag DieTag, bool IsDeclaration,
|
|
bool IsParentDeclaration) {
|
|
DIE *DefinitionDie = TypeDescriptor->Die;
|
|
// Do not allocate any new DIE if definition DIE is already met.
|
|
if (DefinitionDie)
|
|
return nullptr;
|
|
|
|
DIE *DeclarationDie = TypeDescriptor->DeclarationDie;
|
|
bool OldParentIsDeclaration = TypeDescriptor->ParentIsDeclaration;
|
|
|
|
if (IsDeclaration && !DeclarationDie) {
|
|
// Alocate declaration DIE.
|
|
DIE *NewDie = TypeDIEGenerator.createDIE(DieTag, 0);
|
|
if (TypeDescriptor->DeclarationDie.compare_exchange_weak(DeclarationDie,
|
|
NewDie))
|
|
return NewDie;
|
|
} else if (IsDeclaration && !IsParentDeclaration && OldParentIsDeclaration) {
|
|
// Overwrite existing declaration DIE if it's parent is also an declaration
|
|
// while parent of current declaration DIE is a definition.
|
|
if (TypeDescriptor->ParentIsDeclaration.compare_exchange_weak(
|
|
OldParentIsDeclaration, false)) {
|
|
DIE *NewDie = TypeDIEGenerator.createDIE(DieTag, 0);
|
|
TypeDescriptor->DeclarationDie = NewDie;
|
|
return NewDie;
|
|
}
|
|
} else if (!IsDeclaration && IsParentDeclaration && !DeclarationDie) {
|
|
// Alocate declaration DIE since parent of current DIE is marked as
|
|
// declaration.
|
|
DIE *NewDie = TypeDIEGenerator.createDIE(DieTag, 0);
|
|
if (TypeDescriptor->DeclarationDie.compare_exchange_weak(DeclarationDie,
|
|
NewDie))
|
|
return NewDie;
|
|
} else if (!IsDeclaration && !IsParentDeclaration) {
|
|
// Allocate definition DIE.
|
|
DIE *NewDie = TypeDIEGenerator.createDIE(DieTag, 0);
|
|
if (TypeDescriptor->Die.compare_exchange_weak(DefinitionDie, NewDie)) {
|
|
TypeDescriptor->ParentIsDeclaration = false;
|
|
return NewDie;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
TypeEntry *CompileUnit::createTypeDIEandCloneAttributes(
|
|
const DWARFDebugInfoEntry *InputDieEntry, DIEGenerator &TypeDIEGenerator,
|
|
TypeEntry *ClonedParentTypeDIE, TypeUnit *ArtificialTypeUnit) {
|
|
assert(ArtificialTypeUnit != nullptr);
|
|
uint32_t InputDieIdx = getDIEIndex(InputDieEntry);
|
|
|
|
TypeEntry *Entry = getDieTypeEntry(InputDieIdx);
|
|
assert(Entry != nullptr);
|
|
assert(ClonedParentTypeDIE != nullptr);
|
|
TypeEntryBody *EntryBody =
|
|
ArtificialTypeUnit->getTypePool().getOrCreateTypeEntryBody(
|
|
Entry, ClonedParentTypeDIE);
|
|
assert(EntryBody);
|
|
|
|
bool IsDeclaration =
|
|
dwarf::toUnsigned(find(InputDieEntry, dwarf::DW_AT_declaration), 0);
|
|
|
|
bool ParentIsDeclaration = false;
|
|
if (std::optional<uint32_t> ParentIdx = InputDieEntry->getParentIdx())
|
|
ParentIsDeclaration =
|
|
dwarf::toUnsigned(find(*ParentIdx, dwarf::DW_AT_declaration), 0);
|
|
|
|
DIE *OutDIE =
|
|
allocateTypeDie(EntryBody, TypeDIEGenerator, InputDieEntry->getTag(),
|
|
IsDeclaration, ParentIsDeclaration);
|
|
|
|
if (OutDIE != nullptr) {
|
|
assert(ArtificialTypeUnit != nullptr);
|
|
ArtificialTypeUnit->getSectionDescriptor(DebugSectionKind::DebugInfo);
|
|
|
|
DIEAttributeCloner AttributesCloner(OutDIE, *this, ArtificialTypeUnit,
|
|
InputDieEntry, TypeDIEGenerator,
|
|
std::nullopt, std::nullopt, false);
|
|
AttributesCloner.clone();
|
|
|
|
// Remember accelerator info.
|
|
AcceleratorRecordsSaver AccelRecordsSaver(getGlobalData(), *this,
|
|
ArtificialTypeUnit);
|
|
AccelRecordsSaver.save(InputDieEntry, OutDIE, AttributesCloner.AttrInfo,
|
|
Entry);
|
|
|
|
// if AttributesCloner.getOutOffset() == 0 then we need to add
|
|
// 1 to avoid assertion for zero size. We will subtract it back later.
|
|
OutDIE->setSize(AttributesCloner.getOutOffset() + 1);
|
|
}
|
|
|
|
return Entry;
|
|
}
|
|
|
|
Error CompileUnit::cloneAndEmitLineTable(const Triple &TargetTriple) {
|
|
const DWARFDebugLine::LineTable *InputLineTable =
|
|
getContaingFile().Dwarf->getLineTableForUnit(&getOrigUnit());
|
|
if (InputLineTable == nullptr) {
|
|
if (getOrigUnit().getUnitDIE().find(dwarf::DW_AT_stmt_list))
|
|
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() << "{";
|
|
llvm::errs() << " Placement: ";
|
|
switch (getPlacement()) {
|
|
case NotSet:
|
|
llvm::errs() << "NotSet";
|
|
break;
|
|
case TypeTable:
|
|
llvm::errs() << "TypeTable";
|
|
break;
|
|
case PlainDwarf:
|
|
llvm::errs() << "PlainDwarf";
|
|
break;
|
|
case Both:
|
|
llvm::errs() << "Both";
|
|
break;
|
|
}
|
|
|
|
llvm::errs() << " Keep: " << getKeep();
|
|
llvm::errs() << " KeepPlainChildren: " << getKeepPlainChildren();
|
|
llvm::errs() << " KeepTypeChildren: " << getKeepTypeChildren();
|
|
llvm::errs() << " IsInMouduleScope: " << getIsInMouduleScope();
|
|
llvm::errs() << " IsInFunctionScope: " << getIsInFunctionScope();
|
|
llvm::errs() << " IsInAnonNamespaceScope: " << getIsInAnonNamespaceScope();
|
|
llvm::errs() << " ODRAvailable: " << getODRAvailable();
|
|
llvm::errs() << " TrackLiveness: " << getTrackLiveness();
|
|
llvm::errs() << "}\n";
|
|
}
|
|
#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
|
|
std::optional<std::pair<StringRef, StringRef>>
|
|
CompileUnit::getDirAndFilenameFromLineTable(
|
|
const DWARFFormValue &FileIdxValue) {
|
|
uint64_t FileIdx;
|
|
if (std::optional<uint64_t> Val = FileIdxValue.getAsUnsignedConstant())
|
|
FileIdx = *Val;
|
|
else if (std::optional<int64_t> Val = FileIdxValue.getAsSignedConstant())
|
|
FileIdx = *Val;
|
|
else if (std::optional<uint64_t> Val = FileIdxValue.getAsSectionOffset())
|
|
FileIdx = *Val;
|
|
else
|
|
return std::nullopt;
|
|
|
|
return getDirAndFilenameFromLineTable(FileIdx);
|
|
}
|
|
|
|
std::optional<std::pair<StringRef, StringRef>>
|
|
CompileUnit::getDirAndFilenameFromLineTable(uint64_t FileIdx) {
|
|
FileNamesCache::iterator FileData = FileNames.find(FileIdx);
|
|
if (FileData != FileNames.end())
|
|
return std::make_pair(StringRef(FileData->second.first),
|
|
StringRef(FileData->second.second));
|
|
|
|
if (const DWARFDebugLine::LineTable *LineTable =
|
|
getOrigUnit().getContext().getLineTableForUnit(&getOrigUnit())) {
|
|
if (LineTable->hasFileAtIndex(FileIdx)) {
|
|
|
|
const llvm::DWARFDebugLine::FileNameEntry &Entry =
|
|
LineTable->Prologue.getFileNameEntry(FileIdx);
|
|
|
|
Expected<const char *> Name = Entry.Name.getAsCString();
|
|
if (!Name) {
|
|
warn(Name.takeError());
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::string FileName = *Name;
|
|
if (isPathAbsoluteOnWindowsOrPosix(FileName)) {
|
|
FileNamesCache::iterator FileData =
|
|
FileNames
|
|
.insert(std::make_pair(
|
|
FileIdx,
|
|
std::make_pair(std::string(""), std::move(FileName))))
|
|
.first;
|
|
return std::make_pair(StringRef(FileData->second.first),
|
|
StringRef(FileData->second.second));
|
|
}
|
|
|
|
SmallString<256> FilePath;
|
|
StringRef IncludeDir;
|
|
// Be defensive about the contents of Entry.
|
|
if (getVersion() >= 5) {
|
|
// DirIdx 0 is the compilation directory, so don't include it for
|
|
// relative names.
|
|
if ((Entry.DirIdx != 0) &&
|
|
Entry.DirIdx < LineTable->Prologue.IncludeDirectories.size()) {
|
|
Expected<const char *> DirName =
|
|
LineTable->Prologue.IncludeDirectories[Entry.DirIdx]
|
|
.getAsCString();
|
|
if (DirName)
|
|
IncludeDir = *DirName;
|
|
else {
|
|
warn(DirName.takeError());
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
} else {
|
|
if (0 < Entry.DirIdx &&
|
|
Entry.DirIdx <= LineTable->Prologue.IncludeDirectories.size()) {
|
|
Expected<const char *> DirName =
|
|
LineTable->Prologue.IncludeDirectories[Entry.DirIdx - 1]
|
|
.getAsCString();
|
|
if (DirName)
|
|
IncludeDir = *DirName;
|
|
else {
|
|
warn(DirName.takeError());
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
}
|
|
|
|
StringRef CompDir = getOrigUnit().getCompilationDir();
|
|
|
|
if (!CompDir.empty() && !isPathAbsoluteOnWindowsOrPosix(IncludeDir)) {
|
|
sys::path::append(FilePath, sys::path::Style::native, CompDir);
|
|
}
|
|
|
|
sys::path::append(FilePath, sys::path::Style::native, IncludeDir);
|
|
|
|
FileNamesCache::iterator FileData =
|
|
FileNames
|
|
.insert(
|
|
std::make_pair(FileIdx, std::make_pair(std::string(FilePath),
|
|
std::move(FileName))))
|
|
.first;
|
|
return std::make_pair(StringRef(FileData->second.first),
|
|
StringRef(FileData->second.second));
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
#define MAX_REFERENCIES_DEPTH 1000
|
|
UnitEntryPairTy UnitEntryPairTy::getNamespaceOrigin() {
|
|
UnitEntryPairTy CUDiePair(*this);
|
|
std::optional<UnitEntryPairTy> RefDiePair;
|
|
int refDepth = 0;
|
|
do {
|
|
RefDiePair = CUDiePair.CU->resolveDIEReference(
|
|
CUDiePair.DieEntry, dwarf::DW_AT_extension,
|
|
ResolveInterCUReferencesMode::Resolve);
|
|
if (!RefDiePair || !RefDiePair->DieEntry)
|
|
return CUDiePair;
|
|
|
|
CUDiePair = *RefDiePair;
|
|
} while (refDepth++ < MAX_REFERENCIES_DEPTH);
|
|
|
|
return CUDiePair;
|
|
}
|
|
|
|
std::optional<UnitEntryPairTy> UnitEntryPairTy::getParent() {
|
|
if (std::optional<uint32_t> ParentIdx = DieEntry->getParentIdx())
|
|
return UnitEntryPairTy{CU, CU->getDebugInfoEntry(*ParentIdx)};
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
CompileUnit::OutputUnitVariantPtr::OutputUnitVariantPtr(CompileUnit *U)
|
|
: Ptr(U) {
|
|
assert(U != nullptr);
|
|
}
|
|
|
|
CompileUnit::OutputUnitVariantPtr::OutputUnitVariantPtr(TypeUnit *U) : Ptr(U) {
|
|
assert(U != nullptr);
|
|
}
|
|
|
|
DwarfUnit *CompileUnit::OutputUnitVariantPtr::operator->() {
|
|
if (isCompileUnit())
|
|
return getAsCompileUnit();
|
|
else
|
|
return getAsTypeUnit();
|
|
}
|
|
|
|
bool CompileUnit::OutputUnitVariantPtr::isCompileUnit() {
|
|
return Ptr.is<CompileUnit *>();
|
|
}
|
|
|
|
bool CompileUnit::OutputUnitVariantPtr::isTypeUnit() {
|
|
return Ptr.is<TypeUnit *>();
|
|
}
|
|
|
|
CompileUnit *CompileUnit::OutputUnitVariantPtr::getAsCompileUnit() {
|
|
return Ptr.get<CompileUnit *>();
|
|
}
|
|
|
|
TypeUnit *CompileUnit::OutputUnitVariantPtr::getAsTypeUnit() {
|
|
return Ptr.get<TypeUnit *>();
|
|
}
|
|
|
|
bool CompileUnit::resolveDependenciesAndMarkLiveness(
|
|
bool InterCUProcessingStarted, std::atomic<bool> &HasNewInterconnectedCUs) {
|
|
if (!Dependencies.get())
|
|
Dependencies.reset(new DependencyTracker(*this));
|
|
|
|
return Dependencies->resolveDependenciesAndMarkLiveness(
|
|
InterCUProcessingStarted, HasNewInterconnectedCUs);
|
|
}
|
|
|
|
bool CompileUnit::updateDependenciesCompleteness() {
|
|
assert(Dependencies.get());
|
|
|
|
return Dependencies.get()->updateDependenciesCompleteness();
|
|
}
|
|
|
|
void CompileUnit::verifyDependencies() {
|
|
assert(Dependencies.get());
|
|
|
|
Dependencies.get()->verifyKeepChain();
|
|
}
|
|
|
|
ArrayRef<dwarf::Attribute> dwarf_linker::parallel::getODRAttributes() {
|
|
static dwarf::Attribute ODRAttributes[] = {
|
|
dwarf::DW_AT_type, dwarf::DW_AT_specification,
|
|
dwarf::DW_AT_abstract_origin, dwarf::DW_AT_import};
|
|
|
|
return ODRAttributes;
|
|
}
|