1421 lines
51 KiB
C++
1421 lines
51 KiB
C++
//=== 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);
|
|
});
|
|
}
|