diff options
Diffstat (limited to 'contrib/llvm-project/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.cpp')
| -rw-r--r-- | contrib/llvm-project/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.cpp | 1451 |
1 files changed, 1451 insertions, 0 deletions
diff --git a/contrib/llvm-project/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.cpp b/contrib/llvm-project/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.cpp new file mode 100644 index 000000000000..bb59cbfdb347 --- /dev/null +++ b/contrib/llvm-project/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.cpp @@ -0,0 +1,1451 @@ +//=== 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 "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, + std::optional<Triple> TargetTriple) + : OutputSections(GlobalData), InputDWARFFile(File), + ClangModules(ClangModules), TargetTriple(TargetTriple), + 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)); +} + +Error DWARFLinkerImpl::createEmitter(const Triple &TheTriple, + OutputFileType FileType, + raw_pwrite_stream &OutFile) { + + TheDwarfEmitter = std::make_unique<DwarfEmitterImpl>(FileType, OutFile); + + return TheDwarfEmitter->init(TheTriple, "__DWARF"); +} + +ExtraDwarfEmitter *DWARFLinkerImpl::getEmitter() { + return TheDwarfEmitter.get(); +} + +void DWARFLinkerImpl::addObjectFile(DWARFFile &File, ObjFileLoaderTy Loader, + CompileUnitHandlerTy OnCUDieLoaded) { + ObjectContexts.emplace_back(std::make_unique<LinkContext>( + GlobalData, File, ClangModules, UniqueUnitID, + (TheDwarfEmitter.get() == nullptr ? std::optional<Triple>(std::nullopt) + : TheDwarfEmitter->getTargetTriple()))); + + 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 (TheDwarfEmitter) { + GlobalEndianness = TheDwarfEmitter->getTargetTriple().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() << "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 (!TheDwarfEmitter) + 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 (TheDwarfEmitter) + GlobalFormat.AddrSize = + TheDwarfEmitter->getTargetTriple().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()) { + std::optional<Triple> OutTriple = TheDwarfEmitter.get() == nullptr + ? std::optional<Triple>(std::nullopt) + : TheDwarfEmitter->getTargetTriple(); + + if (Error Err = ArtificialTypeUnit.get()->finishCloningAndEmit(OutTriple)) + 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"); + + GlobalData.Options.NoOutput = TheDwarfEmitter.get() == nullptr; + + 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(TargetTriple, 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.getOptions().NoOutput) + 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.getOptions().NoOutput) + 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.getOptions().NoOutput) + return; + + // 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); + + const Triple &TargetTriple = TheDwarfEmitter->getTargetTriple(); + + // 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(TargetTriple); }); + } + + if (llvm::is_contained(GlobalData.Options.AccelTables, + AccelTableKind::DebugNames)) { + // Emit .debug_names section. + TG.spawn([&]() { emitDWARFv5DebugNamesSection(TargetTriple); }); + } + + // 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, 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() { + bool HasAbbreviations = false; + + // Enumerate all sections and store them into the final emitter. + forEachObjectSectionsSet([&](OutputSections &Sections) { + Sections.forEach([&](SectionDescriptor &OutSection) { + if (!HasAbbreviations && !OutSection.getContents().empty() && + OutSection.getKind() == DebugSectionKind::DebugAbbrev) + HasAbbreviations = true; + + // Emit section content. + TheDwarfEmitter->emitSectionContents(OutSection.getContents(), + OutSection.getName()); + OutSection.clearSectionContent(); + }); + }); + + if (!HasAbbreviations) { + const SmallVector<std::unique_ptr<DIEAbbrev>> Abbreviations; + TheDwarfEmitter->emitAbbrevs(Abbreviations, 3); + } +} + +void DWARFLinkerImpl::writeCommonSectionsToTheOutput() { + CommonSections.forEach([&](SectionDescriptor &OutSection) { + // Emit section content. + TheDwarfEmitter->emitSectionContents(OutSection.getContents(), + OutSection.getName()); + OutSection.clearSectionContent(); + }); +} |
