diff --git a/llvm/test/tools/dsymutil/Inputs/modules/1.o b/llvm/test/tools/dsymutil/Inputs/modules/1.o new file mode 100644 index 000000000000..6e0775f57559 Binary files /dev/null and b/llvm/test/tools/dsymutil/Inputs/modules/1.o differ diff --git a/llvm/test/tools/dsymutil/Inputs/modules/Bar.pcm b/llvm/test/tools/dsymutil/Inputs/modules/Bar.pcm new file mode 100644 index 000000000000..8b628e3cf016 Binary files /dev/null and b/llvm/test/tools/dsymutil/Inputs/modules/Bar.pcm differ diff --git a/llvm/test/tools/dsymutil/Inputs/modules/Foo.pcm b/llvm/test/tools/dsymutil/Inputs/modules/Foo.pcm new file mode 100644 index 000000000000..2416a4054431 Binary files /dev/null and b/llvm/test/tools/dsymutil/Inputs/modules/Foo.pcm differ diff --git a/llvm/test/tools/dsymutil/X86/lit.local.cfg b/llvm/test/tools/dsymutil/X86/lit.local.cfg index ed9eba0b0d9f..05f8b38b3346 100644 --- a/llvm/test/tools/dsymutil/X86/lit.local.cfg +++ b/llvm/test/tools/dsymutil/X86/lit.local.cfg @@ -1,4 +1,4 @@ if not 'X86' in config.root.targets: config.unsupported = True -config.suffixes = ['.test', '.cpp', '.s'] +config.suffixes = ['.test', '.cpp', '.m', '.s'] diff --git a/llvm/test/tools/dsymutil/X86/modules.m b/llvm/test/tools/dsymutil/X86/modules.m new file mode 100644 index 000000000000..6a93f28587a2 --- /dev/null +++ b/llvm/test/tools/dsymutil/X86/modules.m @@ -0,0 +1,62 @@ +/* Compile with: + cat >modules.modulemap < Streamer; uint64_t OutputDebugInfoSize; + unsigned UnitID; ///< A unique ID that identifies each compile unit. /// The units of the current debug map object. std::vector Units; @@ -1416,6 +1447,9 @@ private: /// Offset of the last CIE that has been emitted in the output /// debug_frame section. uint32_t LastCIEOffset; + + /// FIXME: We may need to use something more resilient than the PCM filename. + StringSet<> ClangModules; }; /// Similar to DWARFUnitSection::getUnitForOffset(), but returning our @@ -2588,7 +2622,13 @@ DIE *DwarfLinker::DIECloner::cloneDIE( // Extract and clone every attribute. DataExtractor Data = U.getDebugInfoExtractor(); - uint32_t NextOffset = U.getDIEAtIndex(Idx + 1)->getOffset(); + // Point to the next DIE (generally there is always at least a NULL + // entry after the current one). If this is a lone + // DW_TAG_compile_unit without any children, point to the next unit. + uint32_t NextOffset = + (Idx + 1 < U.getNumDIEs()) + ? U.getDIEAtIndex(Idx + 1)->getOffset() + : U.getNextUnitOffset(); AttributesInfo AttrInfo; // We could copy the data only if we need to aply a relocation to @@ -3051,6 +3091,38 @@ void DwarfLinker::DIECloner::copyAbbrev( Linker.AssignAbbrev(Copy); } +bool DwarfLinker::registerModuleReference( + const DWARFDebugInfoEntryMinimal &CUDie, const DWARFUnit &Unit, + DebugMap &ModuleMap, unsigned Indent) { + std::string PCMfile = + CUDie.getAttributeValueAsString(&Unit, dwarf::DW_AT_GNU_dwo_name, ""); + if (PCMfile.empty()) + return false; + + // Clang module DWARF skeleton CUs abuse this for the path to the module. + std::string PCMpath = + CUDie.getAttributeValueAsString(&Unit, dwarf::DW_AT_comp_dir, ""); + + if (Options.Verbose) { + outs().indent(Indent); + outs() << "Found clang module reference " << PCMfile; + } + + if (ClangModules.count(PCMfile)) { + if (Options.Verbose) + outs() << " [cached].\n"; + return true; + } + if (Options.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); + loadClangModule(PCMfile, PCMpath, ModuleMap, Indent + 2); + return true; +} + ErrorOr DwarfLinker::loadObject(BinaryHolder &BinaryHolder, DebugMapObject &Obj, const DebugMap &Map) { @@ -3066,6 +3138,58 @@ DwarfLinker::loadObject(BinaryHolder &BinaryHolder, DebugMapObject &Obj, return ErrOrObj; } +void DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath, + DebugMap &ModuleMap, unsigned Indent) { + SmallString<80> Path(Options.PrependPath); + if (sys::path::is_relative(Filename)) + sys::path::append(Path, ModulePath, Filename); + else + sys::path::append(Path, Filename); + BinaryHolder ObjHolder(Options.Verbose); + auto &Obj = + ModuleMap.addDebugMapObject(Path, sys::TimeValue::PosixZeroTime()); + auto ErrOrObj = loadObject(ObjHolder, Obj, ModuleMap); + if (!ErrOrObj) { + ClangModules.erase(ClangModules.find(Filename)); + return; + } + + // FIXME: At this point dsymutil should verify the DW_AT_gnu_dwo_id + // against the module hash of the clang module. + + CompileUnit *Unit = nullptr; + + // Setup access to the debug info. + DWARFContextInMemory DwarfContext(*ErrOrObj); + RelocationManager RelocMgr(*this); + for (const auto &CU : DwarfContext.compile_units()) { + auto *CUDie = CU->getUnitDIE(false); + // Recursively get all modules imported by this one. + if (!registerModuleReference(*CUDie, *CU, ModuleMap, Indent)) { + // Add this module. + if (Unit) { + errs() << Filename << ": Clang modules are expected to have exactly" + << " 1 compile unit.\n"; + exitDsymutil(1); + } + Unit = new CompileUnit(*CU, UnitID++, !Options.NoODR); + Unit->setHasInterestingContent(); + gatherDIEParents(CUDie, 0, *Unit, &ODRContexts.getRoot(), StringPool, + ODRContexts); + // Keep everything. + Unit->markEverythingAsKept(); + } + } + if (Options.Verbose) { + outs().indent(Indent); + outs() << "cloning .debug_info from " << Filename << "\n"; + } + + DIECloner(*this, RelocMgr, DIEAlloc, MutableArrayRef(*Unit), + Options) + .cloneAllCompileUnits(DwarfContext); +} + void DwarfLinker::DIECloner::cloneAllCompileUnits( DWARFContextInMemory &DwarfContext) { if (!Linker.Streamer) @@ -3113,7 +3237,9 @@ bool DwarfLinker::link(const DebugMap &Map) { // Size of the DIEs (and headers) generated for the linked output. OutputDebugInfoSize = 0; // A unique ID that identifies each compile unit. - unsigned UnitID = 0; + UnitID = 0; + DebugMap ModuleMap(Map.getTriple(), Map.getBinaryPath()); + for (const auto &Obj : Map.objects()) { CurrentDebugObject = Obj.get(); @@ -3143,9 +3269,11 @@ bool DwarfLinker::link(const DebugMap &Map) { outs() << "Input compilation unit:"; CUDie->dump(outs(), CU.get(), 0); } - Units.emplace_back(*CU, UnitID++, !Options.NoODR); - gatherDIEParents(CUDie, 0, Units.back(), &ODRContexts.getRoot(), - StringPool, ODRContexts); + if (!registerModuleReference(*CUDie, *CU, ModuleMap)) { + Units.emplace_back(*CU, UnitID++, !Options.NoODR); + gatherDIEParents(CUDie, 0, Units.back(), &ODRContexts.getRoot(), + StringPool, ODRContexts); + } } // Then mark all the DIEs that need to be present in the linked