blob: a052969e74c0f8d009d98dedbcdb26f4898ef6d8 [file] [log] [blame]
//=== DWARFLinkerImpl.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 "DWARFLinkerImpl.h"
#include "DIEGenerator.h"
#include "DependencyTracker.h"
#include "llvm/DWARFLinker/Utils.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Parallel.h"
#include "llvm/Support/ThreadPool.h"
using namespace llvm;
using namespace dwarf_linker;
using namespace dwarf_linker::parallel;
DWARFLinkerImpl::DWARFLinkerImpl(MessageHandlerTy ErrorHandler,
MessageHandlerTy WarningHandler,
TranslatorFuncTy StringsTranslator)
: UniqueUnitID(0), DebugStrStrings(GlobalData),
DebugLineStrStrings(GlobalData), CommonSections(GlobalData) {
GlobalData.setTranslator(StringsTranslator);
GlobalData.setErrorHandler(ErrorHandler);
GlobalData.setWarningHandler(WarningHandler);
}
DWARFLinkerImpl::LinkContext::LinkContext(LinkingGlobalData &GlobalData,
DWARFFile &File,
StringMap<uint64_t> &ClangModules,
std::atomic<size_t> &UniqueUnitID)
: OutputSections(GlobalData), InputDWARFFile(File),
ClangModules(ClangModules), UniqueUnitID(UniqueUnitID) {
if (File.Dwarf) {
if (!File.Dwarf->compile_units().empty())
CompileUnits.reserve(File.Dwarf->getNumCompileUnits());
// Set context format&endianness based on the input file.
Format.Version = File.Dwarf->getMaxVersion();
Format.AddrSize = File.Dwarf->getCUAddrSize();
Endianness = File.Dwarf->isLittleEndian() ? llvm::endianness::little
: llvm::endianness::big;
}
}
DWARFLinkerImpl::LinkContext::RefModuleUnit::RefModuleUnit(
DWARFFile &File, std::unique_ptr<CompileUnit> Unit)
: File(File), Unit(std::move(Unit)) {}
DWARFLinkerImpl::LinkContext::RefModuleUnit::RefModuleUnit(
LinkContext::RefModuleUnit &&Other)
: File(Other.File), Unit(std::move(Other.Unit)) {}
void DWARFLinkerImpl::LinkContext::addModulesCompileUnit(
LinkContext::RefModuleUnit &&Unit) {
ModulesCompileUnits.emplace_back(std::move(Unit));
}
void DWARFLinkerImpl::addObjectFile(DWARFFile &File, ObjFileLoaderTy Loader,
CompileUnitHandlerTy OnCUDieLoaded) {
ObjectContexts.emplace_back(std::make_unique<LinkContext>(
GlobalData, File, ClangModules, UniqueUnitID));
if (ObjectContexts.back()->InputDWARFFile.Dwarf) {
for (const std::unique_ptr<DWARFUnit> &CU :
ObjectContexts.back()->InputDWARFFile.Dwarf->compile_units()) {
DWARFDie CUDie = CU->getUnitDIE();
OverallNumberOfCU++;
if (!CUDie)
continue;
OnCUDieLoaded(*CU);
// Register mofule reference.
if (!GlobalData.getOptions().UpdateIndexTablesOnly)
ObjectContexts.back()->registerModuleReference(CUDie, Loader,
OnCUDieLoaded);
}
}
}
void DWARFLinkerImpl::setEstimatedObjfilesAmount(unsigned ObjFilesNum) {
ObjectContexts.reserve(ObjFilesNum);
}
Error DWARFLinkerImpl::link() {
// reset compile unit unique ID counter.
UniqueUnitID = 0;
if (Error Err = validateAndUpdateOptions())
return Err;
dwarf::FormParams GlobalFormat = {GlobalData.getOptions().TargetDWARFVersion,
0, dwarf::DwarfFormat::DWARF32};
llvm::endianness GlobalEndianness = llvm::endianness::native;
if (std::optional<std::reference_wrapper<const Triple>> CurTriple =
GlobalData.getTargetTriple()) {
GlobalEndianness = (*CurTriple).get().isLittleEndian()
? llvm::endianness::little
: llvm::endianness::big;
}
std::optional<uint16_t> Language;
for (std::unique_ptr<LinkContext> &Context : ObjectContexts) {
if (Context->InputDWARFFile.Dwarf.get() == nullptr) {
Context->setOutputFormat(Context->getFormParams(), GlobalEndianness);
continue;
}
if (GlobalData.getOptions().Verbose) {
outs() << "DEBUG MAP OBJECT: " << Context->InputDWARFFile.FileName
<< "\n";
for (const std::unique_ptr<DWARFUnit> &OrigCU :
Context->InputDWARFFile.Dwarf->compile_units()) {
outs() << "Input compilation unit:";
DIDumpOptions DumpOpts;
DumpOpts.ChildRecurseDepth = 0;
DumpOpts.Verbose = GlobalData.getOptions().Verbose;
OrigCU->getUnitDIE().dump(outs(), 0, DumpOpts);
}
}
// Verify input DWARF if requested.
if (GlobalData.getOptions().VerifyInputDWARF)
verifyInput(Context->InputDWARFFile);
if (!GlobalData.getTargetTriple())
GlobalEndianness = Context->getEndianness();
GlobalFormat.AddrSize =
std::max(GlobalFormat.AddrSize, Context->getFormParams().AddrSize);
Context->setOutputFormat(Context->getFormParams(), GlobalEndianness);
// FIXME: move creation of CompileUnits into the addObjectFile.
// This would allow to not scan for context Language and Modules state
// twice. And then following handling might be removed.
for (const std::unique_ptr<DWARFUnit> &OrigCU :
Context->InputDWARFFile.Dwarf->compile_units()) {
DWARFDie UnitDie = OrigCU.get()->getUnitDIE();
if (!Language) {
if (std::optional<DWARFFormValue> Val =
UnitDie.find(dwarf::DW_AT_language)) {
uint16_t LangVal = dwarf::toUnsigned(Val, 0);
if (isODRLanguage(LangVal))
Language = LangVal;
}
}
}
}
if (GlobalFormat.AddrSize == 0) {
if (std::optional<std::reference_wrapper<const Triple>> TargetTriple =
GlobalData.getTargetTriple())
GlobalFormat.AddrSize = (*TargetTriple).get().isArch32Bit() ? 4 : 8;
else
GlobalFormat.AddrSize = 8;
}
CommonSections.setOutputFormat(GlobalFormat, GlobalEndianness);
if (!GlobalData.Options.NoODR && Language.has_value()) {
llvm::parallel::TaskGroup TGroup;
TGroup.spawn([&]() {
ArtificialTypeUnit = std::make_unique<TypeUnit>(
GlobalData, UniqueUnitID++, Language, GlobalFormat, GlobalEndianness);
});
}
// Set parallel options.
if (GlobalData.getOptions().Threads == 0)
llvm::parallel::strategy = optimal_concurrency(OverallNumberOfCU);
else
llvm::parallel::strategy =
hardware_concurrency(GlobalData.getOptions().Threads);
// Link object files.
if (GlobalData.getOptions().Threads == 1) {
for (std::unique_ptr<LinkContext> &Context : ObjectContexts) {
// Link object file.
if (Error Err = Context->link(ArtificialTypeUnit.get()))
GlobalData.error(std::move(Err), Context->InputDWARFFile.FileName);
Context->InputDWARFFile.unload();
}
} else {
ThreadPool Pool(llvm::parallel::strategy);
for (std::unique_ptr<LinkContext> &Context : ObjectContexts)
Pool.async([&]() {
// Link object file.
if (Error Err = Context->link(ArtificialTypeUnit.get()))
GlobalData.error(std::move(Err), Context->InputDWARFFile.FileName);
Context->InputDWARFFile.unload();
});
Pool.wait();
}
if (ArtificialTypeUnit.get() != nullptr && !ArtificialTypeUnit->getTypePool()
.getRoot()
->getValue()
.load()
->Children.empty()) {
if (GlobalData.getTargetTriple().has_value())
if (Error Err = ArtificialTypeUnit.get()->finishCloningAndEmit(
(*GlobalData.getTargetTriple()).get()))
return Err;
}
// At this stage each compile units are cloned to their own set of debug
// sections. Now, update patches, assign offsets and assemble final file
// glueing debug tables from each compile unit.
glueCompileUnitsAndWriteToTheOutput();
return Error::success();
}
void DWARFLinkerImpl::verifyInput(const DWARFFile &File) {
assert(File.Dwarf);
std::string Buffer;
raw_string_ostream OS(Buffer);
DIDumpOptions DumpOpts;
if (!File.Dwarf->verify(OS, DumpOpts.noImplicitRecursion())) {
if (GlobalData.getOptions().InputVerificationHandler)
GlobalData.getOptions().InputVerificationHandler(File, OS.str());
}
}
Error DWARFLinkerImpl::validateAndUpdateOptions() {
if (GlobalData.getOptions().TargetDWARFVersion == 0)
return createStringError(std::errc::invalid_argument,
"target DWARF version is not set");
if (GlobalData.getOptions().Verbose && GlobalData.getOptions().Threads != 1) {
GlobalData.Options.Threads = 1;
GlobalData.warn(
"set number of threads to 1 to make --verbose to work properly.", "");
}
// Do not do types deduplication in case --update.
if (GlobalData.getOptions().UpdateIndexTablesOnly &&
!GlobalData.Options.NoODR)
GlobalData.Options.NoODR = true;
return Error::success();
}
/// Resolve the relative path to a build artifact referenced by DWARF by
/// applying DW_AT_comp_dir.
static void resolveRelativeObjectPath(SmallVectorImpl<char> &Buf, DWARFDie CU) {
sys::path::append(Buf, dwarf::toString(CU.find(dwarf::DW_AT_comp_dir), ""));
}
static uint64_t getDwoId(const DWARFDie &CUDie) {
auto DwoId = dwarf::toUnsigned(
CUDie.find({dwarf::DW_AT_dwo_id, dwarf::DW_AT_GNU_dwo_id}));
if (DwoId)
return *DwoId;
return 0;
}
static std::string
remapPath(StringRef Path,
const DWARFLinker::ObjectPrefixMapTy &ObjectPrefixMap) {
if (ObjectPrefixMap.empty())
return Path.str();
SmallString<256> p = Path;
for (const auto &Entry : ObjectPrefixMap)
if (llvm::sys::path::replace_path_prefix(p, Entry.first, Entry.second))
break;
return p.str().str();
}
static std::string getPCMFile(const DWARFDie &CUDie,
DWARFLinker::ObjectPrefixMapTy *ObjectPrefixMap) {
std::string PCMFile = dwarf::toString(
CUDie.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), "");
if (PCMFile.empty())
return PCMFile;
if (ObjectPrefixMap)
PCMFile = remapPath(PCMFile, *ObjectPrefixMap);
return PCMFile;
}
std::pair<bool, bool> DWARFLinkerImpl::LinkContext::isClangModuleRef(
const DWARFDie &CUDie, std::string &PCMFile, unsigned Indent, bool Quiet) {
if (PCMFile.empty())
return std::make_pair(false, false);
// Clang module DWARF skeleton CUs abuse this for the path to the module.
uint64_t DwoId = getDwoId(CUDie);
std::string Name = dwarf::toString(CUDie.find(dwarf::DW_AT_name), "");
if (Name.empty()) {
if (!Quiet)
GlobalData.warn("anonymous module skeleton CU for " + PCMFile + ".",
InputDWARFFile.FileName);
return std::make_pair(true, true);
}
if (!Quiet && GlobalData.getOptions().Verbose) {
outs().indent(Indent);
outs() << "Found clang module reference " << PCMFile;
}
auto Cached = ClangModules.find(PCMFile);
if (Cached != ClangModules.end()) {
// FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is
// fixed in clang, only warn about DWO_id mismatches in verbose mode.
// ASTFileSignatures will change randomly when a module is rebuilt.
if (!Quiet && GlobalData.getOptions().Verbose && (Cached->second != DwoId))
GlobalData.warn(
Twine("hash mismatch: this object file was built against a "
"different version of the module ") +
PCMFile + ".",
InputDWARFFile.FileName);
if (!Quiet && GlobalData.getOptions().Verbose)
outs() << " [cached].\n";
return std::make_pair(true, true);
}
return std::make_pair(true, false);
}
/// If this compile unit is really a skeleton CU that points to a
/// clang module, register it in ClangModules and return true.
///
/// A skeleton CU is a CU without children, a DW_AT_gnu_dwo_name
/// pointing to the module, and a DW_AT_gnu_dwo_id with the module
/// hash.
bool DWARFLinkerImpl::LinkContext::registerModuleReference(
const DWARFDie &CUDie, ObjFileLoaderTy Loader,
CompileUnitHandlerTy OnCUDieLoaded, unsigned Indent) {
std::string PCMFile =
getPCMFile(CUDie, GlobalData.getOptions().ObjectPrefixMap);
std::pair<bool, bool> IsClangModuleRef =
isClangModuleRef(CUDie, PCMFile, Indent, false);
if (!IsClangModuleRef.first)
return false;
if (IsClangModuleRef.second)
return true;
if (GlobalData.getOptions().Verbose)
outs() << " ...\n";
// Cyclic dependencies are disallowed by Clang, but we still
// shouldn't run into an infinite loop, so mark it as processed now.
ClangModules.insert({PCMFile, getDwoId(CUDie)});
if (Error E =
loadClangModule(Loader, CUDie, PCMFile, OnCUDieLoaded, Indent + 2)) {
consumeError(std::move(E));
return false;
}
return true;
}
Error DWARFLinkerImpl::LinkContext::loadClangModule(
ObjFileLoaderTy Loader, const DWARFDie &CUDie, const std::string &PCMFile,
CompileUnitHandlerTy OnCUDieLoaded, unsigned Indent) {
uint64_t DwoId = getDwoId(CUDie);
std::string ModuleName = dwarf::toString(CUDie.find(dwarf::DW_AT_name), "");
/// Using a SmallString<0> because loadClangModule() is recursive.
SmallString<0> Path(GlobalData.getOptions().PrependPath);
if (sys::path::is_relative(PCMFile))
resolveRelativeObjectPath(Path, CUDie);
sys::path::append(Path, PCMFile);
// Don't use the cached binary holder because we have no thread-safety
// guarantee and the lifetime is limited.
if (Loader == nullptr) {
GlobalData.error("cann't load clang module: loader is not specified.",
InputDWARFFile.FileName);
return Error::success();
}
auto ErrOrObj = Loader(InputDWARFFile.FileName, Path);
if (!ErrOrObj)
return Error::success();
std::unique_ptr<CompileUnit> Unit;
for (const auto &CU : ErrOrObj->Dwarf->compile_units()) {
OnCUDieLoaded(*CU);
// Recursively get all modules imported by this one.
auto ChildCUDie = CU->getUnitDIE();
if (!ChildCUDie)
continue;
if (!registerModuleReference(ChildCUDie, Loader, OnCUDieLoaded, Indent)) {
if (Unit) {
std::string Err =
(PCMFile +
": Clang modules are expected to have exactly 1 compile unit.\n");
GlobalData.error(Err, InputDWARFFile.FileName);
return make_error<StringError>(Err, inconvertibleErrorCode());
}
// FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is
// fixed in clang, only warn about DWO_id mismatches in verbose mode.
// ASTFileSignatures will change randomly when a module is rebuilt.
uint64_t PCMDwoId = getDwoId(ChildCUDie);
if (PCMDwoId != DwoId) {
if (GlobalData.getOptions().Verbose)
GlobalData.warn(
Twine("hash mismatch: this object file was built against a "
"different version of the module ") +
PCMFile + ".",
InputDWARFFile.FileName);
// Update the cache entry with the DwoId of the module loaded from disk.
ClangModules[PCMFile] = PCMDwoId;
}
// Empty modules units should not be cloned.
if (!ChildCUDie.hasChildren())
continue;
// Add this module.
Unit = std::make_unique<CompileUnit>(
GlobalData, *CU, UniqueUnitID.fetch_add(1), ModuleName, *ErrOrObj,
getUnitForOffset, CU->getFormParams(), getEndianness());
}
}
if (Unit) {
ModulesCompileUnits.emplace_back(RefModuleUnit{*ErrOrObj, std::move(Unit)});
// Preload line table, as it can't be loaded asynchronously.
ModulesCompileUnits.back().Unit->loadLineTable();
}
return Error::success();
}
Error DWARFLinkerImpl::LinkContext::link(TypeUnit *ArtificialTypeUnit) {
InterCUProcessingStarted = false;
if (!InputDWARFFile.Dwarf)
return Error::success();
// Preload macro tables, as they can't be loaded asynchronously.
InputDWARFFile.Dwarf->getDebugMacinfo();
InputDWARFFile.Dwarf->getDebugMacro();
// Link modules compile units first.
parallelForEach(ModulesCompileUnits, [&](RefModuleUnit &RefModule) {
linkSingleCompileUnit(*RefModule.Unit, ArtificialTypeUnit);
});
// Check for live relocations. If there is no any live relocation then we
// can skip entire object file.
if (!GlobalData.getOptions().UpdateIndexTablesOnly &&
!InputDWARFFile.Addresses->hasValidRelocs()) {
if (GlobalData.getOptions().Verbose)
outs() << "No valid relocations found. Skipping.\n";
return Error::success();
}
OriginalDebugInfoSize = getInputDebugInfoSize();
// Create CompileUnit structures to keep information about source
// DWARFUnit`s, load line tables.
for (const auto &OrigCU : InputDWARFFile.Dwarf->compile_units()) {
// Load only unit DIE at this stage.
auto CUDie = OrigCU->getUnitDIE();
std::string PCMFile =
getPCMFile(CUDie, GlobalData.getOptions().ObjectPrefixMap);
// The !isClangModuleRef condition effectively skips over fully resolved
// skeleton units.
if (!CUDie || GlobalData.getOptions().UpdateIndexTablesOnly ||
!isClangModuleRef(CUDie, PCMFile, 0, true).first) {
CompileUnits.emplace_back(std::make_unique<CompileUnit>(
GlobalData, *OrigCU, UniqueUnitID.fetch_add(1), "", InputDWARFFile,
getUnitForOffset, OrigCU->getFormParams(), getEndianness()));
// Preload line table, as it can't be loaded asynchronously.
CompileUnits.back()->loadLineTable();
}
};
HasNewInterconnectedCUs = false;
// Link self-sufficient compile units and discover inter-connected compile
// units.
parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
linkSingleCompileUnit(*CU, ArtificialTypeUnit);
});
// Link all inter-connected units.
if (HasNewInterconnectedCUs) {
InterCUProcessingStarted = true;
if (Error Err = finiteLoop([&]() -> Expected<bool> {
HasNewInterconnectedCUs = false;
// Load inter-connected units.
parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
if (CU->isInterconnectedCU()) {
CU->maybeResetToLoadedStage();
linkSingleCompileUnit(*CU, ArtificialTypeUnit,
CompileUnit::Stage::Loaded);
}
});
// Do liveness analysis for inter-connected units.
parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
linkSingleCompileUnit(*CU, ArtificialTypeUnit,
CompileUnit::Stage::LivenessAnalysisDone);
});
return HasNewInterconnectedCUs.load();
}))
return Err;
// Update dependencies.
if (Error Err = finiteLoop([&]() -> Expected<bool> {
HasNewGlobalDependency = false;
parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
linkSingleCompileUnit(
*CU, ArtificialTypeUnit,
CompileUnit::Stage::UpdateDependenciesCompleteness);
});
return HasNewGlobalDependency.load();
}))
return Err;
parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
if (CU->isInterconnectedCU() &&
CU->getStage() == CompileUnit::Stage::LivenessAnalysisDone)
CU->setStage(CompileUnit::Stage::UpdateDependenciesCompleteness);
});
// Assign type names.
parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
linkSingleCompileUnit(*CU, ArtificialTypeUnit,
CompileUnit::Stage::TypeNamesAssigned);
});
// Clone inter-connected units.
parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
linkSingleCompileUnit(*CU, ArtificialTypeUnit,
CompileUnit::Stage::Cloned);
});
// Update patches for inter-connected units.
parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
linkSingleCompileUnit(*CU, ArtificialTypeUnit,
CompileUnit::Stage::PatchesUpdated);
});
// Release data.
parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
linkSingleCompileUnit(*CU, ArtificialTypeUnit,
CompileUnit::Stage::Cleaned);
});
}
if (GlobalData.getOptions().UpdateIndexTablesOnly) {
// Emit Invariant sections.
if (Error Err = emitInvariantSections())
return Err;
} else if (!CompileUnits.empty()) {
// Emit .debug_frame section.
Error ResultErr = Error::success();
llvm::parallel::TaskGroup TGroup;
// We use task group here as PerThreadBumpPtrAllocator should be called from
// the threads created by ThreadPoolExecutor.
TGroup.spawn([&]() {
if (Error Err = cloneAndEmitDebugFrame())
ResultErr = std::move(Err);
});
return ResultErr;
}
return Error::success();
}
void DWARFLinkerImpl::LinkContext::linkSingleCompileUnit(
CompileUnit &CU, TypeUnit *ArtificialTypeUnit,
enum CompileUnit::Stage DoUntilStage) {
if (InterCUProcessingStarted != CU.isInterconnectedCU())
return;
if (Error Err = finiteLoop([&]() -> Expected<bool> {
if (CU.getStage() >= DoUntilStage)
return false;
switch (CU.getStage()) {
case CompileUnit::Stage::CreatedNotLoaded: {
// Load input compilation unit DIEs.
// Analyze properties of DIEs.
if (!CU.loadInputDIEs()) {
// We do not need to do liveness analysis for invalid compilation
// unit.
CU.setStage(CompileUnit::Stage::Skipped);
} else {
CU.analyzeDWARFStructure();
// The registerModuleReference() condition effectively skips
// over fully resolved skeleton units. This second pass of
// registerModuleReferences doesn't do any new work, but it
// will collect top-level errors, which are suppressed. Module
// warnings were already displayed in the first iteration.
if (registerModuleReference(
CU.getOrigUnit().getUnitDIE(), nullptr,
[](const DWARFUnit &) {}, 0))
CU.setStage(CompileUnit::Stage::PatchesUpdated);
else
CU.setStage(CompileUnit::Stage::Loaded);
}
} break;
case CompileUnit::Stage::Loaded: {
// Mark all the DIEs that need to be present in the generated output.
// If ODR requested, build type names.
if (!CU.resolveDependenciesAndMarkLiveness(InterCUProcessingStarted,
HasNewInterconnectedCUs)) {
assert(HasNewInterconnectedCUs &&
"Flag indicating new inter-connections is not set");
return false;
}
CU.setStage(CompileUnit::Stage::LivenessAnalysisDone);
} break;
case CompileUnit::Stage::LivenessAnalysisDone: {
if (InterCUProcessingStarted) {
if (CU.updateDependenciesCompleteness())
HasNewGlobalDependency = true;
return false;
} else {
if (Error Err = finiteLoop([&]() -> Expected<bool> {
return CU.updateDependenciesCompleteness();
}))
return std::move(Err);
CU.setStage(CompileUnit::Stage::UpdateDependenciesCompleteness);
}
} break;
case CompileUnit::Stage::UpdateDependenciesCompleteness:
#ifndef NDEBUG
CU.verifyDependencies();
#endif
if (ArtificialTypeUnit) {
if (Error Err =
CU.assignTypeNames(ArtificialTypeUnit->getTypePool()))
return std::move(Err);
}
CU.setStage(CompileUnit::Stage::TypeNamesAssigned);
break;
case CompileUnit::Stage::TypeNamesAssigned:
// Clone input compile unit.
if (CU.isClangModule() ||
GlobalData.getOptions().UpdateIndexTablesOnly ||
CU.getContaingFile().Addresses->hasValidRelocs()) {
if (Error Err = CU.cloneAndEmit(GlobalData.getTargetTriple(),
ArtificialTypeUnit))
return std::move(Err);
}
CU.setStage(CompileUnit::Stage::Cloned);
break;
case CompileUnit::Stage::Cloned:
// Update DIEs referencies.
CU.updateDieRefPatchesWithClonedOffsets();
CU.setStage(CompileUnit::Stage::PatchesUpdated);
break;
case CompileUnit::Stage::PatchesUpdated:
// Cleanup resources.
CU.cleanupDataAfterClonning();
CU.setStage(CompileUnit::Stage::Cleaned);
break;
case CompileUnit::Stage::Cleaned:
assert(false);
break;
case CompileUnit::Stage::Skipped:
// Nothing to do.
break;
}
return true;
})) {
CU.error(std::move(Err));
CU.cleanupDataAfterClonning();
CU.setStage(CompileUnit::Stage::Skipped);
}
}
Error DWARFLinkerImpl::LinkContext::emitInvariantSections() {
if (!GlobalData.getTargetTriple().has_value())
return Error::success();
getOrCreateSectionDescriptor(DebugSectionKind::DebugLoc).OS
<< InputDWARFFile.Dwarf->getDWARFObj().getLocSection().Data;
getOrCreateSectionDescriptor(DebugSectionKind::DebugLocLists).OS
<< InputDWARFFile.Dwarf->getDWARFObj().getLoclistsSection().Data;
getOrCreateSectionDescriptor(DebugSectionKind::DebugRange).OS
<< InputDWARFFile.Dwarf->getDWARFObj().getRangesSection().Data;
getOrCreateSectionDescriptor(DebugSectionKind::DebugRngLists).OS
<< InputDWARFFile.Dwarf->getDWARFObj().getRnglistsSection().Data;
getOrCreateSectionDescriptor(DebugSectionKind::DebugARanges).OS
<< InputDWARFFile.Dwarf->getDWARFObj().getArangesSection();
getOrCreateSectionDescriptor(DebugSectionKind::DebugFrame).OS
<< InputDWARFFile.Dwarf->getDWARFObj().getFrameSection().Data;
getOrCreateSectionDescriptor(DebugSectionKind::DebugAddr).OS
<< InputDWARFFile.Dwarf->getDWARFObj().getAddrSection().Data;
return Error::success();
}
Error DWARFLinkerImpl::LinkContext::cloneAndEmitDebugFrame() {
if (!GlobalData.getTargetTriple().has_value())
return Error::success();
if (InputDWARFFile.Dwarf.get() == nullptr)
return Error::success();
const DWARFObject &InputDWARFObj = InputDWARFFile.Dwarf->getDWARFObj();
StringRef OrigFrameData = InputDWARFObj.getFrameSection().Data;
if (OrigFrameData.empty())
return Error::success();
RangesTy AllUnitsRanges;
for (std::unique_ptr<CompileUnit> &Unit : CompileUnits) {
for (auto CurRange : Unit->getFunctionRanges())
AllUnitsRanges.insert(CurRange.Range, CurRange.Value);
}
unsigned SrcAddrSize = InputDWARFObj.getAddressSize();
SectionDescriptor &OutSection =
getOrCreateSectionDescriptor(DebugSectionKind::DebugFrame);
DataExtractor Data(OrigFrameData, InputDWARFObj.isLittleEndian(), 0);
uint64_t InputOffset = 0;
// Store the data of the CIEs defined in this object, keyed by their
// offsets.
DenseMap<uint64_t, StringRef> LocalCIES;
/// The CIEs that have been emitted in the output section. The actual CIE
/// data serves a the key to this StringMap.
StringMap<uint32_t> EmittedCIEs;
while (Data.isValidOffset(InputOffset)) {
uint64_t EntryOffset = InputOffset;
uint32_t InitialLength = Data.getU32(&InputOffset);
if (InitialLength == 0xFFFFFFFF)
return createFileError(InputDWARFObj.getFileName(),
createStringError(std::errc::invalid_argument,
"Dwarf64 bits no supported"));
uint32_t CIEId = Data.getU32(&InputOffset);
if (CIEId == 0xFFFFFFFF) {
// This is a CIE, store it.
StringRef CIEData = OrigFrameData.substr(EntryOffset, InitialLength + 4);
LocalCIES[EntryOffset] = CIEData;
// The -4 is to account for the CIEId we just read.
InputOffset += InitialLength - 4;
continue;
}
uint64_t Loc = Data.getUnsigned(&InputOffset, SrcAddrSize);
// Some compilers seem to emit frame info that doesn't start at
// the function entry point, thus we can't just lookup the address
// in the debug map. Use the AddressInfo's range map to see if the FDE
// describes something that we can relocate.
std::optional<AddressRangeValuePair> Range =
AllUnitsRanges.getRangeThatContains(Loc);
if (!Range) {
// The +4 is to account for the size of the InitialLength field itself.
InputOffset = EntryOffset + InitialLength + 4;
continue;
}
// This is an FDE, and we have a mapping.
// Have we already emitted a corresponding CIE?
StringRef CIEData = LocalCIES[CIEId];
if (CIEData.empty())
return createFileError(
InputDWARFObj.getFileName(),
createStringError(std::errc::invalid_argument,
"Inconsistent debug_frame content. Dropping."));
uint64_t OffsetToCIERecord = OutSection.OS.tell();
// Look if we already emitted a CIE that corresponds to the
// referenced one (the CIE data is the key of that lookup).
auto IteratorInserted =
EmittedCIEs.insert(std::make_pair(CIEData, OffsetToCIERecord));
OffsetToCIERecord = IteratorInserted.first->getValue();
// Emit CIE for this ID if it is not emitted yet.
if (IteratorInserted.second)
OutSection.OS << CIEData;
// Remember offset to the FDE record, so that we might update
// field referencing CIE record(containing OffsetToCIERecord),
// when final offsets are known. OffsetToCIERecord(which is written later)
// is local to the current .debug_frame section, it should be updated
// with final offset of the .debug_frame section.
OutSection.notePatch(
DebugOffsetPatch{OutSection.OS.tell() + 4, &OutSection, true});
// Emit the FDE with updated address and CIE pointer.
// (4 + AddrSize) is the size of the CIEId + initial_location
// fields that will get reconstructed by emitFDE().
unsigned FDERemainingBytes = InitialLength - (4 + SrcAddrSize);
emitFDE(OffsetToCIERecord, SrcAddrSize, Loc + Range->Value,
OrigFrameData.substr(InputOffset, FDERemainingBytes), OutSection);
InputOffset += FDERemainingBytes;
}
return Error::success();
}
/// Emit a FDE into the debug_frame section. \p FDEBytes
/// contains the FDE data without the length, CIE offset and address
/// which will be replaced with the parameter values.
void DWARFLinkerImpl::LinkContext::emitFDE(uint32_t CIEOffset,
uint32_t AddrSize, uint64_t Address,
StringRef FDEBytes,
SectionDescriptor &Section) {
Section.emitIntVal(FDEBytes.size() + 4 + AddrSize, 4);
Section.emitIntVal(CIEOffset, 4);
Section.emitIntVal(Address, AddrSize);
Section.OS.write(FDEBytes.data(), FDEBytes.size());
}
void DWARFLinkerImpl::glueCompileUnitsAndWriteToTheOutput() {
if (!GlobalData.getTargetTriple().has_value())
return;
assert(SectionHandler);
// Go through all object files, all compile units and assign
// offsets to them.
assignOffsets();
// Patch size/offsets fields according to the assigned CU offsets.
patchOffsetsAndSizes();
// Emit common sections and write debug tables from all object files/compile
// units into the resulting file.
emitCommonSectionsAndWriteCompileUnitsToTheOutput();
if (ArtificialTypeUnit.get() != nullptr)
ArtificialTypeUnit.reset();
// Write common debug sections into the resulting file.
writeCommonSectionsToTheOutput();
// Cleanup data.
cleanupDataAfterDWARFOutputIsWritten();
if (GlobalData.getOptions().Statistics)
printStatistic();
}
void DWARFLinkerImpl::printStatistic() {
// For each object file map how many bytes were emitted.
StringMap<DebugInfoSize> SizeByObject;
for (const std::unique_ptr<LinkContext> &Context : ObjectContexts) {
uint64_t AllDebugInfoSectionsSize = 0;
for (std::unique_ptr<CompileUnit> &CU : Context->CompileUnits)
if (std::optional<SectionDescriptor *> DebugInfo =
CU->tryGetSectionDescriptor(DebugSectionKind::DebugInfo))
AllDebugInfoSectionsSize += (*DebugInfo)->getContents().size();
SizeByObject[Context->InputDWARFFile.FileName].Input =
Context->OriginalDebugInfoSize;
SizeByObject[Context->InputDWARFFile.FileName].Output =
AllDebugInfoSectionsSize;
}
// Create a vector sorted in descending order by output size.
std::vector<std::pair<StringRef, DebugInfoSize>> Sorted;
for (auto &E : SizeByObject)
Sorted.emplace_back(E.first(), E.second);
llvm::sort(Sorted, [](auto &LHS, auto &RHS) {
return LHS.second.Output > RHS.second.Output;
});
auto ComputePercentange = [](int64_t Input, int64_t Output) -> float {
const float Difference = Output - Input;
const float Sum = Input + Output;
if (Sum == 0)
return 0;
return (Difference / (Sum / 2));
};
int64_t InputTotal = 0;
int64_t OutputTotal = 0;
const char *FormatStr = "{0,-45} {1,10}b {2,10}b {3,8:P}\n";
// Print header.
outs() << ".debug_info section size (in bytes)\n";
outs() << "----------------------------------------------------------------"
"---------------\n";
outs() << "Filename Object "
" dSYM Change\n";
outs() << "----------------------------------------------------------------"
"---------------\n";
// Print body.
for (auto &E : Sorted) {
InputTotal += E.second.Input;
OutputTotal += E.second.Output;
llvm::outs() << formatv(
FormatStr, sys::path::filename(E.first).take_back(45), E.second.Input,
E.second.Output, ComputePercentange(E.second.Input, E.second.Output));
}
// Print total and footer.
outs() << "----------------------------------------------------------------"
"---------------\n";
llvm::outs() << formatv(FormatStr, "Total", InputTotal, OutputTotal,
ComputePercentange(InputTotal, OutputTotal));
outs() << "----------------------------------------------------------------"
"---------------\n\n";
}
void DWARFLinkerImpl::assignOffsets() {
llvm::parallel::TaskGroup TGroup;
TGroup.spawn([&]() { assignOffsetsToStrings(); });
TGroup.spawn([&]() { assignOffsetsToSections(); });
}
void DWARFLinkerImpl::assignOffsetsToStrings() {
size_t CurDebugStrIndex = 1; // start from 1 to take into account zero entry.
uint64_t CurDebugStrOffset =
1; // start from 1 to take into account zero entry.
size_t CurDebugLineStrIndex = 0;
uint64_t CurDebugLineStrOffset = 0;
// Enumerates all strings, add them into the DwarfStringPoolEntry map,
// assign offset and index to the string if it is not indexed yet.
forEachOutputString([&](StringDestinationKind Kind,
const StringEntry *String) {
switch (Kind) {
case StringDestinationKind::DebugStr: {
DwarfStringPoolEntryWithExtString *Entry = DebugStrStrings.add(String);
assert(Entry != nullptr);
if (!Entry->isIndexed()) {
Entry->Offset = CurDebugStrOffset;
CurDebugStrOffset += Entry->String.size() + 1;
Entry->Index = CurDebugStrIndex++;
}
} break;
case StringDestinationKind::DebugLineStr: {
DwarfStringPoolEntryWithExtString *Entry =
DebugLineStrStrings.add(String);
assert(Entry != nullptr);
if (!Entry->isIndexed()) {
Entry->Offset = CurDebugLineStrOffset;
CurDebugLineStrOffset += Entry->String.size() + 1;
Entry->Index = CurDebugLineStrIndex++;
}
} break;
}
});
}
void DWARFLinkerImpl::assignOffsetsToSections() {
std::array<uint64_t, SectionKindsNum> SectionSizesAccumulator = {0};
forEachObjectSectionsSet([&](OutputSections &UnitSections) {
UnitSections.assignSectionsOffsetAndAccumulateSize(SectionSizesAccumulator);
});
}
void DWARFLinkerImpl::forEachOutputString(
function_ref<void(StringDestinationKind Kind, const StringEntry *String)>
StringHandler) {
// To save space we do not create any separate string table.
// We use already allocated string patches and accelerator entries:
// enumerate them in natural order and assign offsets.
// ASSUMPTION: strings should be stored into .debug_str/.debug_line_str
// sections in the same order as they were assigned offsets.
forEachCompileUnit([&](CompileUnit *CU) {
CU->forEach([&](SectionDescriptor &OutSection) {
OutSection.ListDebugStrPatch.forEach([&](DebugStrPatch &Patch) {
StringHandler(StringDestinationKind::DebugStr, Patch.String);
});
OutSection.ListDebugLineStrPatch.forEach([&](DebugLineStrPatch &Patch) {
StringHandler(StringDestinationKind::DebugLineStr, Patch.String);
});
});
CU->forEachAcceleratorRecord([&](DwarfUnit::AccelInfo &Info) {
StringHandler(DebugStr, Info.String);
});
});
if (ArtificialTypeUnit.get() != nullptr) {
ArtificialTypeUnit->forEach([&](SectionDescriptor &OutSection) {
OutSection.ListDebugStrPatch.forEach([&](DebugStrPatch &Patch) {
StringHandler(StringDestinationKind::DebugStr, Patch.String);
});
OutSection.ListDebugLineStrPatch.forEach([&](DebugLineStrPatch &Patch) {
StringHandler(StringDestinationKind::DebugLineStr, Patch.String);
});
OutSection.ListDebugTypeStrPatch.forEach([&](DebugTypeStrPatch &Patch) {
if (Patch.Die == nullptr)
return;
StringHandler(StringDestinationKind::DebugStr, Patch.String);
});
OutSection.ListDebugTypeLineStrPatch.forEach(
[&](DebugTypeLineStrPatch &Patch) {
if (Patch.Die == nullptr)
return;
StringHandler(StringDestinationKind::DebugStr, Patch.String);
});
});
}
}
void DWARFLinkerImpl::forEachObjectSectionsSet(
function_ref<void(OutputSections &)> SectionsSetHandler) {
// Handle artificial type unit first.
if (ArtificialTypeUnit.get() != nullptr)
SectionsSetHandler(*ArtificialTypeUnit);
// Then all modules(before regular compilation units).
for (const std::unique_ptr<LinkContext> &Context : ObjectContexts)
for (LinkContext::RefModuleUnit &ModuleUnit : Context->ModulesCompileUnits)
if (ModuleUnit.Unit->getStage() != CompileUnit::Stage::Skipped)
SectionsSetHandler(*ModuleUnit.Unit);
// Finally all compilation units.
for (const std::unique_ptr<LinkContext> &Context : ObjectContexts) {
// Handle object file common sections.
SectionsSetHandler(*Context);
// Handle compilation units.
for (std::unique_ptr<CompileUnit> &CU : Context->CompileUnits)
if (CU->getStage() != CompileUnit::Stage::Skipped)
SectionsSetHandler(*CU);
}
}
void DWARFLinkerImpl::forEachCompileAndTypeUnit(
function_ref<void(DwarfUnit *CU)> UnitHandler) {
if (ArtificialTypeUnit.get() != nullptr)
UnitHandler(ArtificialTypeUnit.get());
// Enumerate module units.
for (const std::unique_ptr<LinkContext> &Context : ObjectContexts)
for (LinkContext::RefModuleUnit &ModuleUnit : Context->ModulesCompileUnits)
if (ModuleUnit.Unit->getStage() != CompileUnit::Stage::Skipped)
UnitHandler(ModuleUnit.Unit.get());
// Enumerate compile units.
for (const std::unique_ptr<LinkContext> &Context : ObjectContexts)
for (std::unique_ptr<CompileUnit> &CU : Context->CompileUnits)
if (CU->getStage() != CompileUnit::Stage::Skipped)
UnitHandler(CU.get());
}
void DWARFLinkerImpl::forEachCompileUnit(
function_ref<void(CompileUnit *CU)> UnitHandler) {
// Enumerate module units.
for (const std::unique_ptr<LinkContext> &Context : ObjectContexts)
for (LinkContext::RefModuleUnit &ModuleUnit : Context->ModulesCompileUnits)
if (ModuleUnit.Unit->getStage() != CompileUnit::Stage::Skipped)
UnitHandler(ModuleUnit.Unit.get());
// Enumerate compile units.
for (const std::unique_ptr<LinkContext> &Context : ObjectContexts)
for (std::unique_ptr<CompileUnit> &CU : Context->CompileUnits)
if (CU->getStage() != CompileUnit::Stage::Skipped)
UnitHandler(CU.get());
}
void DWARFLinkerImpl::patchOffsetsAndSizes() {
forEachObjectSectionsSet([&](OutputSections &SectionsSet) {
SectionsSet.forEach([&](SectionDescriptor &OutSection) {
SectionsSet.applyPatches(OutSection, DebugStrStrings, DebugLineStrStrings,
ArtificialTypeUnit.get());
});
});
}
void DWARFLinkerImpl::emitCommonSectionsAndWriteCompileUnitsToTheOutput() {
llvm::parallel::TaskGroup TG;
// Create section descriptors ahead if they are not exist at the moment.
// SectionDescriptors container is not thread safe. Thus we should be sure
// that descriptors would not be created in following parallel tasks.
CommonSections.getOrCreateSectionDescriptor(DebugSectionKind::DebugStr);
CommonSections.getOrCreateSectionDescriptor(DebugSectionKind::DebugLineStr);
if (llvm::is_contained(GlobalData.Options.AccelTables,
AccelTableKind::Apple)) {
CommonSections.getOrCreateSectionDescriptor(DebugSectionKind::AppleNames);
CommonSections.getOrCreateSectionDescriptor(
DebugSectionKind::AppleNamespaces);
CommonSections.getOrCreateSectionDescriptor(DebugSectionKind::AppleObjC);
CommonSections.getOrCreateSectionDescriptor(DebugSectionKind::AppleTypes);
}
if (llvm::is_contained(GlobalData.Options.AccelTables,
AccelTableKind::DebugNames))
CommonSections.getOrCreateSectionDescriptor(DebugSectionKind::DebugNames);
// Emit .debug_str and .debug_line_str sections.
TG.spawn([&]() { emitStringSections(); });
if (llvm::is_contained(GlobalData.Options.AccelTables,
AccelTableKind::Apple)) {
// Emit apple accelerator sections.
TG.spawn([&]() {
emitAppleAcceleratorSections((*GlobalData.getTargetTriple()).get());
});
}
if (llvm::is_contained(GlobalData.Options.AccelTables,
AccelTableKind::DebugNames)) {
// Emit .debug_names section.
TG.spawn([&]() {
emitDWARFv5DebugNamesSection((*GlobalData.getTargetTriple()).get());
});
}
// Write compile units to the output file.
TG.spawn([&]() { writeCompileUnitsToTheOutput(); });
}
void DWARFLinkerImpl::emitStringSections() {
uint64_t DebugStrNextOffset = 0;
uint64_t DebugLineStrNextOffset = 0;
// Emit zero length string. Accelerator tables does not work correctly
// if the first string is not zero length string.
CommonSections.getSectionDescriptor(DebugSectionKind::DebugStr)
.emitInplaceString("");
DebugStrNextOffset++;
forEachOutputString(
[&](StringDestinationKind Kind, const StringEntry *String) {
switch (Kind) {
case StringDestinationKind::DebugStr: {
DwarfStringPoolEntryWithExtString *StringToEmit =
DebugStrStrings.getExistingEntry(String);
assert(StringToEmit->isIndexed());
// Strings may be repeated. Use accumulated DebugStrNextOffset
// to understand whether corresponding string is already emitted.
// Skip string if its offset less than accumulated offset.
if (StringToEmit->Offset >= DebugStrNextOffset) {
DebugStrNextOffset =
StringToEmit->Offset + StringToEmit->String.size() + 1;
// Emit the string itself.
CommonSections.getSectionDescriptor(DebugSectionKind::DebugStr)
.emitInplaceString(StringToEmit->String);
}
} break;
case StringDestinationKind::DebugLineStr: {
DwarfStringPoolEntryWithExtString *StringToEmit =
DebugLineStrStrings.getExistingEntry(String);
assert(StringToEmit->isIndexed());
// Strings may be repeated. Use accumulated DebugLineStrStrings
// to understand whether corresponding string is already emitted.
// Skip string if its offset less than accumulated offset.
if (StringToEmit->Offset >= DebugLineStrNextOffset) {
DebugLineStrNextOffset =
StringToEmit->Offset + StringToEmit->String.size() + 1;
// Emit the string itself.
CommonSections.getSectionDescriptor(DebugSectionKind::DebugLineStr)
.emitInplaceString(StringToEmit->String);
}
} break;
}
});
}
void DWARFLinkerImpl::emitAppleAcceleratorSections(const Triple &TargetTriple) {
AccelTable<AppleAccelTableStaticOffsetData> AppleNamespaces;
AccelTable<AppleAccelTableStaticOffsetData> AppleNames;
AccelTable<AppleAccelTableStaticOffsetData> AppleObjC;
AccelTable<AppleAccelTableStaticTypeData> AppleTypes;
forEachCompileAndTypeUnit([&](DwarfUnit *CU) {
CU->forEachAcceleratorRecord([&](const DwarfUnit::AccelInfo &Info) {
uint64_t OutOffset = Info.OutOffset;
switch (Info.Type) {
case DwarfUnit::AccelType::None: {
llvm_unreachable("Unknown accelerator record");
} break;
case DwarfUnit::AccelType::Namespace: {
AppleNamespaces.addName(
*DebugStrStrings.getExistingEntry(Info.String),
CU->getSectionDescriptor(DebugSectionKind::DebugInfo).StartOffset +
OutOffset);
} break;
case DwarfUnit::AccelType::Name: {
AppleNames.addName(
*DebugStrStrings.getExistingEntry(Info.String),
CU->getSectionDescriptor(DebugSectionKind::DebugInfo).StartOffset +
OutOffset);
} break;
case DwarfUnit::AccelType::ObjC: {
AppleObjC.addName(
*DebugStrStrings.getExistingEntry(Info.String),
CU->getSectionDescriptor(DebugSectionKind::DebugInfo).StartOffset +
OutOffset);
} break;
case DwarfUnit::AccelType::Type: {
AppleTypes.addName(
*DebugStrStrings.getExistingEntry(Info.String),
CU->getSectionDescriptor(DebugSectionKind::DebugInfo).StartOffset +
OutOffset,
Info.Tag,
Info.ObjcClassImplementation ? dwarf::DW_FLAG_type_implementation
: 0,
Info.QualifiedNameHash);
} break;
}
});
});
{
// FIXME: we use AsmPrinter to emit accelerator sections.
// It might be beneficial to directly emit accelerator data
// to the raw_svector_ostream.
SectionDescriptor &OutSection =
CommonSections.getSectionDescriptor(DebugSectionKind::AppleNamespaces);
DwarfEmitterImpl Emitter(DWARFLinker::OutputFileType::Object,
OutSection.OS);
if (Error Err = Emitter.init(TargetTriple, "__DWARF")) {
consumeError(std::move(Err));
return;
}
// Emit table.
Emitter.emitAppleNamespaces(AppleNamespaces);
Emitter.finish();
// Set start offset and size for output section.
OutSection.setSizesForSectionCreatedByAsmPrinter();
}
{
// FIXME: we use AsmPrinter to emit accelerator sections.
// It might be beneficial to directly emit accelerator data
// to the raw_svector_ostream.
SectionDescriptor &OutSection =
CommonSections.getSectionDescriptor(DebugSectionKind::AppleNames);
DwarfEmitterImpl Emitter(DWARFLinker::OutputFileType::Object,
OutSection.OS);
if (Error Err = Emitter.init(TargetTriple, "__DWARF")) {
consumeError(std::move(Err));
return;
}
// Emit table.
Emitter.emitAppleNames(AppleNames);
Emitter.finish();
// Set start offset ans size for output section.
OutSection.setSizesForSectionCreatedByAsmPrinter();
}
{
// FIXME: we use AsmPrinter to emit accelerator sections.
// It might be beneficial to directly emit accelerator data
// to the raw_svector_ostream.
SectionDescriptor &OutSection =
CommonSections.getSectionDescriptor(DebugSectionKind::AppleObjC);
DwarfEmitterImpl Emitter(DWARFLinker::OutputFileType::Object,
OutSection.OS);
if (Error Err = Emitter.init(TargetTriple, "__DWARF")) {
consumeError(std::move(Err));
return;
}
// Emit table.
Emitter.emitAppleObjc(AppleObjC);
Emitter.finish();
// Set start offset ans size for output section.
OutSection.setSizesForSectionCreatedByAsmPrinter();
}
{
// FIXME: we use AsmPrinter to emit accelerator sections.
// It might be beneficial to directly emit accelerator data
// to the raw_svector_ostream.
SectionDescriptor &OutSection =
CommonSections.getSectionDescriptor(DebugSectionKind::AppleTypes);
DwarfEmitterImpl Emitter(DWARFLinker::OutputFileType::Object,
OutSection.OS);
if (Error Err = Emitter.init(TargetTriple, "__DWARF")) {
consumeError(std::move(Err));
return;
}
// Emit table.
Emitter.emitAppleTypes(AppleTypes);
Emitter.finish();
// Set start offset ans size for output section.
OutSection.setSizesForSectionCreatedByAsmPrinter();
}
}
void DWARFLinkerImpl::emitDWARFv5DebugNamesSection(const Triple &TargetTriple) {
std::unique_ptr<DWARF5AccelTable> DebugNames;
DebugNamesUnitsOffsets CompUnits;
CompUnitIDToIdx CUidToIdx;
unsigned Id = 0;
forEachCompileAndTypeUnit([&](DwarfUnit *CU) {
bool HasRecords = false;
CU->forEachAcceleratorRecord([&](const DwarfUnit::AccelInfo &Info) {
if (DebugNames.get() == nullptr)
DebugNames = std::make_unique<DWARF5AccelTable>();
HasRecords = true;
switch (Info.Type) {
case DwarfUnit::AccelType::Name:
case DwarfUnit::AccelType::Namespace:
case DwarfUnit::AccelType::Type: {
DebugNames->addName(*DebugStrStrings.getExistingEntry(Info.String),
Info.OutOffset, std::nullopt /*ParentDIEOffset*/,
Info.Tag, CU->getUniqueID());
} break;
default:
break; // Nothing to do.
};
});
if (HasRecords) {
CompUnits.push_back(
CU->getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo)
.StartOffset);
CUidToIdx[CU->getUniqueID()] = Id++;
}
});
if (DebugNames.get() != nullptr) {
// FIXME: we use AsmPrinter to emit accelerator sections.
// It might be beneficial to directly emit accelerator data
// to the raw_svector_ostream.
SectionDescriptor &OutSection =
CommonSections.getSectionDescriptor(DebugSectionKind::DebugNames);
DwarfEmitterImpl Emitter(DWARFLinker::OutputFileType::Object,
OutSection.OS);
if (Error Err = Emitter.init(TargetTriple, "__DWARF")) {
consumeError(std::move(Err));
return;
}
// Emit table.
Emitter.emitDebugNames(*DebugNames, CompUnits, CUidToIdx);
Emitter.finish();
// Set start offset ans size for output section.
OutSection.setSizesForSectionCreatedByAsmPrinter();
}
}
void DWARFLinkerImpl::cleanupDataAfterDWARFOutputIsWritten() {
GlobalData.getStringPool().clear();
DebugStrStrings.clear();
DebugLineStrStrings.clear();
}
void DWARFLinkerImpl::writeCompileUnitsToTheOutput() {
// Enumerate all sections and store them into the final emitter.
forEachObjectSectionsSet([&](OutputSections &Sections) {
Sections.forEach([&](std::shared_ptr<SectionDescriptor> OutSection) {
// Emit section content.
SectionHandler(OutSection);
});
});
}
void DWARFLinkerImpl::writeCommonSectionsToTheOutput() {
CommonSections.forEach([&](std::shared_ptr<SectionDescriptor> OutSection) {
SectionHandler(OutSection);
});
}